"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DgnSpiralTypeQueries = exports.BGFBReader = void 0;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
const flatbuffers_1 = require("flatbuffers");
const core_bentley_1 = require("@itwin/core-bentley");
const AkimaCurve3d_1 = require("../bspline/AkimaCurve3d");
const BSplineCurve_1 = require("../bspline/BSplineCurve");
const BSplineCurve3dH_1 = require("../bspline/BSplineCurve3dH");
const BSplineSurface_1 = require("../bspline/BSplineSurface");
const InterpolationCurve3d_1 = require("../bspline/InterpolationCurve3d");
const Arc3d_1 = require("../curve/Arc3d");
const CurveCollection_1 = require("../curve/CurveCollection");
const GeometryQuery_1 = require("../curve/GeometryQuery");
const LineSegment3d_1 = require("../curve/LineSegment3d");
const LineString3d_1 = require("../curve/LineString3d");
const Loop_1 = require("../curve/Loop");
const ParityRegion_1 = require("../curve/ParityRegion");
const Path_1 = require("../curve/Path");
const PointString3d_1 = require("../curve/PointString3d");
const DirectSpiral3d_1 = require("../curve/spiral/DirectSpiral3d");
const IntegratedSpiral3d_1 = require("../curve/spiral/IntegratedSpiral3d");
const TransitionSpiral3d_1 = require("../curve/spiral/TransitionSpiral3d");
const UnionRegion_1 = require("../curve/UnionRegion");
const Geometry_1 = require("../Geometry");
const Angle_1 = require("../geometry3d/Angle");
const AngleSweep_1 = require("../geometry3d/AngleSweep");
const Point3dVector3d_1 = require("../geometry3d/Point3dVector3d");
const PointHelpers_1 = require("../geometry3d/PointHelpers");
const Ray3d_1 = require("../geometry3d/Ray3d");
const Segment1d_1 = require("../geometry3d/Segment1d");
const Transform_1 = require("../geometry3d/Transform");
const AuxData_1 = require("../polyface/AuxData");
const Polyface_1 = require("../polyface/Polyface");
const TaggedNumericData_1 = require("../polyface/TaggedNumericData");
const Box_1 = require("../solid/Box");
const Cone_1 = require("../solid/Cone");
const LinearSweep_1 = require("../solid/LinearSweep");
const RotationalSweep_1 = require("../solid/RotationalSweep");
const RuledSweep_1 = require("../solid/RuledSweep");
const Sphere_1 = require("../solid/Sphere");
const TorusPipe_1 = require("../solid/TorusPipe");
const BGFBAccessors_1 = require("./BGFBAccessors");
const SerializationHelpers_1 = require("./SerializationHelpers");
/** * Context to write to a flatbuffer blob.
 *  * This class is internal.
 *  * Public access is through BentleyGeometryFlatBuffer.geometryToBytes()
 * @internal
 */
