/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Annotation
 */
import { ColorDef } from "../ColorDef";
import { FontType } from "../Fonts";
/** Predefined markers for list items in text annotations.
 * These values control the appearance of list item markers (e.g., bullet, circle, square, dash, number) that denote the start of a list item in a list.
 * @beta
 */
export var ListMarkerEnumerator;
(function (ListMarkerEnumerator) {
    /** English Alphabet */
    ListMarkerEnumerator["Letter"] = "A";
    ListMarkerEnumerator["RomanNumeral"] = "I";
    ListMarkerEnumerator["Number"] = "1";
    ListMarkerEnumerator["Bullet"] = "\u2022";
    ListMarkerEnumerator["Circle"] = "\u25CB";
    ListMarkerEnumerator["Square"] = "\u25A0";
    /** EM Dash */
    ListMarkerEnumerator["Dash"] = "\u2013";
})(ListMarkerEnumerator || (ListMarkerEnumerator = {}));
/** Set of predefined shapes that can be computed and drawn around the margins of a [[TextBlock]]
 * @beta
*/
export const textAnnotationFrameShapes = ["none", "line", "rectangle", "circle", "equilateralTriangle", "diamond", "square", "pentagon", "hexagon", "octagon", "capsule", "roundedRectangle"];
/** Set of predefined shapes that can be used as terminators for leaders in a [[TextAnnotation]]
 * @beta
*/
export const terminatorShapes = ["openArrow", "closedArrow", "closedArrowFilled", "circle", "circleFilled", "slash", "none"];
;
;
function deepFreeze(obj) {
    if (obj === null || typeof obj !== "object" || Object.isFrozen(obj))
        return;
    Object.getOwnPropertyNames(obj).forEach((prop) => {
        const value = obj[prop];
        if (value && typeof value === "object") {
            deepFreeze(value);
        }
    });
    Object.freeze(obj);
}
/** A description of the formatting to be applied to a [[TextBlockComponent]].
 * Named instances of these settings can be stored as [AnnotationTextStyle]($backend)s in an iModel.
 * @note This is an immutable type. Use [[clone]] to create a modified copy.
 * @see [[TextStyleSettingsProps]] for documentation of each of the settings.
 * @beta
 */
