/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module ElementGeometry
 */
import { IModelStatus } from "@itwin/core-bentley";
import { IModelError, LinePixels } from "@itwin/core-common";
import { EditTxn } from "./EditTxn";
import { LineStyle } from "./Element";
import { _implicitTxn } from "./internal/Symbols";
function getLinePixelsLineCode(linePixels) {
    switch (linePixels) {
        case LinePixels.Code1:
            return 1;
        case LinePixels.Code2:
            return 2;
        case LinePixels.Code3:
            return 3;
        case LinePixels.Code4:
            return 4;
        case LinePixels.Code5:
            return 5;
        case LinePixels.Code6:
            return 6;
        case LinePixels.Code7:
            return 7;
        default:
            return -1; // Invalid value...
    }
}
/** A line style definition is a uniquely named pattern that repeats as it is displayed along a curve path. In the absence of a line style, curve display is limited to solid lines with a width in pixels.
 * There are three varieties of line styles:
 * - A style described by a stroke pattern (series of dashes and gaps) that may also include symbol graphics.
 * - A style using pre-defined pixel bit patterns [[LinePixels]] for dashed display (Code1-Code7).
 * - A style that uses a texture.
 *
 * A definition is defined by one or more components. A component is saved as a "file property" and can be referenced by other components. The line style definition references a component
 * by file property id and type and is saved as a dictionary element.
 * @public
 */