class BGFBReader {
    constructor() {
    }
    /**
     * Extract a bspline surface
     * @param variant read position in the flat buffer.
     */
    readBSplineSurfaceFromVariant(variantHeader) {
        let newSurface;
        const geometryType = variantHeader.geometryType();
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagBsplineSurface) {
            const bsurfHeader = variantHeader.geometry(new BGFBAccessors_1.BGFBAccessors.BsplineSurface());
            if (bsurfHeader !== null) {
                const orderU = bsurfHeader.orderU();
                const orderV = bsurfHeader.orderV();
                const numPolesU = bsurfHeader.numPolesU();
                const numPolesV = bsurfHeader.numPolesV();
                const xyzArray = bsurfHeader.polesArray();
                const knotArrayU = bsurfHeader.knotsUArray();
                const knotArrayV = bsurfHeader.knotsVArray();
                const weightArray = bsurfHeader.weightsArray();
                const closedU = bsurfHeader.closedU();
                const closedV = bsurfHeader.closedV();
                if (xyzArray !== null && knotArrayU !== null && knotArrayV !== null) {
                    const myData = SerializationHelpers_1.SerializationHelpers.createBSplineSurfaceData(xyzArray, 3, knotArrayU, numPolesU, orderU, knotArrayV, numPolesV, orderV);
                    if (weightArray !== null)
                        myData.weights = weightArray;
                    if (closedU)
                        myData.uParams.closed = true;
                    if (closedV)
                        myData.vParams.closed = true;
                    if (SerializationHelpers_1.SerializationHelpers.Import.prepareBSplineSurfaceData(myData, { jsonPoles: false })) {
                        if (undefined === myData.weights)
                            newSurface = BSplineSurface_1.BSplineSurface3d.create(myData.poles, myData.uParams.numPoles, myData.uParams.order, myData.uParams.knots, myData.vParams.numPoles, myData.vParams.order, myData.vParams.knots);
                        else
                            newSurface = BSplineSurface_1.BSplineSurface3dH.create(myData.poles, myData.weights, myData.uParams.numPoles, myData.uParams.order, myData.uParams.knots, myData.vParams.numPoles, myData.vParams.order, myData.vParams.knots);
                        if (undefined !== newSurface) {
                            if (undefined !== myData.uParams.wrapMode)
                                newSurface.setWrappable(BSplineSurface_1.UVSelect.uDirection, myData.uParams.wrapMode);
                            if (undefined !== myData.vParams.wrapMode)
                                newSurface.setWrappable(BSplineSurface_1.UVSelect.vDirection, myData.vParams.wrapMode);
                        }
                    }
                }
            }
        }
        return newSurface;
    }
    /**
    * Extract an interpolating curve
    * @param header read position in the flat buffer.
    */
    readInterpolationCurve3d(header) {
        const xyzArray = header.fitPointsArray();
        if (xyzArray instanceof Float64Array) {
            const knots = header.knotsArray();
            const options = new InterpolationCurve3d_1.InterpolationCurve3dOptions(PointHelpers_1.Point3dArray.clonePoint3dArray(xyzArray), knots ? PointHelpers_1.NumberArray.create(knots) : undefined);
            const startTangent = header.startTangent();
            const endTangent = header.endTangent();
            options.captureOptionalProps(header.order(), header.closed(), header.isChordLenKnots(), header.isColinearTangents(), header.isChordLenTangents(), header.isNaturalTangents(), startTangent !== null ? Point3dVector3d_1.Vector3d.create(startTangent.x(), startTangent.y(), startTangent.z()) : undefined, endTangent !== null ? Point3dVector3d_1.Vector3d.create(endTangent.x(), endTangent.y(), endTangent.z()) : undefined);
            return InterpolationCurve3d_1.InterpolationCurve3d.createCapture(options);
        }
        return undefined;
    }
    /**
     * Extract an akima curve
     * @param header read position in the flat buffer.
     */
    readAkimaCurve3d(header) {
        const xyzArray = header.pointsArray();
        if (xyzArray instanceof Float64Array) {
            const options = new AkimaCurve3d_1.AkimaCurve3dOptions(PointHelpers_1.Point3dArray.clonePoint3dArray(xyzArray));
            return AkimaCurve3d_1.AkimaCurve3d.createCapture(options);
        }
        return undefined;
    }
    /**
       * Extract a bspline curve
       * @param variant read position in the flat buffer.
       */
    readBSplineCurve(header) {
        let newCurve;
        const order = header.order();
        const xyzArray = header.polesArray();
        const knots = header.knotsArray();
        const weightsArray = header.weightsArray();
        const closed = header.closed();
        if (xyzArray !== null && knots !== null) {
            const numPoles = Math.floor(xyzArray.length / 3);
            const myData = SerializationHelpers_1.SerializationHelpers.createBSplineCurveData(xyzArray, 3, knots, numPoles, order);
            if (closed)
                myData.params.closed = true;
            if (weightsArray === null) {
                if (SerializationHelpers_1.SerializationHelpers.Import.prepareBSplineCurveData(myData))
                    newCurve = BSplineCurve_1.BSplineCurve3d.create(myData.poles, myData.params.knots, myData.params.order);
            }
            else {
                myData.weights = weightsArray;
                if (SerializationHelpers_1.SerializationHelpers.Import.prepareBSplineCurveData(myData, { jsonPoles: false }))
                    newCurve = BSplineCurve3dH_1.BSplineCurve3dH.create({ xyz: myData.poles, weights: myData.weights }, myData.params.knots, myData.params.order);
            }
            if (undefined !== newCurve) {
                if (undefined !== myData.params.wrapMode)
                    newCurve.setWrappable(myData.params.wrapMode);
            }
        }
        return newCurve;
    }
    /**
     * Extract a bspline curve
     * @param variant read position in the flat buffer.
     */
    readTransitionSpiral(header) {
        const detailHeader = header.detail();
        if (detailHeader) {
            const directDetailHeader = header.directDetail();
            const _extraDataArray = header.extraDataArray();
            const spiralTypeName = DgnSpiralTypeQueries.typeCodeToString(detailHeader.spiralType());
            const curvature0 = detailHeader.curvature0();
            const curvature1 = detailHeader.curvature1();
            const bearing0Radians = detailHeader.bearing0Radians();
            const bearing1Radians = detailHeader.bearing1Radians();
            const fbTransform = detailHeader.transform();
            const localToWorld = fbTransform ? Transform_1.Transform.createRowValues(fbTransform.axx(), fbTransform.axy(), fbTransform.axz(), fbTransform.axw(), fbTransform.ayx(), fbTransform.ayy(), fbTransform.ayz(), fbTransform.ayw(), fbTransform.azx(), fbTransform.azy(), fbTransform.azz(), fbTransform.azw()) :
                Transform_1.Transform.createIdentity();
            const activeFractionInterval = Segment1d_1.Segment1d.create(detailHeader.fractionA(), detailHeader.fractionB());
            if (!directDetailHeader) {
                const integratedSpiral = IntegratedSpiral3d_1.IntegratedSpiral3d.createRadiusRadiusBearingBearing(Segment1d_1.Segment1d.create(IntegratedSpiral3d_1.IntegratedSpiral3d.curvatureToRadius(curvature0), IntegratedSpiral3d_1.IntegratedSpiral3d.curvatureToRadius(curvature1)), AngleSweep_1.AngleSweep.createStartEndRadians(bearing0Radians, bearing1Radians), activeFractionInterval, localToWorld, spiralTypeName);
                if (integratedSpiral)
                    return integratedSpiral;
                const radius0 = TransitionSpiral3d_1.TransitionSpiral3d.curvatureToRadius(curvature0);
                const radius1 = TransitionSpiral3d_1.TransitionSpiral3d.curvatureToRadius(curvature1);
                const arcLength = TransitionSpiral3d_1.TransitionSpiral3d.radiusRadiusSweepRadiansToArcLength(radius0, radius1, bearing1Radians - bearing0Radians);
                const directSpiral = DirectSpiral3d_1.DirectSpiral3d.createFromLengthAndRadius(spiralTypeName, radius0, radius1, Angle_1.Angle.createRadians(bearing0Radians), Angle_1.Angle.createRadians(bearing1Radians), arcLength, activeFractionInterval, localToWorld);
                if (directSpiral)
                    return directSpiral;
            }
        }
        return undefined;
    }
    /**
     * Extract a curve primitive
     * @param variant read position in the flat buffer.
     */
    readCurvePrimitiveFromVariant(variant) {
        const geometryType = variant.geometryType();
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagLineSegment) {
            const offsetToLineSegment = variant.geometry(new BGFBAccessors_1.BGFBAccessors.LineSegment());
            const offsetToCoordinates = offsetToLineSegment.segment();
            return LineSegment3d_1.LineSegment3d.createXYZXYZ(offsetToCoordinates.point0X(), offsetToCoordinates.point0Y(), offsetToCoordinates.point0Z(), offsetToCoordinates.point1X(), offsetToCoordinates.point1Y(), offsetToCoordinates.point1Z());
        }
        else if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagEllipticArc) {
            const offsetToEllipticArc = variant.geometry(new BGFBAccessors_1.BGFBAccessors.EllipticArc());
            const offsetToCoordinates = offsetToEllipticArc.arc();
            return Arc3d_1.Arc3d.createXYZXYZXYZ(offsetToCoordinates.centerX(), offsetToCoordinates.centerY(), offsetToCoordinates.centerZ(), offsetToCoordinates.vector0X(), offsetToCoordinates.vector0Y(), offsetToCoordinates.vector0Z(), offsetToCoordinates.vector90X(), offsetToCoordinates.vector90Y(), offsetToCoordinates.vector90Z(), AngleSweep_1.AngleSweep.createStartSweepRadians(offsetToCoordinates.startRadians(), offsetToCoordinates?.sweepRadians()));
        }
        else if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagLineString) {
            const offsetToLineString = variant.geometry(new BGFBAccessors_1.BGFBAccessors.LineString());
            const numCoordinates = offsetToLineString.pointsLength();
            const result = LineString3d_1.LineString3d.create();
            for (let i = 0; i + 2 < numCoordinates; i += 3) {
                result.packedPoints.pushXYZ(offsetToLineString.points(i), offsetToLineString.points(i + 1), offsetToLineString.points(i + 2));
            }
            return result;
        }
        else if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagBsplineCurve) {
            const offsetToBCurve = variant.geometry(new BGFBAccessors_1.BGFBAccessors.BsplineCurve());
            if (offsetToBCurve !== null)
                return this.readBSplineCurve(offsetToBCurve);
        }
        else if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagTransitionSpiral) {
            const offsetToTransitionSpiralTable = variant.geometry(new BGFBAccessors_1.BGFBAccessors.TransitionSpiral());
            if (offsetToTransitionSpiralTable !== null)
                return this.readTransitionSpiral(offsetToTransitionSpiralTable);
        }
        else if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagInterpolationCurve) {
            const offsetToInterpolationCurveTable = variant.geometry(new BGFBAccessors_1.BGFBAccessors.InterpolationCurve());
            if (offsetToInterpolationCurveTable !== null)
                return this.readInterpolationCurve3d(offsetToInterpolationCurveTable);
        }
        else if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagAkimaCurve) {
            const offsetToAkimaCurveTable = variant.geometry(new BGFBAccessors_1.BGFBAccessors.AkimaCurve());
            if (offsetToAkimaCurveTable !== null)
                return this.readAkimaCurve3d(offsetToAkimaCurveTable);
        }
        return undefined;
    }
    /**
     * Extract a curve primitive
     * @param variant read position in the flat buffer.
     */
    readPointStringFromVariant(variant) {
        const geometryType = variant.geometryType();
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagPointString) {
            const offsetToLineString = variant.geometry(new BGFBAccessors_1.BGFBAccessors.PointString());
            const numCoordinates = offsetToLineString.pointsLength();
            const result = PointString3d_1.PointString3d.create();
            for (let i = 0; i + 2 < numCoordinates; i += 3) {
                result.points.push(Point3dVector3d_1.Point3d.create(offsetToLineString.points(i), offsetToLineString.points(i + 1), offsetToLineString.points(i + 2)));
            }
            return result;
        }
        return undefined;
    }
    /** Extract auxData channel data for a mesh */
    readPolyfaceAuxChannelData(channelDataHeader) {
        if (channelDataHeader !== null) {
            const input = channelDataHeader.input();
            const values = channelDataHeader.valuesArray();
            if (values !== null)
                return new AuxData_1.AuxChannelData(input, values);
        }
        return undefined;
    }
    /** Extract auxData channel for a mesh */
    readPolyfaceAuxChannel(channelHeader) {
        if (channelHeader) {
            const dataType = channelHeader.dataType();
            const dataLength = channelHeader.dataLength();
            const channelDataArray = [];
            const name = channelHeader.name();
            const inputName = channelHeader.inputName();
            for (let i = 0; i < dataLength; i++) {
                const channelData = this.readPolyfaceAuxChannelData(channelHeader.data(i));
                if (channelData)
                    channelDataArray.push(channelData);
            }
            return new AuxData_1.AuxChannel(channelDataArray, dataType, name ? name : undefined, inputName ? inputName : undefined);
        }
        return undefined;
    }
    /** Compute the number of logical entries in every flat data array in the AuxData */
    static channelDataLength(fbAuxData) {
        if (fbAuxData.channelsLength() <= 0)
            return 0;
        const fbChannel0 = nullToUndefined(fbAuxData.channels(0));
        if (!fbChannel0)
            return 0;
        const numChannel0Data = fbChannel0.dataLength();
        if (numChannel0Data <= 0)
            return 0;
        const fbChannel0Data0 = nullToUndefined(fbChannel0.data(0));
        if (!fbChannel0Data0)
            return 0;
        const numChannelDataValues = fbChannel0Data0.valuesLength();
        if (numChannelDataValues <= 0)
            return 0;
        return numChannelDataValues / AuxData_1.AuxChannel.entriesPerValue(fbChannel0.dataType());
    }
    /** Examine int array for range and zero count  */
    countIntArray(ints) {
        let min = Infinity;
        let max = -Infinity;
        let numZeroes = 0;
        for (const i of ints) {
            if (min > i)
                min = i;
            if (max < i)
                max = i;
            if (0 === i)
                ++numZeroes;
        }
        return { min, max, numZeroes };
    }
    /**
     * Extract auxData for a mesh.
     * Typescript object format for Polyface/PolyfaceAuxData indices is 0-based, unterminated.
     * FlatBuffer format for Polyface/PolyfaceAuxData indices is 1-based, 0-terminated/padded.
     * Typescript API previously wrote FlatBuffer PolyfaceAuxData indices as 0-based, unterminated;
     * heuristics are used herein to identify this legacy format so it can still be read.
     */
    readPolyfaceAuxData(fbPolyface, fbAuxData) {
        if (!fbPolyface || !fbAuxData)
            return undefined;
        const fbPointIndices = nullToUndefined(fbPolyface.pointIndexArray());
        const fbAuxIndices = nullToUndefined(fbAuxData.indicesArray());
        const numChannels = fbAuxData.channelsLength();
        const fbNumData = BGFBReader.channelDataLength(fbAuxData);
        if (!fbPointIndices || !fbPointIndices.length || !fbAuxIndices || !fbAuxIndices.length || numChannels <= 0 || fbNumData <= 0)
            return undefined;
        const numPerFace = fbPolyface.numPerFace();
        // HEURISTICS to detect legacy AuxData indices, previously mistakenly serialized by BGFBWriter.writePolyfaceAsFBVariantGeometry as 0-based unblocked indices
        let isLegacy = false;
        const pointIndicesPadCount = fbPointIndices.filter((index) => index === 0).length;
        if (numPerFace > 1) {
            const auxIndexCounts = this.countIntArray(fbAuxIndices);
            if (auxIndexCounts.max > fbNumData) // auxIndices invalid
                return undefined;
            if (auxIndexCounts.max === fbNumData) // auxIndices 1-based
                isLegacy = false;
            else if (auxIndexCounts.max <= 0 || auxIndexCounts.min < 0) // auxIndices 1-based (signed)
                isLegacy = false;
            else if (auxIndexCounts.min === 0) // auxIndices likely legacy 0-based index, but could be modern with padding
                isLegacy = pointIndicesPadCount !== auxIndexCounts.numZeroes;
            else if (auxIndexCounts.min > 0) // auxIndices likely modern without padding, but could be legacy if first datum not indexed
                isLegacy = pointIndicesPadCount > 0;
        }
        else {
            isLegacy = (fbAuxIndices.length < fbPointIndices.length) && (fbAuxIndices.length + pointIndicesPadCount === fbPointIndices.length);
        }
        if (!isLegacy && fbAuxIndices.length !== fbPointIndices.length)
            return undefined; // auxIndices invalid
        const indices = [];
        if (isLegacy)
            SerializationHelpers_1.SerializationHelpers.announceZeroBasedIndicesWithExternalBlocking(fbAuxIndices, fbPointIndices, numPerFace, (i0) => { indices.push(i0); });
        else
            SerializationHelpers_1.SerializationHelpers.announceZeroBasedIndicesFromSignedOneBasedIndices(fbAuxIndices, numPerFace, (i0) => { indices.push(i0); });
        if (indices.length + pointIndicesPadCount !== fbPointIndices.length)
            return undefined;
        const maxIndex = Math.max(...indices);
        const channels = [];
        for (let i = 0; i < numChannels; i++) {
            const channelHeader = fbAuxData.channels(i);
            const channelContent = this.readPolyfaceAuxChannel(channelHeader);
            if (channelContent) {
                if (maxIndex >= channelContent.valueCount)
                    return undefined; // invalid index
                channels.push(channelContent);
            }
        }
        if (!channels.length)
            return undefined;
        return new AuxData_1.PolyfaceAuxData(channels, indices);
    }
    /** Extract tagged numeric data for a mesh */
    readTaggedNumericData(accessor) {
        if (accessor) {
            const taggedNumericData = new TaggedNumericData_1.TaggedNumericData(accessor.tagA(), accessor.tagB());
            const intDataArray = nullToUndefined(accessor.intDataArray());
            const doubleDataArray = nullToUndefined(accessor.doubleDataArray());
            if (intDataArray) {
                taggedNumericData.intData = [];
                for (const c of intDataArray)
                    taggedNumericData.intData.push(c);
            }
            if (doubleDataArray) {
                taggedNumericData.doubleData = [];
                for (const c of doubleDataArray)
                    taggedNumericData.doubleData.push(c);
            }
            return taggedNumericData;
        }
        return undefined;
    }
    /**
     * Extract a mesh
     * @param variant read position in the flat buffer.
     */
    readPolyfaceFromVariant(variant) {
        const geometryType = variant.geometryType();
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagPolyface) {
            const polyfaceHeader = variant.geometry(new BGFBAccessors_1.BGFBAccessors.Polyface());
            if (polyfaceHeader) {
                const twoSided = polyfaceHeader.twoSided();
                const expectedClosure = polyfaceHeader.expectedClosure();
                const meshStyle = polyfaceHeader.meshStyle();
                const numPerFace = polyfaceHeader.numPerFace();
                const pointF64 = nullToUndefined(polyfaceHeader.pointArray());
                const paramF64 = nullToUndefined(polyfaceHeader.paramArray());
                const normalF64 = nullToUndefined(polyfaceHeader.normalArray());
                const intColorU32 = nullToUndefined(polyfaceHeader.intColorArray());
                const pointIndexI32 = nullToUndefined(polyfaceHeader.pointIndexArray());
                const paramIndexI32 = nullToUndefined(polyfaceHeader.paramIndexArray());
                const normalIndexI32 = nullToUndefined(polyfaceHeader.normalIndexArray());
                const colorIndexI32 = nullToUndefined(polyfaceHeader.colorIndexArray());
                const edgeMateIndexI32 = nullToUndefined(polyfaceHeader.edgeMateIndexArray());
                const taggedNumericDataOffset = polyfaceHeader.taggedNumericData();
                // FB polyfaces are always indexed face loop style, but we recognize both variable and fixed-size face loops.
                // (BGFBWriter only writes variable-sized face loops, but native FBWriter can write both.)
                (0, core_bentley_1.assert)(meshStyle === 1, "Unrecognized flatbuffer mesh style");
                if (meshStyle === 1 && pointF64 && pointIndexI32) {
                    const polyface = Polyface_1.IndexedPolyface.create();
                    polyface.twoSided = twoSided;
                    polyface.expectedClosure = expectedClosure;
                    // Flatbuffer parallel index array values are:
                    // * signed 1-based index into the respective data array
                    // * 0 indicates variable-sized face loop terminator (numPerFace < 2)
                    // * 0 indicates fixed-sized face loop padding (numPerFace >= 2)
                    // Native format is the same as flatbuffer.
                    // TypeScript format is 0-based indices sans terminator/padding
                    if (normalF64 && normalIndexI32) {
                        for (let i = 0; i + 2 < normalF64.length; i += 3)
                            polyface.addNormalXYZ(normalF64[i], normalF64[i + 1], normalF64[i + 2]);
                        SerializationHelpers_1.SerializationHelpers.announceZeroBasedIndicesFromSignedOneBasedIndices(normalIndexI32, numPerFace, (i) => { polyface.addNormalIndex(i); });
                    }
                    if (paramF64 && paramIndexI32) {
                        for (let i = 0; i + 1 < paramF64.length; i += 2)
                            polyface.addParamUV(paramF64[i], paramF64[i + 1]);
                        SerializationHelpers_1.SerializationHelpers.announceZeroBasedIndicesFromSignedOneBasedIndices(paramIndexI32, numPerFace, (i) => { polyface.addParamIndex(i); });
                    }
                    if (intColorU32 && colorIndexI32) {
                        for (const c of intColorU32)
                            polyface.addColor(c);
                        SerializationHelpers_1.SerializationHelpers.announceZeroBasedIndicesFromSignedOneBasedIndices(colorIndexI32, numPerFace, (i) => { polyface.addColorIndex(i); });
                    }
                    // the second callback below builds up the facetStart array as we process the main point index array
                    for (let i = 0; i + 2 < pointF64.length; i += 3)
                        polyface.addPointXYZ(pointF64[i], pointF64[i + 1], pointF64[i + 2]);
                    SerializationHelpers_1.SerializationHelpers.announceZeroBasedIndicesFromSignedOneBasedIndices(pointIndexI32, numPerFace, (i, v) => { polyface.addPointIndex(i, v); }, () => { polyface.terminateFacet(false); });
                    // FB/JSON/native edgeMateIndex array format:
                    // 0-based edgeMate indices into the parallel Polyface index arrays (and edgeMateIndex!),
                    // -1 terminator/pad, not present in TypeScript,
                    // -2 for "no edgeMate", maps to `undefined` in TypeScript
                    if (edgeMateIndexI32) {
                        const edgeMateIndex = [];
                        if (SerializationHelpers_1.SerializationHelpers.announceCompressedZeroBasedReflexiveIndices(edgeMateIndexI32, numPerFace, SerializationHelpers_1.SerializationHelpers.EdgeMateIndex.BlockSeparator, SerializationHelpers_1.SerializationHelpers.EdgeMateIndex.NoEdgeMate, (i) => edgeMateIndex.push(i))) {
                            polyface.data.edgeMateIndex = edgeMateIndex;
                        }
                    }
                    if (!polyface.validateAllIndices())
                        return undefined;
                    polyface.data.auxData = this.readPolyfaceAuxData(polyfaceHeader, polyfaceHeader.auxData());
                    if (taggedNumericDataOffset) {
                        const taggedNumericDataAccessor = nullToUndefined(taggedNumericDataOffset);
                        if (taggedNumericDataAccessor !== undefined) {
                            const taggedNumericData = this.readTaggedNumericData(taggedNumericDataAccessor);
                            if (taggedNumericData !== undefined)
                                polyface.data.setTaggedNumericData(taggedNumericData);
                        }
                    }
                    // NOTE: faceData is ignored
                    return polyface;
                }
            }
        }
        return undefined;
    }
    readCurveCollectionFromCurveVectorTable(cvTable) {
        const numChildren = cvTable.curvesLength();
        const collectionType = cvTable.type();
        const collection = createTypedCurveCollection(collectionType);
        for (let i = 0; i < numChildren; i++) {
            const childOffset = cvTable.curves(i);
            if (childOffset !== null) {
                const child = this.readCurvePrimitiveFromVariant(childOffset);
                if (child)
                    collection.tryAddChild(child);
                else {
                    const childCollection = this.readCurveCollectionFromVariantGeometry(childOffset);
                    if (childCollection)
                        collection.tryAddChild(childCollection);
                }
            }
        }
        return collection;
    }
    /**
   * Extract a curve collection
   * @param variant read position in the flat buffer.
   */
    readCurveCollectionFromVariantGeometry(variant) {
        const geometryType = variant.geometryType();
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagCurveVector) {
            const cvTable = variant.geometry(new BGFBAccessors_1.BGFBAccessors.CurveVector());
            return this.readCurveCollectionFromCurveVectorTable(cvTable);
        }
        return undefined;
    }
    /**
   * Extract a curve collection
   * @param variant read position in the flat buffer.
   */
    readSolidPrimitiveFromVariant(variant) {
        const geometryType = variant.geometryType();
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnBox) {
            const header = variant.geometry(new BGFBAccessors_1.BGFBAccessors.DgnBox());
            const detail = header.detail();
            return Box_1.Box.createDgnBox(Point3dVector3d_1.Point3d.create(detail.baseOriginX(), detail.baseOriginY(), detail.baseOriginZ()), Point3dVector3d_1.Vector3d.create(detail.vectorXX(), detail.vectorXY(), detail.vectorXZ()), Point3dVector3d_1.Vector3d.create(detail.vectorYX(), detail.vectorYY(), detail.vectorYZ()), Point3dVector3d_1.Point3d.create(detail.topOriginX(), detail.topOriginY(), detail.topOriginZ()), detail.baseX(), detail.baseY(), detail.topX(), detail.topY(), detail.capped());
        }
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnSphere) {
            const header = variant.geometry(new BGFBAccessors_1.BGFBAccessors.DgnSphere());
            const detail = header.detail();
            const lToWDetail = detail.localToWorld();
            const localToWorld = Transform_1.Transform.createRowValues(lToWDetail.axx(), lToWDetail.axy(), lToWDetail.axz(), lToWDetail.axw(), lToWDetail.ayx(), lToWDetail.ayy(), lToWDetail.ayz(), lToWDetail.ayw(), lToWDetail.azx(), lToWDetail.azy(), lToWDetail.azz(), lToWDetail.azw());
            return Sphere_1.Sphere.createEllipsoid(localToWorld, AngleSweep_1.AngleSweep.createStartSweepRadians(detail.startLatitudeRadians(), detail.latitudeSweepRadians()), detail.capped());
        }
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnCone) {
            const header = variant.geometry(new BGFBAccessors_1.BGFBAccessors.DgnCone());
            const detail = header.detail();
            const centerA = Point3dVector3d_1.Point3d.create(detail.centerAX(), detail.centerAY(), detail.centerAZ());
            const centerB = Point3dVector3d_1.Point3d.create(detail.centerBX(), detail.centerBY(), detail.centerBZ());
            const vector0 = Point3dVector3d_1.Vector3d.create(detail.vector0X(), detail.vector0Y(), detail.vector0Z());
            const vector90 = Point3dVector3d_1.Vector3d.create(detail.vector90X(), detail.vector90Y(), detail.vector90Z());
            const radiusA = detail.radiusA();
            const radiusB = detail.radiusB();
            return Cone_1.Cone.createBaseAndTarget(centerA, centerB, vector0, vector90, radiusA, radiusB, detail.capped());
        }
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnTorusPipe) {
            const header = variant.geometry(new BGFBAccessors_1.BGFBAccessors.DgnTorusPipe());
            const detail = header.detail();
            const center = Point3dVector3d_1.Point3d.create(detail.centerX(), detail.centerY(), detail.centerZ());
            const vectorX = Point3dVector3d_1.Vector3d.create(detail.vectorXX(), detail.vectorXY(), detail.vectorXZ());
            const vectorY = Point3dVector3d_1.Vector3d.create(detail.vectorYX(), detail.vectorYY(), detail.vectorYZ());
            const sweepRadians = detail.sweepRadians();
            const majorRadius = detail.majorRadius();
            const minorRadius = detail.minorRadius();
            return TorusPipe_1.TorusPipe.createDgnTorusPipe(center, vectorX, vectorY, majorRadius, minorRadius, Angle_1.Angle.createRadians(sweepRadians), detail.capped());
        }
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnExtrusion) {
            const header = variant.geometry(new BGFBAccessors_1.BGFBAccessors.DgnExtrusion());
            const dVector = new BGFBAccessors_1.BGFBAccessors.DVector3d();
            header.extrusionVector(dVector);
            const extrusionVector = Point3dVector3d_1.Vector3d.create(dVector.x(), dVector.y(), dVector.z());
            const baseCurve = header.baseCurve();
            if (baseCurve !== null) {
                const contour = this.readCurveCollectionFromCurveVectorTable(baseCurve);
                return LinearSweep_1.LinearSweep.create(contour, extrusionVector, header.capped());
            }
        }
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnRotationalSweep) {
            const header = variant.geometry(new BGFBAccessors_1.BGFBAccessors.DgnRotationalSweep());
            const dAxis = new BGFBAccessors_1.BGFBAccessors.DRay3d();
            header.axis(dAxis);
            const axis = Ray3d_1.Ray3d.createXYZUVW(dAxis.x(), dAxis.y(), dAxis.z(), dAxis.ux(), dAxis.uy(), dAxis.uz());
            const sweepAngle = Angle_1.Angle.createRadians(header.sweepRadians());
            // const numVRules = header.numVRules();
            const baseCurve = header.baseCurve();
            if (baseCurve !== null) {
                const contour = this.readCurveCollectionFromCurveVectorTable(baseCurve);
                return RotationalSweep_1.RotationalSweep.create(contour, axis, sweepAngle, header.capped());
            }
        }
        if (geometryType === BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnRuledSweep) {
            const header = variant.geometry(new BGFBAccessors_1.BGFBAccessors.DgnRuledSweep());
            const numCurves = header.curvesLength();
            const contours = [];
            for (let i = 0; i < numCurves; i++) {
                const contourTable = header.curves(i);
                if (contourTable) {
                    const contour = this.readCurveCollectionFromCurveVectorTable(contourTable);
                    if (contour)
                        contours.push(contour);
                }
            }
            if (contours.length > 0) {
                return RuledSweep_1.RuledSweep.create(contours, header.capped());
            }
        }
        return undefined;
    }
    /**
     * Extract any geometry type or array of geometry.
     * @param variant read position in the flat buffer.
     */
    readGeometryQueryFromVariant(variant) {
        const rootType = variant.geometryType();
        switch (rootType) {
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagLineSegment:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagLineString:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagEllipticArc:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagBsplineCurve:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagTransitionSpiral:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagInterpolationCurve:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagAkimaCurve: {
                return this.readCurvePrimitiveFromVariant(variant);
            }
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagCurveVector: {
                return this.readCurveCollectionFromVariantGeometry(variant);
            }
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagPolyface: {
                return this.readPolyfaceFromVariant(variant);
            }
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnBox:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnCone:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnTorusPipe:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnSphere:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnExtrusion:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnRotationalSweep:
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnRuledSweep: {
                return this.readSolidPrimitiveFromVariant(variant);
            }
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagVectorOfVariantGeometry: {
                const geometry = [];
                const offsetToVectorOfVariantGeometry = variant.geometry(new BGFBAccessors_1.BGFBAccessors.VectorOfVariantGeometry());
                for (let i = 0; i < offsetToVectorOfVariantGeometry.membersLength(); i++) {
                    const child = offsetToVectorOfVariantGeometry.members(i);
                    if (child !== null) {
                        const childGeometry = this.readGeometryQueryFromVariant(child);
                        if (childGeometry instanceof GeometryQuery_1.GeometryQuery) {
                            geometry.push(childGeometry);
                        }
                        else if (Array.isArray(childGeometry)) {
                            geometry.push(...childGeometry);
                        }
                    }
                }
                return geometry;
            }
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagBsplineSurface: {
                return this.readBSplineSurfaceFromVariant(variant);
            }
            case BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagPointString: {
                return this.readPointStringFromVariant(variant);
            }
        }
        return undefined;
    }
    /**
     * Deserialize bytes from a flatbuffer.
     * @param justTheBytes FlatBuffer bytes as created by BGFBWriter.createFlatBuffer (g);
     */
    static bytesToGeometry(theBytes, signature) {
        const newByteBuffer = new flatbuffers_1.flatbuffers.ByteBuffer(theBytes);
        if (signature) {
            if (theBytes.length < signature.length)
                return undefined;
            for (let i = 0; i < signature.length; i++)
                if (theBytes[i] !== signature[i])
                    return undefined;
            newByteBuffer.setPosition(signature.length);
        }
        const root = BGFBAccessors_1.BGFBAccessors.VariantGeometry.getRootAsVariantGeometry(newByteBuffer);
        const reader = new BGFBReader();
        return reader.readGeometryQueryFromVariant(root);
    }
}
exports.BGFBReader = BGFBReader;
/**
 * if data is "null" (the deprecated javascript idiom!) return undefined.  Otherwise return the data as its own type.
 * @param data
 */
