"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BGFBWriter = void 0;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Serialization
 */
const flatbuffers_1 = require("flatbuffers");
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 KnotVector_1 = require("../bspline/KnotVector");
const Arc3d_1 = require("../curve/Arc3d");
const CurveCollection_1 = require("../curve/CurveCollection");
const CurvePrimitive_1 = require("../curve/CurvePrimitive");
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 GrowableXYZArray_1 = require("../geometry3d/GrowableXYZArray");
const Point3dVector3d_1 = require("../geometry3d/Point3dVector3d");
const AuxData_1 = require("../polyface/AuxData");
const Polyface_1 = require("../polyface/Polyface");
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 SolidPrimitive_1 = require("../solid/SolidPrimitive");
const Sphere_1 = require("../solid/Sphere");
const TorusPipe_1 = require("../solid/TorusPipe");
const BGFBAccessors_1 = require("./BGFBAccessors");
const BGFBReader_1 = require("./BGFBReader");
const SerializationHelpers_1 = require("./SerializationHelpers");
/**
 * Context to write to a flatbuffer blob.
 *  * This class is internal.
 *  * Public access is through BentleyGeometryFlatBuffer.geometryToBytes()
 * @internal
 */
class BGFBWriter {
    builder;
    constructor(defaultSize = 1024) {
        this.builder = new flatbuffers_1.flatbuffers.Builder(defaultSize);
    }
    /**
     *
     * @param data data source, as Float64Array or number[].
     * @param count optional count, used only if less than .length numbers are to be written.
     */
    writeDoubleArray(data, count) {
        if (data === undefined)
            return 0;
        let numFloats = data.length;
        if (numFloats === 0)
            return 0;
        if (count !== undefined && count < numFloats)
            numFloats = count;
        this.builder.startVector(8, numFloats, 8);
        for (let i = numFloats - 1; i >= 0; i--) {
            this.builder.addFloat64(data[i]);
        }
        return this.builder.endVector();
    }
    /**
     *
     * @param data data source, as Float64Array or number[].
     * @param count optional count, used only if less than .length numbers are to be written.
     */
    writeIntArray(data) {
        if (data === undefined)
            return 0;
        const numInt = data.length;
        if (numInt === 0)
            return 0;
        this.builder.startVector(4, numInt, 4);
        for (let i = numInt - 1; i >= 0; i--) {
            this.builder.addInt32(data[i]);
        }
        return this.builder.endVector();
    }
    /**
     *
     * @param data data source, as array derived from XYZ.
     * The data is output as a flat array of 3*data.length numbers.
     */
    writePackedYZArray(data) {
        if (data === undefined)
            return 0;
        const numFloats = data.length * 3;
        if (numFloats === 0)
            return 0;
        this.builder.startVector(8, numFloats, 8);
        // write in reverse index order, and zyx within each XYZ
        for (let i = data.length - 1; i >= 0; i--) {
            this.builder.addFloat64(data[i].z);
            this.builder.addFloat64(data[i].y);
            this.builder.addFloat64(data[i].x);
        }
        return this.builder.endVector();
    }
    writeCurveCollectionAsFBCurveVector(cv) {
        const childrenOffsets = [];
        for (const child of cv.children) {
            if (child instanceof CurvePrimitive_1.CurvePrimitive) {
                const childOffset = this.writeCurvePrimitiveAsFBVariantGeometry(child);
                if (childOffset)
                    childrenOffsets.push(childOffset);
            }
            else if (child instanceof CurveCollection_1.CurveCollection) {
                const childOffset = this.writeCurveCollectionAsFBVariantGeometry(child);
                if (childOffset)
                    childrenOffsets.push(childOffset);
            }
        }
        const childrenVectorOffset = BGFBAccessors_1.BGFBAccessors.CurveVector.createCurvesVector(this.builder, childrenOffsets);
        let cvType = 0;
        if (cv instanceof Path_1.Path)
            cvType = 1;
        else if (cv instanceof Loop_1.Loop) {
            cvType = cv.isInner ? 3 : 2;
        }
        else if (cv instanceof ParityRegion_1.ParityRegion)
            cvType = 4;
        else if (cv instanceof UnionRegion_1.UnionRegion)
            cvType = 5;
        const curveVectorOffset = BGFBAccessors_1.BGFBAccessors.CurveVector.createCurveVector(this.builder, cvType, childrenVectorOffset);
        return curveVectorOffset;
    }
    writeCurveCollectionAsFBVariantGeometry(cv) {
        const curveVectorOffset = this.writeCurveCollectionAsFBCurveVector(cv);
        if (curveVectorOffset === undefined)
            return undefined;
        return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagCurveVector, curveVectorOffset, 0);
    }
    writeInterpolationCurve3dAsFBVariantGeometry(curve) {
        const props = curve.cloneProps();
        const fitPointsOffset = this.writeDoubleArray(curve.copyFitPointsFloat64Array());
        const knotOffset = props.knots ? this.writeDoubleArray(props.knots) : 0;
        BGFBAccessors_1.BGFBAccessors.InterpolationCurve.startInterpolationCurve(this.builder);
        BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addFitPoints(this.builder, fitPointsOffset);
        if (props.order)
            BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addOrder(this.builder, props.order);
        if (props.closed)
            BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addClosed(this.builder, props.closed);
        if (props.isChordLenKnots)
            BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addIsChordLenKnots(this.builder, props.isChordLenKnots);
        if (props.isColinearTangents)
            BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addIsColinearTangents(this.builder, props.isColinearTangents);
        if (props.isChordLenKnots)
            BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addIsChordLenKnots(this.builder, props.isChordLenKnots);
        if (props.isNaturalTangents)
            BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addIsNaturalTangents(this.builder, props.isNaturalTangents);
        // REMARK: some native or flatbuffer quirk made startTangent a point and endTangent a vector.
        if (props.startTangent !== undefined) {
            const startTangentOffset = BGFBAccessors_1.BGFBAccessors.DPoint3d.createDPoint3d(this.builder, Point3dVector3d_1.XYZ.x(props.startTangent), Point3dVector3d_1.XYZ.y(props.startTangent), Point3dVector3d_1.XYZ.z(props.startTangent));
            BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addStartTangent(this.builder, startTangentOffset);
        }
        if (props.endTangent !== undefined) {
            const endTangentOffset = BGFBAccessors_1.BGFBAccessors.DVector3d.createDVector3d(this.builder, Point3dVector3d_1.XYZ.x(props.endTangent), Point3dVector3d_1.XYZ.y(props.endTangent), Point3dVector3d_1.XYZ.z(props.endTangent));
            BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addEndTangent(this.builder, endTangentOffset);
        }
        if (knotOffset !== 0)
            BGFBAccessors_1.BGFBAccessors.InterpolationCurve.addKnots(this.builder, knotOffset);
        const headerOffset = BGFBAccessors_1.BGFBAccessors.InterpolationCurve.endInterpolationCurve(this.builder);
        return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagInterpolationCurve, headerOffset, 0);
    }
    writeAkimaCurve3dAsFBVariantGeometry(curve) {
        const fitPointsOffset = this.writeDoubleArray(curve.copyFitPointsFloat64Array());
        BGFBAccessors_1.BGFBAccessors.AkimaCurve.startAkimaCurve(this.builder);
        BGFBAccessors_1.BGFBAccessors.AkimaCurve.addPoints(this.builder, fitPointsOffset);
        const headerOffset = BGFBAccessors_1.BGFBAccessors.AkimaCurve.endAkimaCurve(this.builder);
        return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagAkimaCurve, headerOffset, 0);
    }
    writeBsplineCurve3dAsFBVariantGeometry(bcurve) {
        const data = SerializationHelpers_1.SerializationHelpers.createBSplineCurveData(bcurve.polesRef, bcurve.poleDimension, bcurve.knotsRef, bcurve.numPoles, bcurve.order);
        const wrapMode = bcurve.getWrappable();
        if (KnotVector_1.BSplineWrapMode.None !== wrapMode)
            data.params.wrapMode = wrapMode;
        if (!SerializationHelpers_1.SerializationHelpers.Export.prepareBSplineCurveData(data, { jsonPoles: false }))
            return undefined;
        const closed = !!data.params.closed;
        const polesOffset = this.writeDoubleArray(data.poles);
        const weightsOffset = 0;
        const knotsOffset = this.writeDoubleArray(data.params.knots);
        const headerOffset = BGFBAccessors_1.BGFBAccessors.BsplineCurve.createBsplineCurve(this.builder, data.params.order, closed, polesOffset, weightsOffset, knotsOffset);
        return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagBsplineCurve, headerOffset, 0);
    }
    writeBSplineSurfaceAsFBVariantGeometry(bsurf) {
        let poles = bsurf.coffs;
        let weights;
        let dim = bsurf.poleDimension;
        if (bsurf instanceof BSplineSurface_1.BSplineSurface3dH) {
            poles = bsurf.copyXYZToFloat64Array(false);
            weights = bsurf.copyWeightsToFloat64Array();
            dim = 3;
        }
        const data = SerializationHelpers_1.SerializationHelpers.createBSplineSurfaceData(poles, dim, bsurf.knots[BSplineSurface_1.UVSelect.uDirection].knots, bsurf.numPolesUV(BSplineSurface_1.UVSelect.uDirection), bsurf.orderUV(BSplineSurface_1.UVSelect.uDirection), bsurf.knots[BSplineSurface_1.UVSelect.vDirection].knots, bsurf.numPolesUV(BSplineSurface_1.UVSelect.vDirection), bsurf.orderUV(BSplineSurface_1.UVSelect.vDirection));
        if (weights)
            data.weights = weights;
        const wrapModeU = bsurf.getWrappable(BSplineSurface_1.UVSelect.uDirection);
        const wrapModeV = bsurf.getWrappable(BSplineSurface_1.UVSelect.vDirection);
        if (KnotVector_1.BSplineWrapMode.None !== wrapModeU)
            data.uParams.wrapMode = wrapModeU;
        if (KnotVector_1.BSplineWrapMode.None !== wrapModeV)
            data.vParams.wrapMode = wrapModeV;
        if (!SerializationHelpers_1.SerializationHelpers.Export.prepareBSplineSurfaceData(data, { jsonPoles: false }))
            return undefined;
        // TypeScript B-spline surfaces do not support trim curves or isoline counts
        const holeOrigin = 0;
        const boundariesOffset = 0;
        const numRulesU = 0;
        const numRulesV = 0;
        const closedU = !!data.uParams.closed;
        const closedV = !!data.vParams.closed;
        const polesOffset = this.writeDoubleArray(data.poles);
        const weightsOffset = data.weights ? this.writeDoubleArray(data.weights) : 0;
        const uKnotsOffset = this.writeDoubleArray(data.uParams.knots);
        const vKnotsOffset = this.writeDoubleArray(data.vParams.knots);
        const headerOffset = BGFBAccessors_1.BGFBAccessors.BsplineSurface.createBsplineSurface(this.builder, polesOffset, weightsOffset, uKnotsOffset, vKnotsOffset, data.uParams.numPoles, data.vParams.numPoles, data.uParams.order, data.vParams.order, numRulesU, numRulesV, holeOrigin, boundariesOffset, closedU, closedV);
        return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagBsplineSurface, headerOffset, 0);
    }
    writeBsplineCurve3dHAsFBVariantGeometry(bcurve) {
        const poles = bcurve.copyXYZFloat64Array(false);
        const weights = bcurve.copyWeightsFloat64Array();
        const dim = 3;
        const data = SerializationHelpers_1.SerializationHelpers.createBSplineCurveData(poles, dim, bcurve.knotsRef, bcurve.numPoles, bcurve.order);
        data.weights = weights;
        const wrapMode = bcurve.getWrappable();
        if (KnotVector_1.BSplineWrapMode.None !== wrapMode)
            data.params.wrapMode = wrapMode;
        if (!SerializationHelpers_1.SerializationHelpers.Export.prepareBSplineCurveData(data, { jsonPoles: false }))
            return undefined;
        const closed = !!data.params.closed;
        const polesOffset = this.writeDoubleArray(data.poles);
        const weightsOffset = this.writeDoubleArray(data.weights);
        const knotsOffset = this.writeDoubleArray(data.params.knots);
        const headerOffset = BGFBAccessors_1.BGFBAccessors.BsplineCurve.createBsplineCurve(this.builder, data.params.order, closed, polesOffset, weightsOffset, knotsOffset);
        return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagBsplineCurve, headerOffset, 0);
    }
    writeCurvePrimitiveAsFBVariantGeometry(curvePrimitive) {
        if (curvePrimitive instanceof LineSegment3d_1.LineSegment3d) {
            const segmentDataOffset = BGFBAccessors_1.BGFBAccessors.DSegment3d.createDSegment3d(this.builder, curvePrimitive.point0Ref.x, curvePrimitive.point0Ref.y, curvePrimitive.point0Ref.z, curvePrimitive.point1Ref.x, curvePrimitive.point1Ref.y, curvePrimitive.point1Ref.z);
            const lineSegmentOffset = BGFBAccessors_1.BGFBAccessors.LineSegment.createLineSegment(this.builder, segmentDataOffset);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagLineSegment, lineSegmentOffset, 0);
        }
        else if (curvePrimitive instanceof Arc3d_1.Arc3d) {
            const data = curvePrimitive.toVectors();
            const arcDataOffset = BGFBAccessors_1.BGFBAccessors.DEllipse3d.createDEllipse3d(this.builder, data.center.x, data.center.y, data.center.z, data.vector0.x, data.vector0.y, data.vector0.z, data.vector90.x, data.vector90.y, data.vector90.z, data.sweep.startRadians, data.sweep.sweepRadians);
            const arcOffset = BGFBAccessors_1.BGFBAccessors.EllipticArc.createEllipticArc(this.builder, arcDataOffset);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagEllipticArc, arcOffset, 0);
        }
        else if (curvePrimitive instanceof LineString3d_1.LineString3d) {
            const coordinates = extractNumberArray(curvePrimitive.packedPoints);
            const lineStringOffset = BGFBAccessors_1.BGFBAccessors.LineString.createLineString(this.builder, BGFBAccessors_1.BGFBAccessors.LineString.createPointsVector(this.builder, coordinates));
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagLineString, lineStringOffset, 0);
        }
        else if (curvePrimitive instanceof BSplineCurve_1.BSplineCurve3d) {
            return this.writeBsplineCurve3dAsFBVariantGeometry(curvePrimitive);
        }
        else if (curvePrimitive instanceof BSplineCurve3dH_1.BSplineCurve3dH) {
            return this.writeBsplineCurve3dHAsFBVariantGeometry(curvePrimitive);
        }
        else if (curvePrimitive instanceof InterpolationCurve3d_1.InterpolationCurve3d) {
            return this.writeInterpolationCurve3dAsFBVariantGeometry(curvePrimitive);
        }
        else if (curvePrimitive instanceof AkimaCurve3d_1.AkimaCurve3d) {
            return this.writeAkimaCurve3dAsFBVariantGeometry(curvePrimitive);
        }
        else if (curvePrimitive instanceof IntegratedSpiral3d_1.IntegratedSpiral3d) {
            const placement = curvePrimitive.localToWorld;
            const typeCode = BGFBReader_1.DgnSpiralTypeQueries.stringToTypeCode(curvePrimitive.spiralType, true);
            const spiralDetailOffset = BGFBAccessors_1.BGFBAccessors.TransitionSpiralDetail.createTransitionSpiralDetail(this.builder, placement.matrix.coffs[0], placement.matrix.coffs[1], placement.matrix.coffs[2], placement.origin.x, placement.matrix.coffs[3], placement.matrix.coffs[4], placement.matrix.coffs[5], placement.origin.y, placement.matrix.coffs[6], placement.matrix.coffs[5], placement.matrix.coffs[8], placement.origin.z, curvePrimitive.activeFractionInterval.x0, curvePrimitive.activeFractionInterval.x1, curvePrimitive.bearing01.startRadians, curvePrimitive.bearing01.endRadians, TransitionSpiral3d_1.TransitionSpiral3d.radiusToCurvature(curvePrimitive.radius01.x0), TransitionSpiral3d_1.TransitionSpiral3d.radiusToCurvature(curvePrimitive.radius01.x1), typeCode, 0);
            const transitionTableOffset = BGFBAccessors_1.BGFBAccessors.TransitionSpiral.createTransitionSpiral(this.builder, spiralDetailOffset, 0, 0);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagTransitionSpiral, transitionTableOffset, 0);
        }
        else if (curvePrimitive instanceof DirectSpiral3d_1.DirectSpiral3d) {
            const placement = curvePrimitive.localToWorld;
            // direct spirals always inflect at the origin of the local frame ..
            // spiral
            const curvature0 = 0.0;
            const curvature1 = curvePrimitive.nominalCurvature1;
            const radius0 = 0.0;
            const radius1 = curvePrimitive.nominalR1; // which is 1/curvature1
            const nominalLength = curvePrimitive.nominalL1;
            const bearing0Radians = 0.0;
            const bearing1Radians = TransitionSpiral3d_1.TransitionSpiral3d.radiusRadiusLengthToSweepRadians(radius0, radius1, nominalLength);
            const typeCode = BGFBReader_1.DgnSpiralTypeQueries.stringToTypeCode(curvePrimitive.spiralType, true);
            const spiralDetailOffset = BGFBAccessors_1.BGFBAccessors.TransitionSpiralDetail.createTransitionSpiralDetail(this.builder, placement.matrix.coffs[0], placement.matrix.coffs[1], placement.matrix.coffs[2], placement.origin.x, placement.matrix.coffs[3], placement.matrix.coffs[4], placement.matrix.coffs[5], placement.origin.y, placement.matrix.coffs[6], placement.matrix.coffs[5], placement.matrix.coffs[8], placement.origin.z, curvePrimitive.activeFractionInterval.x0, curvePrimitive.activeFractionInterval.x1, bearing0Radians, bearing1Radians, curvature0, curvature1, typeCode, 0);
            const transitionTableOffset = BGFBAccessors_1.BGFBAccessors.TransitionSpiral.createTransitionSpiral(this.builder, spiralDetailOffset, 0, 0);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagTransitionSpiral, transitionTableOffset, 0);
        }
        return undefined;
    }
    writePointString3dAsFBVariantGeometry(pointString) {
        if (pointString instanceof PointString3d_1.PointString3d) {
            const coordinates = extractNumberArray(pointString.points);
            const headerOffset = BGFBAccessors_1.BGFBAccessors.PointString.createPointString(this.builder, BGFBAccessors_1.BGFBAccessors.PointString.createPointsVector(this.builder, coordinates));
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagPointString, headerOffset, 0);
        }
        return undefined;
    }
    writeSolidPrimitiveAsFBVariantGeometry(solid) {
        // NOTE: Box, Sphere, Cone, and TorusPipe have "detail" within a "table"
        // BUT:  linear, rotational, and ruled sweeps have their contour and numerics directly within their table.
        if (solid instanceof Box_1.Box) {
            const originA = solid.getBaseOrigin();
            const originB = solid.getTopOrigin();
            const vectorX = solid.getVectorX();
            const vectorY = solid.getVectorY();
            const baseX = solid.getBaseX();
            const baseY = solid.getBaseY();
            const topX = solid.getTopX();
            const topY = solid.getTopY();
            const detailOffset = BGFBAccessors_1.BGFBAccessors.DgnBoxDetail.createDgnBoxDetail(this.builder, originA.x, originA.y, originA.z, originB.x, originB.y, originB.z, vectorX.x, vectorX.y, vectorX.z, vectorY.x, vectorY.y, vectorY.z, baseX, baseY, topX, topY, solid.capped);
            const carrierOffset = BGFBAccessors_1.BGFBAccessors.DgnBox.createDgnBox(this.builder, detailOffset);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnBox, carrierOffset, 0);
        }
        else if (solid instanceof Sphere_1.Sphere) {
            const localToWorld = solid.cloneLocalToWorld();
            const sweep = solid.cloneLatitudeSweep();
            const detailOffset = BGFBAccessors_1.BGFBAccessors.DgnSphereDetail.createDgnSphereDetail(this.builder, localToWorld.matrix.coffs[0], localToWorld.matrix.coffs[1], localToWorld.matrix.coffs[2], localToWorld.origin.x, localToWorld.matrix.coffs[3], localToWorld.matrix.coffs[4], localToWorld.matrix.coffs[5], localToWorld.origin.y, localToWorld.matrix.coffs[6], localToWorld.matrix.coffs[7], localToWorld.matrix.coffs[8], localToWorld.origin.z, sweep.startRadians, sweep.sweepRadians, solid.capped);
            const carrierOffset = BGFBAccessors_1.BGFBAccessors.DgnSphere.createDgnSphere(this.builder, detailOffset);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnSphere, carrierOffset, 0);
        }
        else if (solid instanceof Cone_1.Cone) {
            const centerA = solid.getCenterA();
            const centerB = solid.getCenterB();
            const vectorX = solid.getVectorX();
            const vectorY = solid.getVectorY();
            const radiusA = solid.getRadiusA();
            const radiusB = solid.getRadiusB();
            const detailOffset = BGFBAccessors_1.BGFBAccessors.DgnConeDetail.createDgnConeDetail(this.builder, centerA.x, centerA.y, centerA.z, centerB.x, centerB.y, centerB.z, vectorX.x, vectorX.y, vectorX.z, vectorY.x, vectorY.y, vectorY.z, radiusA, radiusB, solid.capped);
            const carrierOffset = BGFBAccessors_1.BGFBAccessors.DgnCone.createDgnCone(this.builder, detailOffset);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnCone, carrierOffset, 0);
        }
        else if (solid instanceof TorusPipe_1.TorusPipe) {
            const center = solid.cloneCenter();
            const vectorX = solid.cloneVectorX();
            const vectorY = solid.cloneVectorY();
            const minorRadius = solid.getMinorRadius();
            const majorRadius = solid.getMajorRadius();
            const sweepRadians = solid.getSweepAngle().radians;
            const detailOffset = BGFBAccessors_1.BGFBAccessors.DgnTorusPipeDetail.createDgnTorusPipeDetail(this.builder, center.x, center.y, center.z, vectorX.x, vectorX.y, vectorX.z, vectorY.x, vectorY.y, vectorY.z, majorRadius, minorRadius, sweepRadians, solid.capped);
            const carrierOffset = BGFBAccessors_1.BGFBAccessors.DgnTorusPipe.createDgnTorusPipe(this.builder, detailOffset);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnTorusPipe, carrierOffset, 0);
        }
        else if (solid instanceof LinearSweep_1.LinearSweep) {
            const baseCurveOffset = this.writeCurveCollectionAsFBCurveVector(solid.getSweepContourRef().getCurves());
            const sweepVector = solid.cloneSweepVector();
            // const sweepVectorOffset = BGFBAccessors.DVector3d.createDVector3d(this.builder, sweepVector.x, sweepVector.y, sweepVector.z);
            // const carrierOffset = BGFBAccessors.DgnExtrusion.createDgnExtrusion(this.builder, contourOffset, sweepVectorOffset, solid.capped);
            // WOW -- the machine generated createDgnExtrusion expects an offset for the sweepVector, but then
            //  chokes trying to add it.
            BGFBAccessors_1.BGFBAccessors.DgnExtrusion.startDgnExtrusion(this.builder);
            BGFBAccessors_1.BGFBAccessors.DgnExtrusion.addBaseCurve(this.builder, baseCurveOffset);
            const extrusionVectorOffset = BGFBAccessors_1.BGFBAccessors.DVector3d.createDVector3d(this.builder, sweepVector.x, sweepVector.y, sweepVector.z);
            BGFBAccessors_1.BGFBAccessors.DgnExtrusion.addExtrusionVector(this.builder, extrusionVectorOffset);
            BGFBAccessors_1.BGFBAccessors.DgnExtrusion.addCapped(this.builder, solid.capped);
            const dgnExtrusionOffset = BGFBAccessors_1.BGFBAccessors.DgnExtrusion.endDgnExtrusion(this.builder);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnExtrusion, dgnExtrusionOffset, 0);
        }
        else if (solid instanceof RotationalSweep_1.RotationalSweep) {
            const baseCurveOffset = this.writeCurveCollectionAsFBCurveVector(solid.getSweepContourRef().getCurves());
            const axis = solid.cloneAxisRay();
            const sweepAngle = solid.getSweep();
            // const sweepVectorOffset = BGFBAccessors.DVector3d.createDVector3d(this.builder, sweepVector.x, sweepVector.y, sweepVector.z);
            // const carrierOffset = BGFBAccessors.DgnExtrusion.createDgnExtrusion(this.builder, contourOffset, sweepVectorOffset, solid.capped);
            // WOW -- the machine generated createDgnExtrusion expects an offset for the sweepVector, but then
            //  chokes trying to add it.
            BGFBAccessors_1.BGFBAccessors.DgnRotationalSweep.startDgnRotationalSweep(this.builder);
            BGFBAccessors_1.BGFBAccessors.DgnRotationalSweep.addBaseCurve(this.builder, baseCurveOffset);
            const axisRayOffset = BGFBAccessors_1.BGFBAccessors.DRay3d.createDRay3d(this.builder, axis.origin.x, axis.origin.y, axis.origin.z, axis.direction.x, axis.direction.y, axis.direction.z);
            BGFBAccessors_1.BGFBAccessors.DgnRotationalSweep.addAxis(this.builder, axisRayOffset);
            BGFBAccessors_1.BGFBAccessors.DgnRotationalSweep.addSweepRadians(this.builder, sweepAngle.radians);
            BGFBAccessors_1.BGFBAccessors.DgnRotationalSweep.addCapped(this.builder, solid.capped);
            const dgnRotationalSweepOffset = BGFBAccessors_1.BGFBAccessors.DgnRotationalSweep.endDgnRotationalSweep(this.builder);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnRotationalSweep, dgnRotationalSweepOffset, 0);
        }
        else if (solid instanceof RuledSweep_1.RuledSweep) {
            const contours = solid.sweepContoursRef();
            const contourOffsets = [];
            for (const contour of contours) {
                const contourOffset = this.writeCurveCollectionAsFBCurveVector(contour.getCurves());
                if (contourOffset !== undefined)
                    contourOffsets.push(contourOffset);
            }
            const contoursVectorOffset = BGFBAccessors_1.BGFBAccessors.DgnRuledSweep.createCurvesVector(this.builder, contourOffsets);
            const ruledSweepTable = BGFBAccessors_1.BGFBAccessors.DgnRuledSweep.createDgnRuledSweep(this.builder, contoursVectorOffset, solid.capped);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagDgnRuledSweep, ruledSweepTable, 0);
        }
        return undefined;
    }
    writePolyfaceAuxChannelDataAsFBVariantGeometry(channelData) {
        if (channelData instanceof AuxData_1.AuxChannelData) {
            const valuesOffset = BGFBAccessors_1.BGFBAccessors.PolyfaceAuxChannelData.createValuesVector(this.builder, channelData.values);
            return BGFBAccessors_1.BGFBAccessors.PolyfaceAuxChannelData.createPolyfaceAuxChannelData(this.builder, channelData.input, valuesOffset);
        }
        return undefined;
    }
    writePolyfaceAuxChannelAsFBVariantGeometry(channel) {
        if (channel instanceof AuxData_1.AuxChannel) {
            const channelDataOffsets = [];
            for (const channelData of channel.data) {
                channelDataOffsets.push(this.writePolyfaceAuxChannelDataAsFBVariantGeometry(channelData));
            }
            const valuesOffset = BGFBAccessors_1.BGFBAccessors.PolyfaceAuxChannel.createDataVector(this.builder, channelDataOffsets);
            const nameOffset = channel.name ? this.builder.createString(channel.name) : 0;
            const inputNameOffset = channel.inputName ? this.builder.createString(channel.inputName) : 0;
            return BGFBAccessors_1.BGFBAccessors.PolyfaceAuxChannel.createPolyfaceAuxChannel(this.builder, channel.dataType, nameOffset, inputNameOffset, valuesOffset);
        }
        return undefined;
    }
    writePolyfaceAuxDataAsFBVariantGeometry(mesh, data) {
        if (data instanceof AuxData_1.PolyfaceAuxData) {
            const channelOffsets = [];
            for (const channel of data.channels)
                channelOffsets.push(this.writePolyfaceAuxChannelAsFBVariantGeometry(channel));
            const channelOffsetsOffset = BGFBAccessors_1.BGFBAccessors.PolyfaceAuxChannel.createDataVector(this.builder, channelOffsets);
            const indexArray = [];
            this.fillOneBasedIndexArray(mesh, data.indices, undefined, 0, indexArray);
            const indicesOffset = BGFBAccessors_1.BGFBAccessors.PolyfaceAuxData.createIndicesVector(this.builder, indexArray);
            return BGFBAccessors_1.BGFBAccessors.PolyfaceAuxData.createPolyfaceAuxData(this.builder, indicesOffset, channelOffsetsOffset);
        }
        return undefined;
    }
    writeTaggedNumericDataArray(data) {
        if (data) {
            const intDataOffset = this.writeIntArray(data.intData);
            const doubleDataOffset = this.writeDoubleArray(data.doubleData);
            return BGFBAccessors_1.BGFBAccessors.TaggedNumericData.createTaggedNumericData(this.builder, data.tagA, data.tagB, intDataOffset, doubleDataOffset);
        }
        return 0;
    }
    writePolyfaceAsFBVariantGeometry(mesh) {
        if (mesh instanceof Polyface_1.IndexedPolyface) {
            // WE KNOW . . . . the polyface has blocks of zero-based indices.
            const indexArray = []; // and this will really be integers.
            const numberArray = []; // and this will really be doubles.
            copyToPackedNumberArray(numberArray, mesh.data.point.float64Data(), mesh.data.point.float64Length);
            const pointOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createPointVector(this.builder, numberArray);
            let paramIndexOffset = 0;
            let normalIndexOffset = 0;
            let colorIndexOffset = 0;
            let intColorOffset = 0;
            let normalOffset = 0;
            let paramOffset = 0;
            let auxDataOffset = 0;
            let taggedNumericDataOffset = 0;
            let edgeMateIndexOffset = 0;
            const meshStyle = 1; // always write MESH_ELM_STYLE_INDEXED_FACE_LOOPS
            const numPerFace = 0; // always write variable sized, 0-terminated face loops
            this.fillOneBasedIndexArray(mesh, mesh.data.pointIndex, mesh.data.edgeVisible, 0, indexArray);
            const twoSided = mesh.twoSided;
            const pointIndexOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createPointIndexVector(this.builder, indexArray);
            if (mesh.data.paramIndex !== undefined && mesh.data.paramIndex.length > 0) {
                this.fillOneBasedIndexArray(mesh, mesh.data.paramIndex, undefined, 0, indexArray);
                paramIndexOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createParamIndexVector(this.builder, indexArray);
            }
            if (mesh.data.normalIndex !== undefined && mesh.data.normalIndex.length > 0) {
                this.fillOneBasedIndexArray(mesh, mesh.data.normalIndex, undefined, 0, indexArray);
                normalIndexOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createNormalIndexVector(this.builder, indexArray);
            }
            if (mesh.data.colorIndex !== undefined && mesh.data.colorIndex.length > 0) {
                this.fillOneBasedIndexArray(mesh, mesh.data.colorIndex, undefined, 0, indexArray);
                colorIndexOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createColorIndexVector(this.builder, indexArray);
            }
            // only int colors are persistent
            if (mesh.data.color !== undefined && mesh.data.color.length > 0) {
                intColorOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createIntColorVector(this.builder, mesh.data.color);
            }
            if (mesh.data.normal) {
                copyToPackedNumberArray(numberArray, mesh.data.normal.float64Data(), mesh.data.normal.float64Length);
                normalOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createNormalVector(this.builder, numberArray);
            }
            if (mesh.data.param) {
                copyToPackedNumberArray(numberArray, mesh.data.param.float64Data(), mesh.data.param.float64Length);
                paramOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createPointVector(this.builder, numberArray);
            }
            if (mesh.data.auxData)
                auxDataOffset = this.writePolyfaceAuxDataAsFBVariantGeometry(mesh, mesh.data.auxData);
            if (mesh.data.taggedNumericData)
                taggedNumericDataOffset = this.writeTaggedNumericDataArray(mesh.data.taggedNumericData);
            const expectedClosure = mesh.expectedClosure;
            if (mesh.data.edgeMateIndex) {
                indexArray.length = 0;
                if (SerializationHelpers_1.SerializationHelpers.announceUncompressedZeroBasedReflexiveIndices(mesh.data.edgeMateIndex, mesh.facetStart, SerializationHelpers_1.SerializationHelpers.EdgeMateIndex.BlockSeparator, SerializationHelpers_1.SerializationHelpers.EdgeMateIndex.NoEdgeMate, (i) => indexArray.push(i))) {
                    edgeMateIndexOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createEdgeMateIndexVector(this.builder, indexArray);
                }
            }
            // NOTE: mesh.data.face is not persistent
            const polyfaceOffset = BGFBAccessors_1.BGFBAccessors.Polyface.createPolyface(this.builder, pointOffset, paramOffset, normalOffset, 0, intColorOffset, pointIndexOffset, paramIndexOffset, normalIndexOffset, colorIndexOffset, 0, numPerFace, 0, meshStyle, twoSided, 0, 0, auxDataOffset, expectedClosure, taggedNumericDataOffset, edgeMateIndexOffset);
            return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagPolyface, polyfaceOffset, 0);
        }
        return undefined;
    }
    /**
     * @param sourceIndex a map that takes zeroBasedDataIndex -> zeroBasedDataIndex, packed into unterminated face loops delineated by `mesh.facetIndex0/1`
     * @param destIndex a map that takes zeroBasedDataIndex -> signedOneBasedDataIndex, packed into optionally terminated face loops
     */
    fillOneBasedIndexArray(mesh, sourceIndex, visible, facetTerminator, destIndex) {
        destIndex.length = 0;
        const numFacet = mesh.facetCount;
        for (let facetIndex = 0; facetIndex < numFacet; facetIndex++) {
            const k0 = mesh.facetIndex0(facetIndex);
            const k1 = mesh.facetIndex1(facetIndex);
            for (let k = k0; k < k1; k++) {
                let q = sourceIndex[k] + 1;
                if (visible !== undefined && !visible[k])
                    q = -q;
                destIndex.push(q);
            }
            if (facetTerminator !== undefined)
                destIndex.push(facetTerminator);
        }
    }
    writeGeometryQueryAsFBVariantGeometry(g) {
        let offset;
        if (g instanceof CurvePrimitive_1.CurvePrimitive && (offset = this.writeCurvePrimitiveAsFBVariantGeometry(g)) !== undefined)
            return offset;
        if (g instanceof CurveCollection_1.CurveCollection && (offset = this.writeCurveCollectionAsFBVariantGeometry(g)) !== undefined)
            return offset;
        if (g instanceof Polyface_1.IndexedPolyface && (offset = this.writePolyfaceAsFBVariantGeometry(g)) !== undefined)
            return offset;
        if (g instanceof SolidPrimitive_1.SolidPrimitive && (offset = this.writeSolidPrimitiveAsFBVariantGeometry(g)) !== undefined)
            return offset;
        if (g instanceof BSplineSurface_1.BSplineSurface3d && (offset = this.writeBSplineSurfaceAsFBVariantGeometry(g)) !== undefined)
            return offset;
        if (g instanceof BSplineSurface_1.BSplineSurface3dH && (offset = this.writeBSplineSurfaceAsFBVariantGeometry(g)) !== undefined)
            return offset;
        if (g instanceof PointString3d_1.PointString3d && (offset = this.writePointString3dAsFBVariantGeometry(g)) !== undefined)
            return offset;
        // if (g instanceof CoordinateXYZ) // TODO
        return undefined;
    }
    writeGeometryQueryArrayAsFBVariantGeometry(allGeometry) {
        if (Array.isArray(allGeometry)) {
            const allOffsets = [];
            for (const g of allGeometry) {
                const offset = this.writeGeometryQueryAsFBVariantGeometry(g);
                if (offset !== undefined)
                    allOffsets.push(offset);
            }
            if (allOffsets.length > 0) {
                const membersOffset = BGFBAccessors_1.BGFBAccessors.VectorOfVariantGeometry.createMembersVector(this.builder, allOffsets);
                const vectorOffset = BGFBAccessors_1.BGFBAccessors.VectorOfVariantGeometry.createVectorOfVariantGeometry(this.builder, membersOffset);
                return BGFBAccessors_1.BGFBAccessors.VariantGeometry.createVariantGeometry(this.builder, BGFBAccessors_1.BGFBAccessors.VariantGeometryUnion.tagVectorOfVariantGeometry, vectorOffset, 0);
            }
        }
        else if (allGeometry instanceof GeometryQuery_1.GeometryQuery)
            return this.writeGeometryQueryAsFBVariantGeometry(allGeometry);
        return undefined;
    }
    /**
     * Serialize bytes to a flatbuffer.
     */
    static geometryToBytes(data, signatureBytes) {
        const writer = new BGFBWriter();
        const rootOffset = writer.writeGeometryQueryArrayAsFBVariantGeometry(data);
        if (rootOffset !== undefined) {
            const builder = writer.builder;
            builder.finish(rootOffset);
            const buffer = builder.dataBuffer();
            if (!signatureBytes) {
                return buffer.bytes().slice(buffer.position());
            }
            else if (buffer.position() >= signatureBytes.length) {
                // The buffer has space for the signature ahead of its position . . .
                const i0 = buffer.position() - signatureBytes.length;
                let i = i0;
                for (const k of signatureBytes)
                    buffer.bytes()[i++] = k;
                return buffer.bytes().slice(i0);
            }
            else {
                // There is no space ahead of the position () . . .
                // coverage remark: I have never seen this happen for real.
                //  It has been exercised by adding 1024 to the signatureBytes.length test to force this branch.
                const num1 = buffer.bytes().length - buffer.position();
                const num0 = signatureBytes.length;
                const newBytes = new Uint8Array(num0 + num1);
                newBytes.set(signatureBytes, 0);
                newBytes.set(buffer.bytes().slice(buffer.position()), num0);
                return newBytes;
            }
        }
        return undefined;
    }
}
exports.BGFBWriter = BGFBWriter;
function extractNumberArray(data) {
    const result = [];
    if (data instanceof GrowableXYZArray_1.GrowableXYZArray) {
        // ugh -- accessors only deal with number[] ..
        const numCoordinate = 3 * data.length;
        const source = data.float64Data();
        for (let i = 0; i < numCoordinate; i++)
            result.push(source[i]);
        return result;
    }
    else if (Array.isArray(data)) {
        for (const xyz of data)
            result.push(xyz.x, xyz.y, xyz.z);
    }
    return result;
}
/** Copy the active data to a simple number array. */
function copyToPackedNumberArray(dest, source, count) {
    dest.length = 0;
    for (let i = 0; i < count; i++)
        dest.push(source[i]);
}
//# sourceMappingURL=BGFBWriter.js.map