export var LineStyleDefinition;
(function (LineStyleDefinition) {
    /** Line style component type identifiers */
    let ComponentType;
    (function (ComponentType) {
        /** Component type for [[LineStyleDefinition.PointSymbolProps]] */
        ComponentType[ComponentType["PointSymbol"] = 1] = "PointSymbol";
        /** Component type for [[LineStyleDefinition.CompoundProps]] */
        ComponentType[ComponentType["Compound"] = 2] = "Compound";
        /** Component type for [[LineStyleDefinition.StrokePatternProps]] */
        ComponentType[ComponentType["StrokePattern"] = 3] = "StrokePattern";
        /** Component type for [[LineStyleDefinition.StrokePointProps]] */
        ComponentType[ComponentType["StrokePoint"] = 4] = "StrokePoint";
        /** Component type for [[LinePixels]], never saved as a file property */
        ComponentType[ComponentType["Internal"] = 6] = "Internal";
        /** Component type for [[LineStyleDefinition.RasterImageProps]] */
        ComponentType[ComponentType["RasterImage"] = 7] = "RasterImage";
    })(ComponentType = LineStyleDefinition.ComponentType || (LineStyleDefinition.ComponentType = {}));
    /** Mask of values for StrokeMode */
    let StrokeMode;
    (function (StrokeMode) {
        /** Stroke represents a blank space */
        StrokeMode[StrokeMode["Gap"] = 0] = "Gap";
        /** Stroke represents a solid dash */
        StrokeMode[StrokeMode["Dash"] = 1] = "Dash";
        /** Treat stroke as rigid and continue past a corner to complete the stroke as opposed to breaking at the corner */
        StrokeMode[StrokeMode["Ray"] = 2] = "Ray";
        /** Stroke length can be stretched when [[LineStyleDefinition.StrokePatternOptions.Iteration]] and [[LineStyleDefinition.StrokePatternOptions.AutoPhase]] options are set, applicable to both Gap and Dash strokes */
        StrokeMode[StrokeMode["Scale"] = 4] = "Scale";
        /** Invert stroke in first stroke pattern */
        StrokeMode[StrokeMode["FirstInvert"] = 8] = "FirstInvert";
        /** Invert stroke in last stroke pattern */
        StrokeMode[StrokeMode["LastInvert"] = 16] = "LastInvert";
    })(StrokeMode = LineStyleDefinition.StrokeMode || (LineStyleDefinition.StrokeMode = {}));
    /** Define constant width or tapered strokes with distance specified in meters */
    let StrokeWidth;
    (function (StrokeWidth) {
        /** Stroke draws as one pixel wide line */
        StrokeWidth[StrokeWidth["None"] = 0] = "None";
        /** Half [[LineStyleDefinition.StrokeProps.orgWidth]] and [[LineStyleDefinition.StrokeProps.endWidth]] applied to left side of stroke */
        StrokeWidth[StrokeWidth["Left"] = 1] = "Left";
        /** Half [[LineStyleDefinition.StrokeProps.orgWidth]] and [[LineStyleDefinition.StrokeProps.endWidth]] applied to right side of stroke */
        StrokeWidth[StrokeWidth["Right"] = 2] = "Right";
        /** Half [[LineStyleDefinition.StrokeProps.orgWidth]] and [[LineStyleDefinition.StrokeProps.endWidth]] applied to both sides of stroke */
        StrokeWidth[StrokeWidth["Full"] = 3] = "Full";
    })(StrokeWidth = LineStyleDefinition.StrokeWidth || (LineStyleDefinition.StrokeWidth = {}));
    /** Controls appearance of stroke end caps. If StrokeCap is >= Hexagon, the end cap is stroked as an arc and the value of
     * StrokeCap indicates the number of vectors in the arc.
     */
    let StrokeCap;
    (function (StrokeCap) {
        /** Stroke displays as a closed polygon */
        StrokeCap[StrokeCap["Closed"] = 0] = "Closed";
        /** Stroke displays lines at specified width instead of a polygon */
        StrokeCap[StrokeCap["Open"] = 1] = "Open";
        /** Stroke length extended by half the stroke width */
        StrokeCap[StrokeCap["Extended"] = 2] = "Extended";
        /** Stroke end cap is a hexagon */
        StrokeCap[StrokeCap["Hexagon"] = 3] = "Hexagon";
        /** Stroke end cap is an octagon */
        StrokeCap[StrokeCap["Octagon"] = 4] = "Octagon";
        /** Stroke end cap is a decagon */
        StrokeCap[StrokeCap["Decagon"] = 5] = "Decagon";
        /** Stroke end cap is an arc */
        StrokeCap[StrokeCap["Arc"] = 30] = "Arc";
    })(StrokeCap = LineStyleDefinition.StrokeCap || (LineStyleDefinition.StrokeCap = {}));
    /** Options to control how stroke pattern is applied to underlying curve */
    let StrokePatternOptions;
    (function (StrokePatternOptions) {
        /** Use default stroke behavior */
        StrokePatternOptions[StrokePatternOptions["None"] = 0] = "None";
        /** [[LineStyleDefinition.StrokePatternProps.phase]] represents fractional distance into first stroke of pattern */
        StrokePatternOptions[StrokePatternOptions["AutoPhase"] = 1] = "AutoPhase";
        /** Use [[LineStyleDefinition.StrokePatternProps.maxIter]] to limit the number of iterations of the stroke pattern */
        StrokePatternOptions[StrokePatternOptions["Iteration"] = 8] = "Iteration";
        /** Single segment mode restarts the stroke pattern at corners instead of continuing around corners */
        StrokePatternOptions[StrokePatternOptions["Segment"] = 16] = "Segment";
        /** Center the line style and stretch the ends */
        StrokePatternOptions[StrokePatternOptions["CenterStretch"] = 32] = "CenterStretch";
    })(StrokePatternOptions = LineStyleDefinition.StrokePatternOptions || (LineStyleDefinition.StrokePatternOptions = {}));
    /** Flags to identify point symbol behavior */
    let PointSymbolFlags;
    (function (PointSymbolFlags) {
        /** Default symbol behavior */
        PointSymbolFlags[PointSymbolFlags["None"] = 0] = "None";
        /** Symbol includes 3d geometry */
        PointSymbolFlags[PointSymbolFlags["Is3d"] = 1] = "Is3d";
        /** Symbol does not allow scaling */
        PointSymbolFlags[PointSymbolFlags["NoScale"] = 2] = "NoScale";
    })(PointSymbolFlags = LineStyleDefinition.PointSymbolFlags || (LineStyleDefinition.PointSymbolFlags = {}));
    /** Symbol options for location, orientation, and behavior */
    let SymbolOptions;
    (function (SymbolOptions) {
        /** No point symbol */
        SymbolOptions[SymbolOptions["None"] = 0] = "None";
        /** Symbol at origin of stroke */
        SymbolOptions[SymbolOptions["Origin"] = 1] = "Origin";
        /** Symbol at end of stroke */
        SymbolOptions[SymbolOptions["End"] = 2] = "End";
        /** symbol at center of stroke */
        SymbolOptions[SymbolOptions["Center"] = 3] = "Center";
        /** Symbol at curve start point */
        SymbolOptions[SymbolOptions["CurveOrigin"] = 4] = "CurveOrigin";
        /** Symbol at curve end point */
        SymbolOptions[SymbolOptions["CurveEnd"] = 8] = "CurveEnd";
        /** Symbol at each vertex */
        SymbolOptions[SymbolOptions["CurveVertex"] = 16] = "CurveVertex";
        /** Adjust symbol rotation left->right */
        SymbolOptions[SymbolOptions["AdjustRotation"] = 32] = "AdjustRotation";
        /** Angle of symbol not relative to stroke direction */
        SymbolOptions[SymbolOptions["AbsoluteRotation"] = 64] = "AbsoluteRotation";
        /** No scale on variable strokes */
        SymbolOptions[SymbolOptions["NoScale"] = 256] = "NoScale";
        /** No clip on partial strokes */
        SymbolOptions[SymbolOptions["NoClip"] = 512] = "NoClip";
        /** No partial strokes */
        SymbolOptions[SymbolOptions["NoPartial"] = 1024] = "NoPartial";
        /** Project partial origin */
        SymbolOptions[SymbolOptions["ProjectOrigin"] = 2048] = "ProjectOrigin";
        /** Use color from symbol instead of inheriting curve color */
        SymbolOptions[SymbolOptions["UseColor"] = 16384] = "UseColor";
        /** Use weight from symbol instead of inheriting curve weight */
        SymbolOptions[SymbolOptions["UseWeight"] = 32768] = "UseWeight";
    })(SymbolOptions = LineStyleDefinition.SymbolOptions || (LineStyleDefinition.SymbolOptions = {}));
    /** Flags to describe a style or control style behavior */
    let StyleFlags;
    (function (StyleFlags) {
        /** Use defaults */
        StyleFlags[StyleFlags["None"] = 0] = "None";
        /** Only snap to center line and not individual strokes and symbols of line style */
        StyleFlags[StyleFlags["NoSnap"] = 4] = "NoSnap";
        /** Style represents a continuous line with width (determined by looking at components if not set) */
        StyleFlags[StyleFlags["Continuous"] = 8] = "Continuous";
        /** Style represents physical geometry and should be scaled as such */
        StyleFlags[StyleFlags["Physical"] = 128] = "Physical";
    })(StyleFlags = LineStyleDefinition.StyleFlags || (LineStyleDefinition.StyleFlags = {}));
    /** Helper methods for creating and querying line styles */
    class Utils {
        static createStrokePatternComponent(txnOrIModel, props) {
            const txn = txnOrIModel instanceof EditTxn ? txnOrIModel : txnOrIModel[_implicitTxn];
            const fileProps = { name: "LineCodeV1", namespace: "dgn_LStyle" };
            fileProps.id = txn.iModel.queryNextAvailableFileProperty(fileProps);
            txn.saveFileProperty(fileProps, JSON.stringify(props));
            return { compId: fileProps.id, compType: ComponentType.StrokePattern };
        }
        static createPointSymbolComponent(txnOrIModel, props) {
            const txn = txnOrIModel instanceof EditTxn ? txnOrIModel : txnOrIModel[_implicitTxn];
            const iModel = txn.iModel;
            // if part extents weren't supplied, set them up now.
            if (!props.baseX && !props.baseY && !props.baseZ && !props.sizeX && !props.sizeY && !props.sizeZ) {
                const geomPart = iModel.elements.getElement(props.geomPartId);
                if (!geomPart)
                    return undefined;
                props.baseX = geomPart.bbox.low.x;
                props.baseY = geomPart.bbox.low.y;
                props.baseZ = geomPart.bbox.low.z;
                props.sizeX = geomPart.bbox.high.x;
                props.sizeY = geomPart.bbox.high.y;
                props.sizeZ = geomPart.bbox.high.z;
            }
            const fileProps = { name: "PointSymV1", namespace: "dgn_LStyle" };
            fileProps.id = iModel.queryNextAvailableFileProperty(fileProps);
            txn.saveFileProperty(fileProps, JSON.stringify(props));
            return { compId: fileProps.id, compType: ComponentType.PointSymbol };
        }
        static createStrokePointComponent(txnOrIModel, props) {
            const txn = txnOrIModel instanceof EditTxn ? txnOrIModel : txnOrIModel[_implicitTxn];
            const fileProps = { name: "LinePointV1", namespace: "dgn_LStyle" };
            fileProps.id = txn.iModel.queryNextAvailableFileProperty(fileProps);
            txn.saveFileProperty(fileProps, JSON.stringify(props));
            return { compId: fileProps.id, compType: ComponentType.StrokePoint };
        }
        static createCompoundComponent(txnOrIModel, props) {
            const txn = txnOrIModel instanceof EditTxn ? txnOrIModel : txnOrIModel[_implicitTxn];
            const fileProps = { name: "CompoundV1", namespace: "dgn_LStyle" };
            fileProps.id = txn.iModel.queryNextAvailableFileProperty(fileProps);
            txn.saveFileProperty(fileProps, JSON.stringify(props));
            return { compId: fileProps.id, compType: ComponentType.Compound };
        }
        static createRasterComponent(txnOrIModel, props, image) {
            const txn = txnOrIModel instanceof EditTxn ? txnOrIModel : txnOrIModel[_implicitTxn];
            const iModel = txn.iModel;
            const rasterFileProps = { name: "RasterImageV1", namespace: "dgn_LStyle" };
            rasterFileProps.id = iModel.queryNextAvailableFileProperty(rasterFileProps);
            txn.saveFileProperty(rasterFileProps, undefined, image);
            props.imageId = rasterFileProps.id;
            const fileProps = { name: "RasterComponentV1", namespace: "dgn_LStyle" };
            fileProps.id = iModel.queryNextAvailableFileProperty(fileProps);
            txn.saveFileProperty(fileProps, JSON.stringify(props));
            return { compId: fileProps.id, compType: ComponentType.RasterImage };
        }
        /** Query for an existing line style with the supplied name. */
        static queryStyle(imodel, scopeModelId, name) {
            return imodel.elements.queryElementIdByCode(LineStyle.createCode(imodel, scopeModelId, name));
        }
        static createStyle(txnOrIModel, scopeModelId, name, props) {
            const txn = txnOrIModel instanceof EditTxn ? txnOrIModel : txnOrIModel[_implicitTxn];
            const imodel = txn.iModel;
            if (undefined === props.flags)
                props.flags = StyleFlags.NoSnap; // If flags weren't supplied, default to not snapping to stroke geometry.
            const lsProps = {
                classFullName: "BisCore:LineStyle",
                model: scopeModelId,
                code: LineStyle.createCode(imodel, scopeModelId, name),
                data: JSON.stringify(props),
            };
            return txn.insertElement(lsProps);
        }
        /** Get the name that can be use to query for an existing continuous line style. */
        static getContinuousStyleName(width) {
            if (width === undefined)
                return "Continuous";
            return `Continuous-${width}`;
        }
        /** Query for a continuous line style that can be used to create curves with physical width instead of weight in pixels and create one if it does not already exist.
         * There are 2 ways to define a continuous line style:
         * - Width is not specified in the style itself and instead will be supplied as an override for each curve that is drawn.
         *  - Defined using [[LineStyleDefinition.ComponentType.Internal]] with component id 0 [[LinePixels.Solid]] which has special behavior of being affected by width overrides.
         * - Width is specified in the style.
         *  - Defined using a single stroke component that is a long dash.
         * @param txn Optional explicit EditTxn to use if the style must be created.
         * @throws [[IModelError]] if unable to insert the line style definition element.
         * @beta
         */
        static getOrCreateContinuousStyle(imodel, scopeModelId, width, txn) {
            const writeTxn = txn ?? imodel[_implicitTxn];
            const name = this.getContinuousStyleName(width);
            const lsId = this.queryStyle(imodel, scopeModelId, name);
            if (undefined !== lsId)
                return lsId;
            if (width === undefined)
                return this.createStyle(writeTxn, scopeModelId, name, { compId: 0, compType: ComponentType.Internal, flags: StyleFlags.Continuous | StyleFlags.NoSnap });
            const strokePatternData = this.createStrokePatternComponent(writeTxn, { descr: name, strokes: [{ length: 1e37, orgWidth: width, strokeMode: StrokeMode.Dash, widthMode: StrokeWidth.Full }] });
            if (undefined === strokePatternData)
                throw new IModelError(IModelStatus.BadArg, "Unable to insert stroke component");
            return this.createStyle(writeTxn, scopeModelId, name, { compId: strokePatternData.compId, compType: strokePatternData.compType, flags: StyleFlags.Continuous | StyleFlags.NoSnap });
        }
        /** Get the name that can be use to query for an existing [[LinePixels]] line style.
         * @note Returns undefined for line pixels value that is not Code1 to Code7.
        */
        static getLinePixelsStyleName(linePixels) {
            const lineCode = getLinePixelsLineCode(linePixels);
            if (-1 === lineCode)
                return undefined;
            return `LinePixelsCodeNumber-${lineCode}`;
        }
        /** Query for a line style using the supplied [[LinePixels]] value (Code1-Code7) and create one if it does not already exist.
         * Most applications should instead use [[createStrokePatternComponent]] to define a style with physical dash and gap lengths.
         * Unlike other components, [[LineStyleDefinition.ComponentType.Internal]] uses the line code as the compId instead of a file property id.
        * @param txn Optional explicit EditTxn to use if the style must be created.
         * @throws [[IModelError]] if supplied an invalid [[LinePixels]] value or if unable to insert the line style definition element.
          * @beta
         */
        static getOrCreateLinePixelsStyle(imodel, scopeModelId, linePixels, txn) {
            const lineCode = getLinePixelsLineCode(linePixels);
            if (-1 === lineCode)
                throw new IModelError(IModelStatus.BadArg, "Invalid LinePixels");
            const name = `LinePixelsCodeNumber-${lineCode}`;
            const lsId = this.queryStyle(imodel, scopeModelId, name);
            return (undefined === lsId ? this.createStyle(txn ?? imodel[_implicitTxn], scopeModelId, name, { compId: lineCode, compType: ComponentType.Internal }) : lsId);
        }
    }
    LineStyleDefinition.Utils = Utils;
})(LineStyleDefinition || (LineStyleDefinition = {}));
//# sourceMappingURL=LineStyle.js.map