import { CanvasTextFontFamily, CanvasTextFontStyle, CanvasTextFontWeight, DEFAULT_ROUGHNESS, } from '../../consts.js';
import { isPointIn } from '../../utils/math-utils.js';
import { TextAlign } from '../consts.js';
import { SurfaceElement } from '../surface-element.js';
import { deltaInsertsToChunks, getFontString, getLineHeight, isRTL, wrapText, } from '../text/utils.js';
import { SHAPE_TEXT_FONT_SIZE, SHAPE_TEXT_PADDING } from './consts.js';
import { ShapeMethodsMap } from './shapes/index.js';
export class ShapeElement extends SurfaceElement {
    constructor() {
        super(...arguments);
        this._localProps = ['textDisplay', 'opacity', 'display'];
    }
    get textDisplay() {
        return (this._stashedValues.get('textDisplay') ?? true);
    }
    set textDisplay(val) {
        this._stashedValues.set('textDisplay', val);
    }
    get shapeType() {
        const shapeType = this.yMap.get('shapeType');
        return shapeType;
    }
    get radius() {
        const radius = this.yMap.get('radius');
        return radius;
    }
    get filled() {
        const filled = this.yMap.get('filled');
        return filled;
    }
    get fillColor() {
        const fillColor = this.yMap.get('fillColor');
        return fillColor;
    }
    get strokeWidth() {
        const strokeWidth = this.yMap.get('strokeWidth');
        return strokeWidth;
    }
    get strokeColor() {
        const strokeColor = this.yMap.get('strokeColor');
        return strokeColor;
    }
    get strokeStyle() {
        const strokeStyle = this.yMap.get('strokeStyle');
        return strokeStyle;
    }
    get roughness() {
        const roughness = this.yMap.get('roughness') ?? DEFAULT_ROUGHNESS;
        return roughness;
    }
    get realStrokeColor() {
        return this.computedValue(this.strokeColor);
    }
    get realFillColor() {
        return this.computedValue(this.fillColor);
    }
    get shapeStyle() {
        const shapeStyle = this.yMap.get('shapeStyle');
        return shapeStyle;
    }
    get text() {
        const text = this.yMap.get('text');
        return text;
    }
    get color() {
        const color = this.yMap.get('color') ?? '#000000';
        return color;
    }
    get fontSize() {
        const fontSize = this.yMap.get('fontSize') ??
            SHAPE_TEXT_FONT_SIZE.MEDIUM;
        return fontSize;
    }
    get fontFamily() {
        const fontFamily = this.yMap.get('fontFamily') ??
            CanvasTextFontFamily.Inter;
        return fontFamily;
    }
    get fontWeight() {
        return (this.yMap.get('fontWeight') ??
            CanvasTextFontWeight.Regular);
    }
    get fontStyle() {
        return (this.yMap.get('fontStyle') ??
            CanvasTextFontStyle.Normal);
    }
    get textAlign() {
        const textAlign = this.yMap.get('textAlign') ?? TextAlign.Center;
        return textAlign;
    }
    get textHorizontalAlign() {
        const textHorizontalAlign = this.yMap.get('textHorizontalAlign') ??
            'center';
        return textHorizontalAlign;
    }
    get textVerticalAlign() {
        const textVerticalAlign = this.yMap.get('textVerticalAlign') ??
            'center';
        return textVerticalAlign;
    }
    get wrapTextDeltas() {
        const { text, font, w } = this;
        if (!text)
            return [];
        const deltas = text.toDelta().flatMap(delta => ({
            insert: wrapText(delta.insert, font, w - SHAPE_TEXT_PADDING * 2),
            attributes: delta.attributes,
        }));
        return deltas;
    }
    get font() {
        const { fontStyle, fontWeight, fontSize, fontFamily } = this;
        return getFontString({
            fontStyle,
            fontWeight,
            fontSize,
            fontFamily,
        });
    }
    get horizontalOffset() {
        const { textAlign, w } = this;
        return textAlign === 'center'
            ? w / 2
            : textAlign === 'right'
                ? w - SHAPE_TEXT_PADDING
                : SHAPE_TEXT_PADDING;
    }
    verticalOffset(lines, lineHeight) {
        const { textVerticalAlign, h } = this;
        return textVerticalAlign === 'center'
            ? (h - lineHeight * lines.length) / 2
            : textVerticalAlign === 'top'
                ? SHAPE_TEXT_PADDING
                : h - lineHeight * lines.length - SHAPE_TEXT_PADDING;
    }
    hitTest(x, y, options) {
        const pierce = options.pierce ?? true;
        if (!pierce) {
            return isPointIn(this, x, y);
        }
        const { hitTest } = ShapeMethodsMap[this.shapeType];
        options.ignoreTransparent = options.ignoreTransparent ?? true;
        return hitTest.apply(this, [x, y, options]);
    }
    containedByBounds(bounds) {
        return ShapeMethodsMap[this.shapeType].containedByBounds(bounds, this);
    }
    intersectWithLine(start, end) {
        return ShapeMethodsMap[this.shapeType].intersectWithLine(start, end, this);
    }
    getNearestPoint(point) {
        return ShapeMethodsMap[this.shapeType].getNearestPoint(point, this);
    }
    getRelativePointLocation(point) {
        return ShapeMethodsMap[this.shapeType].getRelativePointLocation(point, this);
    }
    render(ctx, matrix, rc) {
        const { render } = ShapeMethodsMap[this.shapeType];
        render(ctx, matrix, rc, this);
        if (this.textDisplay ?? true) {
            this._renderText(ctx);
        }
    }
    _renderText(ctx) {
        const { text, color, fontSize, fontFamily, textAlign } = this;
        if (!text)
            return;
        const lineHeight = getLineHeight(fontFamily, fontSize);
        const font = this.font;
        const lines = deltaInsertsToChunks(this.wrapTextDeltas);
        const horizontalOffset = this.horizontalOffset;
        const verticalOffset = this.verticalOffset(lines, lineHeight);
        for (const [lineIndex, line] of lines.entries()) {
            for (const delta of line) {
                ctx.save();
                const str = delta.insert;
                const rtl = isRTL(str);
                const shouldTemporarilyAttach = rtl && !ctx.canvas.isConnected;
                if (shouldTemporarilyAttach) {
                    // to correctly render RTL text mixed with LTR, we have to append it
                    // to the DOM
                    document.body.appendChild(ctx.canvas);
                }
                ctx.canvas.setAttribute('dir', rtl ? 'rtl' : 'ltr');
                ctx.font = font;
                ctx.fillStyle = this.computedValue(color);
                ctx.textAlign = textAlign;
                ctx.textBaseline = 'ideographic';
                ctx.fillText(str, horizontalOffset - 2, 
                // 1.5 is a "magic number" used to align the text rendered on the canvas with the text in the DOM.
                // This approach is employed until a better or proper handling method is discovered.
                (lineIndex + 1) * lineHeight + verticalOffset - 1.5);
                if (shouldTemporarilyAttach) {
                    ctx.canvas.remove();
                }
                ctx.restore();
            }
        }
    }
}
//# sourceMappingURL=shape-element.js.map