"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateGeometrySummaries = generateGeometrySummaries;
const core_bentley_1 = require("@itwin/core-bentley");
const core_geometry_1 = require("@itwin/core-geometry");
const core_common_1 = require("@itwin/core-common");
const Element_1 = require("./Element");
const Symbols_1 = require("./internal/Symbols");
// cspell:ignore earlin
/** Generates an array of GeometryStreamResponseProps. */
class ResponseGenerator {
    verbosity = core_common_1.GeometrySummaryVerbosity.Basic;
    includePlacement = false;
    includePartReferences;
    verboseSymbology = false;
    elementIds;
    iModel;
    get wantSquish() { return core_common_1.GeometrySummaryVerbosity.Full !== this.verbosity; }
    constructor(request, iModel) {
        this.elementIds = request.elementIds;
        this.iModel = iModel;
        const opts = request.options;
        if (undefined !== opts) {
            this.verbosity = undefined !== opts.geometryVerbosity ? opts.geometryVerbosity : core_common_1.GeometrySummaryVerbosity.Basic;
            this.includePlacement = true === opts.includePlacement;
            this.includePartReferences = opts.includePartReferences;
            this.verboseSymbology = true === opts.verboseSymbology;
        }
    }
    generateSummaries() {
        const summaries = [];
        for (const elementId of this.elementIds)
            summaries.push(this.generateSummary(elementId));
        return summaries.join("\n");
    }
    generateSummary(id) {
        let lines = [`[Geometry Summary for Element ${id}]`];
        try {
            const geom = this.getElementGeom(id);
            if (undefined === geom)
                throw new core_common_1.IModelError(core_bentley_1.IModelStatus.NoGeometry, "Element is neither a geometric element nor a geometry part");
            if (undefined !== geom.geometricElement)
                lines.push(this.summarizeElement(geom.geometricElement));
            else if (undefined !== this.includePartReferences)
                lines.push(this.summarizePartReferences(id, "2d" === this.includePartReferences)); // NB: Hideously inefficient if more than one element's summary was requested.
            let curGeomParams;
            let curLocalRange;
            for (const entry of geom.iterator) {
                if (this.verboseSymbology && (undefined === curGeomParams || !curGeomParams.isEquivalent(entry.geomParams))) {
                    lines.push(`Symbology: ${this.stringify(entry.geomParams)}`);
                    curGeomParams = entry.geomParams.clone();
                }
                if (undefined !== entry.localRange && (undefined === curLocalRange || !curLocalRange.isAlmostEqual(entry.localRange))) {
                    lines.push(this.summarizeRange3d(entry.localRange));
                    curLocalRange = entry.localRange;
                }
                const prim = entry.primitive;
                switch (prim.type) {
                    case "textString":
                    case "image":
                        this.summarizePrimitive(lines, prim);
                        break;
                    case "brep":
                        this.summarizeBRep(lines, prim.brep);
                        break;
                    case "geometryQuery":
                        this.summarizeGeometryQuery(lines, prim.geometry);
                        break;
                    case "partReference":
                        lines.push(this.summarizePartReference(prim.part.id, prim.part.toLocal));
                        break;
                }
            }
        }
        catch (err) {
            lines = lines.slice(0, 1);
            lines.push(`ERROR: ${core_bentley_1.BentleyError.getErrorMessage(err)}`);
        }
        return lines.filter((line) => line !== "").join("\n");
    }
    summarizeElement(elem) {
        const lines = [];
        lines.push(`(${elem.is2d() ? "2D" : "3D"}) Category: ${elem.category}`);
        lines.push(`Model: ${elem.model}`);
        if (this.includePlacement) {
            lines.push(`Range: ${this.stringify(elem.calculateRange3d())}`);
            lines.push(`Transform: ${this.stringify(elem.getPlacementTransform())}`);
        }
        return lines.join("\n");
    }
    summarizePartReferences(id, is2d) {
        const refIds = this.iModel[Symbols_1._nativeDb].findGeometryPartReferences([id], is2d);
        return `Part references (${refIds.length}): ${refIds.join()}`;
    }
    getElementGeom(id) {
        const elem = this.iModel.elements.getElement({ id, wantGeometry: true });
        let iterator;
        let geometricElement;
        if (elem instanceof Element_1.GeometricElement) {
            geometricElement = elem;
            if (geometricElement.is2d()) {
                iterator = core_common_1.GeometryStreamIterator.fromGeometricElement2d(geometricElement);
            }
            else {
                (0, core_bentley_1.assert)(geometricElement.is3d());
                iterator = core_common_1.GeometryStreamIterator.fromGeometricElement3d(geometricElement);
            }
        }
        else if (elem instanceof Element_1.GeometryPart) {
            iterator = core_common_1.GeometryStreamIterator.fromGeometryPart(elem);
        }
        return undefined !== iterator ? { iterator, geometricElement } : undefined;
    }
    summarizeRange3d(range) {
        return `SubGraphicRange: ${this.stringify(range)}`;
    }
    summarizePrimitive(lines, primitive) {
        const summary = primitive.type;
        if (core_common_1.GeometrySummaryVerbosity.Basic >= this.verbosity) {
            lines.push(summary);
            return;
        }
        const json = this.stringify(primitive.type === "textString" ? primitive.textString : primitive.image);
        if (core_common_1.GeometrySummaryVerbosity.Detailed >= this.verbosity) {
            lines.push(`${summary}: ${json}`);
            return;
        }
        lines.push(`${summary}:`);
        lines.push(json);
    }
    summarizeBRep(lines, brep) {
        const summary = "brep";
        if (core_common_1.GeometrySummaryVerbosity.Basic >= this.verbosity) {
            lines.push(summary);
            return;
        }
        const json = this.stringify({ type: brep.type, transform: brep.transform, faceSymbology: brep.faceSymbology });
        if (core_common_1.GeometrySummaryVerbosity.Detailed >= this.verbosity) {
            lines.push(`${summary}: ${json}`);
            return;
        }
        lines.push(`${summary}:`);
        lines.push(json);
    }
    summarizePartReference(partId, partToLocal) {
        let line = `part id: ${partId}`;
        if (undefined !== partToLocal)
            line = `${line} transform: ${this.stringify(partToLocal)}`;
        return line;
    }
    summarizeGeometryQuery(lines, query) {
        switch (this.verbosity) {
            case core_common_1.GeometrySummaryVerbosity.Detailed:
                lines.push(this.summarizeGeometryQueryDetailed(query));
                break;
            case core_common_1.GeometrySummaryVerbosity.Full:
                lines.push(this.summarizeGeometryQueryFull(query));
                break;
            default:
                lines.push(this.summarizeGeometryQueryBasic(query));
                break;
        }
    }
    summarizeGeometryQueryBasic(query) {
        switch (query.geometryCategory) {
            case "solid":
                return query.solidPrimitiveType;
            case "curvePrimitive":
                return query.curvePrimitiveType;
            case "curveCollection":
                return query.curveCollectionType;
            default:
                return query.geometryCategory;
        }
    }
    summarizeGeometryQueryFull(query) {
        return this.geometryQueryToJson(query);
    }
    geometryQueryToJson(query) {
        try {
            const json = core_geometry_1.IModelJson.Writer.toIModelJson(query);
            const str = JSON.stringify(json);
            return this.wantSquish ? this.squish(str) : str;
        }
        catch (err) {
            return core_bentley_1.BentleyError.getErrorMessage(err);
        }
    }
    // Earlin likes to put tons of whitespace + newlines into his JSON. Remove it.
    squish(str) {
        return str.replace(/\s+/g, "");
    }
    stringify(obj) {
        const json = JSON.stringify(obj);
        return this.wantSquish ? this.squish(json) : json;
    }
    summarizeGeometryQueryDetailed(query) {
        let summary = `${this.summarizeGeometryQueryBasic(query)}: `;
        switch (query.geometryCategory) {
            case "solid":
                return summary + this.summarizeSolidPrimitive(query);
            case "curvePrimitive":
                return summary + this.summarizeCurvePrimitive(query);
            case "curveCollection":
                return summary + this.summarizeCurveCollection(query);
            case "pointCollection":
                return `${summary} numPoints: ${query.points.length}`;
            case "bsurf":
                return `${summary}'
        ' poleDimension: ${query.poleDimension}'
        ' numPolesTotal: ${query.numPolesTotal()}'
        ' degree[U,V]: ${JSON.stringify([query.degreeUV(core_geometry_1.UVSelect.uDirection), query.degreeUV(core_geometry_1.UVSelect.vDirection)])}'
        ' order[U,V]: ${JSON.stringify([query.orderUV(core_geometry_1.UVSelect.uDirection), query.orderUV(core_geometry_1.UVSelect.vDirection)])}'
        ' numSpan[U,V]: ${JSON.stringify([query.numSpanUV(core_geometry_1.UVSelect.uDirection), query.numSpanUV(core_geometry_1.UVSelect.vDirection)])}'
        ' numPoles[U,V]: ${JSON.stringify([query.numPolesUV(core_geometry_1.UVSelect.uDirection), query.numPolesUV(core_geometry_1.UVSelect.vDirection)])}'
        ' poleStep[U,V]: ${JSON.stringify([query.poleStepUV(core_geometry_1.UVSelect.uDirection), query.poleStepUV(core_geometry_1.UVSelect.vDirection)])}`;
            case "polyface": {
                const data = query.data;
                summary = `${summary} pointCount: ${data.point.length}'
        ' pointIndexCount: ${data.pointIndex.length}`;
                if (query.twoSided)
                    summary = `${summary} (two-sided)`;
                if (undefined !== data.normal)
                    summary = `${summary} normalCount: ${data.normal.length}`;
                if (undefined !== data.param)
                    summary = `${summary} paramCount: ${data.param.length}`;
                if (undefined !== data.color)
                    summary = `${summary} colorCount: ${data.color.length}`;
                return summary;
            }
            case "point":
                return summary + this.geometryQueryToJson(query);
        }
    }
    summarizeSolidPrimitive(solid) {
        const summary = solid.capped ? " capped" : " uncapped";
        switch (solid.solidPrimitiveType) {
            case "box":
                const box = solid;
                return `${summary}'
        ' origin: ${JSON.stringify(box.getBaseOrigin().toJSON())}'
        ' topOrigin: ${JSON.stringify(box.getTopOrigin().toJSON())}'
        ' baseX: ${box.getBaseX()}'
        ' baseY: ${box.getBaseY()}`;
            case "cone":
                const cone = solid;
                return `${summary}'
        ' baseCenterPoint: ${JSON.stringify(cone.getCenterA())}'
        ' topCenterPoint: ${JSON.stringify(cone.getCenterB())}'
        ' baseCenterRadius: ${JSON.stringify(cone.getRadiusA())}'
        ' topCenterRadius: ${JSON.stringify(cone.getRadiusB())}`;
            case "sphere":
                const sphere = solid;
                return `${summary}'
        ' centerPoint: ${JSON.stringify(sphere.cloneCenter().toJSON())}'
        ' radius: ${JSON.stringify(sphere.trueSphereRadius())}`;
            case "linearSweep":
                const linearSweep = solid;
                return `${summary}'
        ' vector: ${JSON.stringify(linearSweep.cloneSweepVector().toJSON())}'
        ' curves${this.summarizeCurveCollection(linearSweep.getCurvesRef())}`;
            case "rotationalSweep":
                const rotationalSweep = solid;
                const axis = rotationalSweep.cloneAxisRay();
                return `${summary}'
        ' center: ${JSON.stringify(axis.origin.toJSON())}'
        ' axis: ${JSON.stringify(axis.direction.toJSON())}'
        ' sweepAngle: ${rotationalSweep.getSweep().degrees}`;
            case "ruledSweep":
                const ruledSweep = solid;
                const summarizedCollection = ruledSweep.cloneContours().map((curveCollection) => this.summarizeCurveCollection(curveCollection));
                return `${summary}'
        ' isClosedVolume${ruledSweep.isClosedVolume}'
        ' contours: ${JSON.stringify(summarizedCollection)}`;
            case "torusPipe":
                const torusPipe = solid;
                const vectorX = torusPipe.cloneVectorX();
                const vectorY = torusPipe.cloneVectorY();
                const sweep = torusPipe.getSweepAngle();
                if (torusPipe.getIsReversed()) {
                    vectorY.scaleInPlace(-1.0);
                    sweep.setRadians(-sweep.radians);
                }
                return `${summary}'
        ' center: ${JSON.stringify(torusPipe.cloneCenter().toJSON())}'
        ' xyVectors: ${JSON.stringify([vectorX.toJSON(), vectorY.toJSON()])}'
        ' majorRadius: ${torusPipe.getMajorRadius()}'
        ' minorRadius: ${torusPipe.getMinorRadius()}'
        ' sweepAngle: ${sweep.degrees}`;
        }
    }
    summarizeCurvePrimitive(curve) {
        let summary = "";
        const writer = new core_geometry_1.IModelJson.Writer();
        switch (curve.curvePrimitiveType) {
            case "arc":
                const arc = curve;
                summary = `${summary} center: ${JSON.stringify(arc.center.toJSON())}`;
                if (undefined !== arc.circularRadius)
                    summary = `${summary} radius: ${arc.circularRadius()}`;
                summary = `${summary}'
        ' vectorX:${JSON.stringify(arc.vector0.toJSON())}'
        ' vectorY:${JSON.stringify(arc.vector90.toJSON())}'
        ' sweepStartEnd [${arc.sweep.startDegrees}, ${arc.sweep.endDegrees}]`
                    + ` curveLength: ${curve.curveLength()}`;
                return summary;
            case "lineSegment":
                const lineSegment = curve;
                summary = `${summary} points: ${JSON.stringify(lineSegment.toJSON())}`;
                summary = `${summary} curveLength: ${curve.curveLength()}`;
                return summary;
            case "lineString":
                const lineString = curve;
                summary = `${summary} pointCount: ${lineString.numPoints()}'
        ' curveLength: ${curve.curveLength()}`;
                return summary;
            case "bsplineCurve":
                const bsplineCurve = curve;
                summary = `${summary}'
        ' curveOrder: ${bsplineCurve.order}'
        ' controlPointsCount: ${bsplineCurve.numPoles}'
        ' curveLength: ${curve.curveLength()}`;
                return summary;
            case "interpolationCurve":
                const interpolationCurve = curve;
                const interpolationProps = interpolationCurve.cloneProps();
                summary = `${summary}'
        ' curveOrder: ${interpolationProps.order}'
        ' controlPointsCount: ${interpolationProps.fitPoints.length}`;
                return summary;
            case "akimaCurve":
                const akimaCurve = curve;
                const akimaProps = akimaCurve.cloneProps();
                summary = `${summary}'
          ' controlPointsCount: ${akimaProps.fitPoints.length}`;
                return summary;
            case "bezierCurve":
                const bezierCurve = curve;
                summary = `${summary}'
        ' curveOrder: ${bezierCurve.order}'
        ' controlPointsCount: ${bezierCurve.numPoles}'
        ' curveLength: ${curve.curveLength()}`;
                return summary;
            case "transitionSpiral":
                const transitionSpiral = curve;
                const json = writer.handleTransitionSpiral(transitionSpiral);
                summary = summary + JSON.stringify(json);
                return summary;
            case "curveChainWithDistanceIndex":
                const curveChainWithDistanceIndex = curve;
                const path = curveChainWithDistanceIndex.path;
                summary = `${summary}'
        ' curveLength: ${curve.curveLength()}'
        ' isOpen: ${path.isOpenPath}`;
                return summary;
        }
    }
    summarizeCurveCollection(curves) {
        let summary = "";
        switch (curves.curveCollectionType) {
            case "loop":
                const loop = curves;
                summary = `${summary} isInner: ${loop.isInner}`;
                break;
            case "path":
                const path = curves;
                summary = `${summary} isOpen: ${path.isOpenPath}`;
                break;
        }
        return `${summary}'
    ' numCurves: ${curves.collectCurvePrimitives().length}'
    ' boundary: ${curves.dgnBoundaryType()}`;
    }
}
/** @internal */
function generateGeometrySummaries(request, iModel) {
    const generator = new ResponseGenerator(request, iModel);
    return generator.generateSummaries();
}
//# sourceMappingURL=GeometrySummary.js.map