function nullToUndefined(data) {
    if (data === null)
        return undefined;
    return data;
}
function createTypedCurveCollection(collectionType) {
    if (collectionType === 1)
        return new Path_1.Path();
    if (collectionType === 2 || collectionType === 3)
        return new Loop_1.Loop();
    if (collectionType === 4)
        return new ParityRegion_1.ParityRegion();
    if (collectionType === 5)
        return new UnionRegion_1.UnionRegion();
    return new CurveCollection_1.BagOfCurves();
}
/**
 * mappings between typescript spiral type strings and native integers.
 * @internal
 */
class DgnSpiralTypeQueries {
    // remark: this is the full list based on native DSpiral2dBase.h.
    //   This does not guarantee all types are supported.
    static spiralTypeCodeMap = [
        [10, "clothoid"],
        [11, "bloss"],
        [12, "biquadratic"],
        [13, "cosine"],
        [14, "sine"],
        [15, "Viennese"],
        [16, "weightedViennese"],
        [50, "WesternAustralian"],
        [51, "Czech"],
        [52, "AustralianRailCorp"],
        [53, "Italian"],
        [54, "PolishCubic"],
        [55, "Arema"],
        [56, "MXCubicAlongArc"],
        [57, "MXCubicAlongTangent"],
        [58, "ChineseCubic"],
        [60, "HalfCosine"],
        [61, "JapaneseCubic"],
    ];
    /** Convert native integer type (e.g. from flatbuffer) to typescript string */
    static typeCodeToString(typeCode) {
        for (const entry of DgnSpiralTypeQueries.spiralTypeCodeMap) {
            if (entry[0] === typeCode)
                return entry[1];
        }
        return undefined;
    }
    /** Convert typescript string to native integer type */
    static stringToTypeCode(s, defaultToClothoid = true) {
        for (const entry of DgnSpiralTypeQueries.spiralTypeCodeMap) {
            if (Geometry_1.Geometry.equalStringNoCase(s, entry[1]))
                return entry[0];
        }
        return defaultToClothoid ? 10 : undefined;
    }
    /** Ask if the indicated type code is a "direct" spiral */
    static isDirectSpiralType(typeCode) {
        return typeCode >= 50;
    }
}
exports.DgnSpiralTypeQueries = DgnSpiralTypeQueries;
//# sourceMappingURL=BGFBReader.js.map