/*---------------------------------------------------------------------------------------------
* 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 { TextString } from "@itwin/core-common";
import { LineSegment3d, Point3d, Range2d, Transform, Vector2d } from "@itwin/core-geometry";
import { assert } from "@itwin/core-bentley";
function setColor(color, context) {
    if (color !== context.curColor) {
        context.curColor = color;
        context.entries.push({ color });
    }
}
function createTextString(text, run, origin) {
    assert(text.length > 0);
    const { textHeight, widthFactor, isBold, isItalic, isUnderlined } = run.style;
    return new TextString({
        text,
        font: run.fontId,
        height: textHeight,
        widthFactor,
        bold: isBold,
        italic: isItalic,
        underline: isUnderlined,
        origin,
    });
}
function processTextRun(run, transform, context) {
    assert(run.source.type === "text");
    const text = run.source.content.substring(run.charOffset, run.charOffset + run.numChars);
    if (text.length === 0) {
        return;
    }
    const ts = createTextString(text, run);
    if ("none" !== run.source.baselineShift) {
        const isSub = "subscript" === run.source.baselineShift;
        const offsetFactor = run.style[isSub ? "subScriptOffsetFactor" : "superScriptOffsetFactor"];
        const scale = run.style[isSub ? "subScriptScale" : "superScriptScale"];
        ts.origin.y += offsetFactor * ts.height;
        ts.height *= scale;
    }
    ts.transformInPlace(transform);
    setColor(run.style.color, context);
    context.entries.push({ text: ts });
}
function processFieldRun(run, transform, context) {
    assert(run.source.type === "field");
    const text = run.source.cachedContent.substring(run.charOffset, run.charOffset + run.numChars);
    if (text.length === 0) {
        return;
    }
    const ts = createTextString(text, run);
    ts.transformInPlace(transform);
    setColor(run.style.color, context);
    context.entries.push({ text: ts });
}
function createFractionTextString(text, run, origin, transform) {
    const ts = createTextString(text, run, origin);
    assert(undefined !== ts.widthFactor);
    ts.height *= run.style.stackedFractionScale;
    ts.transformInPlace(transform);
    return ts;
}
function processFractionRun(run, transform, context) {
    const source = run.source;
    assert(source.type === "fraction");
    if (source.numerator.length === 0 && source.denominator.length === 0) {
        return;
    }
    assert(undefined !== run.numeratorRange && undefined !== run.denominatorRange);
    const fontSize = new Vector2d(run.style.textHeight * run.style.widthFactor, run.style.textHeight);
    fontSize.scale(run.style.stackedFractionScale, fontSize);
    const numeratorOffset = new Point3d(run.numeratorRange.low.x, run.numeratorRange.low.y, 0);
    const denominatorOffset = new Point3d(run.denominatorRange.low.x, run.denominatorRange.low.y, 0);
    setColor(run.style.color, context);
    if (source.numerator.length > 0) {
        context.entries.push({ text: createFractionTextString(source.numerator, run, numeratorOffset, transform) });
    }
    const numeratorRange = Range2d.fromJSON(run.numeratorRange);
    const denominatorRange = Range2d.fromJSON(run.denominatorRange);
    let separator;
    if (run.style.stackedFractionType === "horizontal") {
        const fractionWidth = Math.max(numeratorRange.xLength(), denominatorRange.xLength());
        const y = 1.25 * denominatorRange.yLength();
        separator = LineSegment3d.createXYXY(0, y, fractionWidth, y);
    }
    else {
        const p0 = new Point3d(denominatorRange.low.x - fontSize.x / 2, denominatorRange.low.y + fontSize.y * (1 / 3), 0);
        const p1 = new Point3d(p0.x + fontSize.x, p0.y + fontSize.y * 1.5, 0);
        separator = LineSegment3d.createCapture(p0, p1);
    }
    separator.tryTransformInPlace(transform);
    context.entries.push({
        separator: {
            startPoint: separator.point0Ref.toJSON(),
            endPoint: separator.point1Ref.toJSON(),
        },
    });
    if (source.denominator.length > 0) {
        context.entries.push({ text: createFractionTextString(source.denominator, run, denominatorOffset, transform) });
    }
}
/**
 * Produces the geometry for a text block in a way that can be interpreted by a [[GeometryStreamBuilder]] or [[ElementBuilder.Geometry]].
 * To build the geometry for a whole [[TextAnnotation]], use [[appendTextAnnotationGeometry]] instead.
 * @param layout of the text block as computed by [[layoutTextBlock]].
 * @param documentTransform that positions the text block in world coordinates.
 * @returns TextBlockGeometryProps.
 * @beta
 */
export function produceTextBlockGeometry(layout, documentTransform) {
    const context = { entries: [] };
    for (const line of layout.lines) {
        const lineTrans = Transform.createTranslationXYZ(line.offsetFromDocument.x, line.offsetFromDocument.y, 0);
        // Create geometry for list markers if present
        if (line.marker) {
            const markerTrans = Transform.createTranslationXYZ(line.marker.offsetFromLine.x, line.marker.offsetFromLine.y, 0);
            lineTrans.multiplyTransformTransform(markerTrans, markerTrans);
            documentTransform.multiplyTransformTransform(markerTrans, markerTrans);
            processTextRun(line.marker, markerTrans, context);
        }
        // Create geometry for runs
        for (const run of line.runs) {
            // Skip runs that are solely whitespace
            if (run.source.isWhitespace) {
                continue;
            }
            const runTrans = Transform.createTranslationXYZ(run.offsetFromLine.x, run.offsetFromLine.y, 0);
            lineTrans.multiplyTransformTransform(runTrans, runTrans);
            documentTransform.multiplyTransformTransform(runTrans, runTrans);
            if ("text" === run.source.type) {
                processTextRun(run, runTrans, context);
            }
            else if ("fraction" === run.source.type) {
                processFractionRun(run, runTrans, context);
            }
            else {
                processFieldRun(run, runTrans, context);
            }
        }
    }
    return { entries: context.entries };
}
//# sourceMappingURL=TextBlockGeometry.js.map