"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isSVGName = exports.isTextName = exports.LENGTH_STYLE_PROP_NAMES = void 0;
exports.webLayout = webLayout;
const isFrameRelative_1 = require("../../../../isFrameRelative");
const tree_1 = require("../../../../tree");
const attr_1 = require("../../../attr");
const color_1 = require("../../../color");
const namespaceURI_1 = require("../../../component/namespaceURI");
const parseLayoutValue_1 = require("../../../parseLayoutValue");
const parseRelativeUnit_1 = require("../../../parseRelativeUnit");
const parseTransform_1 = require("../../../parseTransform");
const style_1 = require("../../../style");
const getFontSize_1 = require("../../../util/style/getFontSize");
const getOpacity_1 = require("../../../util/style/getOpacity");
const setNodeApparentTextContent_1 = require("../../../util/style/setNodeApparentTextContent");
exports.LENGTH_STYLE_PROP_NAMES = [
    'width',
    'height',
    'maxWidth',
    'maxHeight',
    'fontSize',
];
const isTextName = (tag) => {
    return ['text'].includes(tag);
};
exports.isTextName = isTextName;
const isSVGName = (tag) => {
    return ['path', 'rect', 'circle', 'line', 'ellipse'].includes(tag);
};
exports.isSVGName = isSVGName;
const shouldExpandStyle = (style) => {
    let { display: childDisplay = 'block', width: childWidthStr = 'auto', height: childHeightStr = 'auto', } = style;
    const displayContents = childDisplay === 'contents';
    const fitWidth = childWidthStr === 'fit-content' || childWidthStr === 'auto';
    const fitHeight = childHeightStr === 'fit-content' || childHeightStr === 'auto';
    const shouldExpand = fitWidth || fitHeight || displayContents;
    return shouldExpand;
};
const maybeExpand = (parentTrait, tag, path, expandChild) => {
    const shouldExpand = shouldExpandStyle(tag.style);
    if (shouldExpand) {
        expand(parentTrait, tag, path, expandChild);
    }
};
const expand = (parentTrait, tag, path, expandChild) => {
    const childrenTags = expandChild(path);
    let i = 0;
    for (const childTag of childrenTags) {
        const { container, element } = tagToElement(childTag, parentTrait, 'div');
        childTag.element = element;
        tag.element.style.scrollSnapType = 'none';
        tag.element.appendChild(container);
        maybeExpand(parentTrait, childTag, [...path, i], expandChild);
        i++;
    }
};
const fitTreeChildren = (parentTrait, parentNode, tree, path, expandChild) => {
    let i = 0;
    for (const node of tree) {
        const childPath = [...path, i];
        const tag = node.value;
        const parentTagName = parentNode.tagName.toLowerCase();
        const { container, element } = tagToElement(tag, parentTrait, parentTagName);
        node.value.element = element;
        node.value.container = container;
        parentNode.style.scrollSnapType = 'none';
        parentNode.appendChild(container);
        if (!node.children.length) {
            maybeExpand(parentTrait, tag, childPath, expandChild);
        }
        fitTreeChildren(parentTrait, element, node.children, childPath, expandChild);
        i++;
    }
};
const tagToElement = (child, trait, parentTagName) => {
    const { name, attr, style, textContent } = child;
    let tag = name.replace('#', '').toLocaleLowerCase();
    const isText = (0, exports.isTextName)(tag);
    const isSvg = (0, exports.isSVGName)(tag);
    const tag_ = isText ? 'div' : tag;
    let container;
    let element;
    if (isSvg || tag === 'svg') {
        element = window.document.createElementNS(namespaceURI_1.namespaceURI, tag);
        if (isSvg && parentTagName !== 'svg') {
            const viewBox = attr['data-viewbox'] ?? '0 0 100 100';
            container = window.document.createElementNS(namespaceURI_1.namespaceURI, 'svg');
            container.style.display = 'block';
            container.style.width = '100%';
            container.style.height = '100%';
            const parsedViewBox = viewBox.split(' ').map(Number.parseFloat);
            const strokeWidth = (0, parseLayoutValue_1.parseLayoutValue)(style.strokeWidth ?? attr['stroke-width'] ?? '3px')[0] + 3;
            const newParsedViewBox = [
                parsedViewBox[0] + strokeWidth,
                parsedViewBox[1] + strokeWidth,
                parsedViewBox[2] - 2 * strokeWidth,
                parsedViewBox[3] - 2 * strokeWidth,
            ];
            const newViewBox = newParsedViewBox.join(' ');
            container.setAttribute('viewBox', newViewBox);
            container.appendChild(element);
        }
        else {
            container = element;
        }
    }
    else {
        container = window.document.createElement(tag_);
        element = container;
    }
    (0, attr_1.mergeAttr)(container, attr);
    (0, style_1.applyStyle)(container, style);
    if (textContent) {
        (0, setNodeApparentTextContent_1.setNodeApparentTextContent)(container, textContent);
    }
    if (isText) {
        container.style.display = 'inline';
    }
    const transformRelative = (name) => {
        let parentTrait = trait[name];
        const value = style[name];
        if (name === 'fontSize') {
            if (value.endsWith('vw')) {
                parentTrait = trait['width'];
            }
            else if (value.endsWith('vh')) {
                parentTrait = trait['height'];
            }
        }
        return (0, parseRelativeUnit_1.parseRelativeUnit)(style[name], parentTrait, parentTrait);
    };
    for (const name of exports.LENGTH_STYLE_PROP_NAMES) {
        let value = style[name];
        if (value !== undefined) {
            if (typeof value === 'number') {
                value = `${value}px`;
            }
            if ((0, isFrameRelative_1.isFrameRelativeValue)(value)) {
                const length = transformRelative(name);
                container.style[name] = `${length}px`;
            }
        }
    }
    return { element, container };
};
function webLayout(window, opt) {
    const layout = {
        reflectTreeTrait: function (system, parentTrait, tree, expandChild) {
            const parentNode = system.api.document.createElement('div');
            parentNode.style.position = 'absolute';
            parentNode.style.left = `${parentTrait.x}px`;
            parentNode.style.top = `${parentTrait.y}px`;
            parentNode.style.width = `${parentTrait.width}px`;
            parentNode.style.height = `${parentTrait.height}px`;
            parentNode.style.transform = ``;
            parentNode.style.fontSize = `${parentTrait.fontSize}px`;
            parentNode.style.visibility = 'hidden';
            parentNode.style.margin = '0';
            parentNode.style.color = (0, color_1.rgbaToHex)(parentTrait.color);
            fitTreeChildren(parentTrait, parentNode, tree, [], expandChild);
            system.foreground.layout.appendChild(parentNode);
            for (const root of tree) {
                (0, tree_1.traverseTree)(root, null, (node, parent) => {
                    const { name, attr, style } = node.value;
                    let { container } = node.value;
                    if (container.style.display === 'contents') {
                        container = (container.childNodes.item(0) ??
                            container);
                    }
                    const computedStyle = window.getComputedStyle(container);
                    const rect = container.getBoundingClientRect();
                    let { x, y, width, height } = rect;
                    if ((0, exports.isSVGName)(name) && parent && parent.value.name === 'svg') {
                        const strokeWidth = (0, parseLayoutValue_1.parseLayoutValue)(style.strokeWidth ?? attr['stroke-width'] ?? '3px')[0] + 3;
                        x -= strokeWidth;
                        y -= strokeWidth;
                        width += 2 * strokeWidth;
                        height += 2 * strokeWidth;
                    }
                    let { opacity: childOpacityStr = '1', transform: childTransform = '', fontSize: fontSizeStr = '', } = style;
                    const [childTransformX, childTransformY, childScaleX, childScaleY, childRotateX, childRotateY, childRotateZ,] = (0, parseTransform_1.parseTransform)(childTransform, parentTrait.width, parentTrait.height);
                    let childFontSizeStr = computedStyle.fontSize;
                    let fontSize = (childFontSizeStr &&
                        (0, getFontSize_1.parseFontSize)(childFontSizeStr, parentTrait.width, parentTrait.height, parentTrait.fontSize)) ||
                        parentTrait.fontSize;
                    let opacity = (0, getOpacity_1.parseOpacity)(childOpacityStr);
                    opacity *= ((parent && parent.value.trait.opacity) || 1) ?? 1;
                    if (typeof fontSizeStr === 'number') {
                        fontSizeStr = `${fontSizeStr}px`;
                    }
                    const fontSizeUnit = fontSizeStr?.match(/(px|em|rem|pt|vw|vh|%)$/)?.[1] ?? 'px';
                    if (fontSizeUnit === 'vw') {
                        fontSize = (0, parseRelativeUnit_1.parseFrameRelativeUnit)(fontSizeStr, parentTrait.width);
                    }
                    if (fontSizeUnit === 'vh') {
                        fontSize = (0, parseRelativeUnit_1.parseFrameRelativeUnit)(fontSizeStr, parentTrait.height);
                    }
                    if (fontSizeUnit === 'em') {
                        fontSize *= parentTrait.fontSize;
                    }
                    const sx = parentTrait.sx * childScaleX;
                    const sy = parentTrait.sy * childScaleY;
                    let color;
                    if (computedStyle.color) {
                        const hex = (0, color_1.colorToHex)(computedStyle.color);
                        color = (hex && (0, color_1.hexToRgba)(hex)) || parentTrait.color;
                    }
                    else {
                        color = parentTrait.color;
                    }
                    let background = color_1.TRANSPARENT_RGBA;
                    if (computedStyle.backgroundColor) {
                        const hex = (0, color_1.colorToHex)(computedStyle.backgroundColor);
                        background = (hex && (0, color_1.hexToRgba)(hex)) || color_1.TRANSPARENT_RGBA;
                    }
                    node.value.trait = {
                        x,
                        y,
                        width,
                        height,
                        opacity,
                        fontSize,
                        sx,
                        sy,
                        color,
                        background,
                    };
                });
            }
            system.foreground.layout.removeChild(parentNode);
        },
    };
    return layout;
}
//# sourceMappingURL=layout.js.map