export class TextStyleSettings {
    /** The color of the text. */
    color;
    /** The font stored in an iModel, used to draw the contents of a [[TextRun]].
     */
    font;
    /** The height of the text, in meters. Many other settings use the text height as the basis for computing their own values.
     * For example, the height and offset from baseline of a subscript [[TextRun]]  are computed as textHeight * [[subScriptScale]] and
     * textHeight * [[subScriptOffsetFactor]], respectively.
     */
    textHeight;
    /** Multiplier used to compute the vertical distance between two lines of text.
     * The distance is computed in meters as lineSpacingFactor * [[textHeight]] of the [[TextBlock]].
     */
    lineSpacingFactor;
    /** Multiplier used to compute the vertical distance between two paragraphs of text.
     * The distance is computed in meters as paragraphSpacingFactor * the [[TextBlock]]'s [[textHeight]].
     */
    paragraphSpacingFactor;
    /** Specifies whether the content of a [[TextRun]] should be rendered **bold**. */
    isBold;
    /** Specifies whether the content of a [[TextRun]] should be rendered in *italics*. */
    isItalic;
    /** Specifies whether the content of a [[TextRun]] should be underlined. */
    isUnderlined;
    /** Multiplier used to compute the height of both the numerator and denominator of a [[FractionRun]].
     * The height is computed in meters as stackedFractionScale * [[textHeight]].
     */
    stackedFractionScale;
    /** Specifies how to separate the numerator and denominator of a [[FractionRun]]. */
    stackedFractionType;
    /** Multiplier used to compute the vertical offset from the baseline for a subscript [[TextRun]].
     * The offset is computed in meters as subScriptOffsetFactor * [[textHeight]].
     */
    subScriptOffsetFactor;
    /** Multiplier used to compute the height of a subscript [[TextRun]].
     * The height is computed as subScriptScale * [[textHeight]].
     */
    subScriptScale;
    /** Multiplier used to compute the vertical offset from the baseline for a super [[TextRun]].
     * The offset is computed in meters as superScriptOffsetFactor * [[textHeight]].
     */
    superScriptOffsetFactor;
    /** Multiplier used to compute the height of a superscript [[TextRun]].
     * The height is computed as superScriptScale * [[textHeight]].
     */
    superScriptScale;
    /** Multiplier used to compute the width of each glyph, relative to [[textHeight]]. */
    widthFactor;
    /** Properties describing appearance of leaders in a [[TextAnnotation]].
     * Used when producing geometry for [[TextAnnotation]].
     */
    leader;
    /** The size (in meters) used to calculate the tab stops in a run.
     * These are equally spaced from the left edge of the TextBlock.
     * [[tabInterval]] is also used in lists to compute the offset of each child or [[Paragraph]].
     * The [[listMarker]] is right justified on [[indentation]] + [[tabInterval]]*(depth - 1/2).
     * [[Paragraph]]s will start at [[indentation]] + [[tabInterval]]*depth.
     */
    tabInterval;
    /** The offset (in meters) from the left edge of the text block to the start of the line of text.
     * In lists, the indentation is added to offset of list items.
     * The [[listMarker]] is right justified on [[indentation]] + [[tabInterval]]*(depth - 1/2).
     * [[Paragraph]]s will start at [[indentation]] + [[tabInterval]]*depth.
     */
    indentation;
    /** The marker used to indicate the start of a list item.
     * Default: [[ListMarkerEnumerator.Number]].
     */
    listMarker;
    /** The frame settings of the [[TextAnnotation]]. */
    frame;
    /** Multiplier used to calculate the margins to surround the document content. */
    margins;
    /** The alignment of the text content. */
    justification;
    /** A fully-populated JSON representation of the default settings. A real `font` must be provided before use. */
    static defaultProps = {
        color: "subcategory",
        font: { name: "", type: FontType.TrueType },
        textHeight: 1,
        lineSpacingFactor: 0.5,
        paragraphSpacingFactor: 0.5,
        isBold: false,
        isItalic: false,
        isUnderlined: false,
        stackedFractionScale: 0.7,
        stackedFractionType: "horizontal",
        subScriptOffsetFactor: -0.15,
        subScriptScale: 2 / 3,
        superScriptOffsetFactor: 0.5,
        superScriptScale: 2 / 3,
        widthFactor: 1,
        leader: {
            color: "inherit",
            wantElbow: false,
            elbowLength: 1.0,
            terminatorShape: terminatorShapes[0],
            terminatorHeightFactor: 1.0,
            terminatorWidthFactor: 1.0,
        },
        tabInterval: 4,
        indentation: 0,
        listMarker: { enumerator: "1", terminator: "period", case: "lower" },
        frame: {
            shape: "none",
            fillColor: "none",
            borderColor: ColorDef.black.toJSON(),
            borderWeight: 1,
        },
        margins: {
            left: 0,
            right: 0,
            top: 0,
            bottom: 0
        },
        justification: "left",
    };
    /** Settings initialized to all default values. */
    static defaults = new TextStyleSettings({});
    constructor(props, defaults) {
        if (!defaults) {
            defaults = TextStyleSettings.defaultProps;
        }
        this.color = props.color ?? defaults.color;
        const font = {
            name: props.font?.name ?? defaults.font.name,
            type: props.font?.type ?? defaults.font.type,
        };
        this.font = Object.freeze(font);
        this.textHeight = props.textHeight ?? defaults.textHeight;
        this.lineSpacingFactor = props.lineSpacingFactor ?? defaults.lineSpacingFactor;
        this.paragraphSpacingFactor = props.paragraphSpacingFactor ?? defaults.paragraphSpacingFactor;
        this.isBold = props.isBold ?? defaults.isBold;
        this.isItalic = props.isItalic ?? defaults.isItalic;
        this.isUnderlined = props.isUnderlined ?? defaults.isUnderlined;
        this.stackedFractionScale = props.stackedFractionScale ?? defaults.stackedFractionScale;
        this.stackedFractionType = props.stackedFractionType ?? defaults.stackedFractionType;
        this.subScriptOffsetFactor = props.subScriptOffsetFactor ?? defaults.subScriptOffsetFactor;
        this.subScriptScale = props.subScriptScale ?? defaults.subScriptScale;
        this.superScriptOffsetFactor = props.superScriptOffsetFactor ?? defaults.superScriptOffsetFactor;
        this.superScriptScale = props.superScriptScale ?? defaults.superScriptScale;
        this.widthFactor = props.widthFactor ?? defaults.widthFactor;
        const leader = {
            color: props.leader?.color ?? defaults.leader.color,
            wantElbow: props.leader?.wantElbow ?? defaults.leader.wantElbow,
            elbowLength: props.leader?.elbowLength ?? defaults.leader.elbowLength,
            terminatorShape: props.leader?.terminatorShape ?? defaults.leader.terminatorShape,
            terminatorHeightFactor: props.leader?.terminatorHeightFactor ?? defaults.leader.terminatorHeightFactor,
            terminatorWidthFactor: props.leader?.terminatorWidthFactor ?? defaults.leader.terminatorWidthFactor,
        };
        this.leader = Object.freeze(leader);
        this.tabInterval = props.tabInterval ?? defaults.tabInterval;
        this.indentation = props.indentation ?? defaults.indentation;
        this.listMarker = props.listMarker ?? defaults.listMarker;
        const frame = {
            shape: props.frame?.shape ?? defaults.frame.shape,
            fillColor: props.frame?.fillColor ?? defaults.frame.fillColor,
            borderColor: props.frame?.borderColor ?? defaults.frame.borderColor,
            borderWeight: props.frame?.borderWeight ?? defaults.frame.borderWeight,
        };
        // Cast to indicate to TypeScript that the frame properties are all defined
        this.frame = Object.freeze(frame);
        this.margins = Object.freeze({
            left: props.margins?.left ?? defaults.margins.left,
            right: props.margins?.right ?? defaults.margins.right,
            top: props.margins?.top ?? defaults.margins.top,
            bottom: props.margins?.bottom ?? defaults.margins.bottom,
        });
        this.justification = props.justification ?? defaults.justification;
    }
    /** Create a copy of these settings, modified according to the properties defined by `alteredProps`. */
    clone(alteredProps) {
        return alteredProps ? new TextStyleSettings(alteredProps, this) : this;
    }
    /** Create settings from their JSON representation. */
    static fromJSON(props) {
        return props ? new TextStyleSettings(props) : TextStyleSettings.defaults;
    }
    toJSON() {
        return structuredClone(this);
    }
    /** Compare two [[TextLeaderStyleProps]] for equality.
     * @param other The other leader style properties to compare against.
     * @returns true if the two leader styles are equal, false otherwise.
     */
    leaderEquals(other) {
        return this.leader.color === other.color && this.leader.wantElbow === other.wantElbow
            && this.leader.elbowLength === other.elbowLength && this.leader.terminatorShape === other.terminatorShape && this.leader.terminatorHeightFactor === other.terminatorHeightFactor
            && this.leader.terminatorWidthFactor === other.terminatorWidthFactor;
    }
    frameEquals(other) {
        return this.frame?.shape === other.shape
            && this.frame?.fillColor === other.fillColor
            && this.frame?.borderColor === other.borderColor
            && this.frame?.borderWeight === other.borderWeight;
    }
    marginsEqual(other) {
        return Object.entries(this.margins).every(([key, value]) => value === other[key]);
    }
    equals(other) {
        return this.color === other.color && this.font.name === other.font.name && this.font.type === other.font.type
            && this.textHeight === other.textHeight && this.widthFactor === other.widthFactor
            && this.lineSpacingFactor === other.lineSpacingFactor && this.paragraphSpacingFactor === other.paragraphSpacingFactor
            && this.isBold === other.isBold && this.isItalic === other.isItalic && this.isUnderlined === other.isUnderlined
            && this.stackedFractionType === other.stackedFractionType && this.stackedFractionScale === other.stackedFractionScale
            && this.subScriptOffsetFactor === other.subScriptOffsetFactor && this.subScriptScale === other.subScriptScale
            && this.superScriptOffsetFactor === other.superScriptOffsetFactor && this.superScriptScale === other.superScriptScale
            && this.tabInterval === other.tabInterval && this.indentation === other.indentation
            && this.listMarker.case === other.listMarker.case && this.listMarker.enumerator === other.listMarker.enumerator && this.listMarker.terminator === other.listMarker.terminator
            && this.justification === other.justification
            && this.leaderEquals(other.leader)
            && this.frameEquals(other.frame)
            && this.marginsEqual(other.margins);
    }
    /**
     * Returns a list of validation errors for this instance.
     *
     * A TextStyleSettings object may contain values that are invalid in all contexts.
     * If this method returns any error strings, using the settings will likely result in rendering failures or runtime exceptions.
     *
     * This method only checks for universally invalid values. Additional domain-specific validation may be required depending on the context in which these settings are used.
     *
     * @returns An array of error strings describing the invalid values, or an empty array if the settings are valid.
     */
    getValidationErrors() {
        const errorMessages = [];
        if (this.font.name.trim() === "") {
            errorMessages.push("font name must be provided");
        }
        if (this.textHeight <= 0) {
            errorMessages.push("textHeight must be greater than 0");
        }
        if (this.stackedFractionScale <= 0) {
            errorMessages.push("stackedFractionScale must be greater than 0");
        }
        return errorMessages;
    }
}
deepFreeze(TextStyleSettings.defaultProps);
deepFreeze(TextStyleSettings.defaults);
//# sourceMappingURL=TextStyle.js.map