"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.childrenOverflow = exports.MAX_HEIGHT = exports.MAX_WIDTH = exports.MIN_WIDTH = exports.CONTAINER_COLUMN_RIGHT_MARGIN = exports.CONTAINER_COLUMN_LEFT_MARGIN = exports.CONTAINER_ROW_RIGHT_MARGIN = exports.CONTAINER_ROW_LEFT_MARGIN = exports.SPACE_WIDTH = exports.COLON_WIDTH = exports.COMMA_WIDTH = exports.DELIMITER_HEIGHT = exports.DELIMITER_WIDTH = exports.LEAF_HEIGHT = void 0;
exports.measureSpecialCharacters = measureSpecialCharacters;
exports.getLeafWidth = getLeafWidth;
exports.getDatumWidth = getDatumWidth;
exports.getLeafHeight = getLeafHeight;
exports.getDatumHeight = getDatumHeight;
exports.getDatumSize = getDatumSize;
const parser_1 = require("../../spec/parser");
exports.LEAF_HEIGHT = 16;
exports.DELIMITER_WIDTH = 6;
exports.DELIMITER_HEIGHT = exports.LEAF_HEIGHT;
exports.COMMA_WIDTH = 6;
exports.COLON_WIDTH = 6;
exports.SPACE_WIDTH = 2;
exports.CONTAINER_ROW_LEFT_MARGIN = 1;
exports.CONTAINER_ROW_RIGHT_MARGIN = 1;
exports.CONTAINER_COLUMN_LEFT_MARGIN = 7;
exports.CONTAINER_COLUMN_RIGHT_MARGIN = 7;
exports.MIN_WIDTH = 6; // cursor
exports.MAX_WIDTH = 180;
exports.MAX_HEIGHT = 180;
function measureSpecialCharacters(value, fontSize, measureText) {
    let count = 0;
    let width = 0;
    let height = 0;
    const specialCharRegex = /[\u0080-\uD7FF\uE000-\uFFFF]/g;
    while (true) {
        const match = specialCharRegex.exec(value);
        if (match) {
            const [char] = match;
            count++;
            const size = measureText(char, fontSize, Infinity);
            width += size.width;
            height = Math.max(height, size.height);
        }
        else {
            break;
        }
    }
    return { count, width, height };
}
function getLeafWidth(value, fontSize, measureText) {
    const k = fontSize / 12;
    if (value === '') {
        return exports.MIN_WIDTH * k;
    }
    const _value = value.replace(/\n/g, '\\n');
    const { count: specialCharCount, width: specialCharTotalWidth } = measureSpecialCharacters(_value, fontSize, measureText);
    return (Math.min(Math.max((_value.length - specialCharCount) * 6 * k, exports.MIN_WIDTH * k), exports.MAX_WIDTH * k) + specialCharTotalWidth);
}
const calcVerticalHeight = (data, fontSize, measureText) => {
    const verticalHeight = 2 * getLeafHeight('', fontSize, measureText) +
        data.children.reduce((acc, c) => acc + getDatumHeight(c, fontSize, measureText), 0);
    return verticalHeight;
};
// calculate width assuming there is no overflow
const calcHorizontalWidth = (data, fontSize, measureText) => {
    if (data.type === parser_1.TreeNodeType.ObjectLiteral ||
        data.type === parser_1.TreeNodeType.ArrayLiteral) {
        let width = 1; // mobile
        width += 2 * exports.DELIMITER_WIDTH;
        width += exports.CONTAINER_ROW_LEFT_MARGIN;
        width += exports.CONTAINER_ROW_RIGHT_MARGIN;
        width += Math.max(data.children.reduce((acc, c) => acc + calcHorizontalWidth(c, fontSize, measureText), 0), exports.MIN_WIDTH);
        if (data.children.length > 1) {
            width += (data.children.length - 1) * (exports.COMMA_WIDTH + exports.SPACE_WIDTH);
        }
        return width;
    }
    else if (data.type === parser_1.TreeNodeType.KeyValue) {
        return (calcHorizontalWidth(data.children[0], fontSize, measureText) +
            exports.COLON_WIDTH +
            exports.SPACE_WIDTH +
            calcHorizontalWidth(data.children[1], fontSize, measureText));
    }
    else {
        return getDatumWidth(data, fontSize, measureText);
    }
};
const childrenOverflow = (data, fontSize, measureText) => {
    const { type } = data;
    if (type === parser_1.TreeNodeType.ObjectLiteral ||
        type === parser_1.TreeNodeType.ArrayLiteral ||
        type === parser_1.TreeNodeType.ArrayExpression ||
        type === parser_1.TreeNodeType.PropExpression ||
        type === parser_1.TreeNodeType.KeyValue ||
        type === parser_1.TreeNodeType.Expression ||
        type === parser_1.TreeNodeType.Or ||
        type === parser_1.TreeNodeType.And) {
        const totalWidth = calcHorizontalWidth(data, fontSize, measureText);
        if (totalWidth > exports.MAX_WIDTH) {
            return true;
        }
        for (let child of data.children) {
            if ((0, exports.childrenOverflow)(child, fontSize, measureText)) {
                return true;
            }
        }
    }
    return false;
};
exports.childrenOverflow = childrenOverflow;
function getDatumWidth(data, fontSize, measureText) {
    let { value, type } = data;
    if (value === '') {
        return exports.MIN_WIDTH;
    }
    let horizontalWidth;
    let width = exports.MIN_WIDTH;
    switch (type) {
        case parser_1.TreeNodeType.Any:
        case parser_1.TreeNodeType.Invalid:
        case parser_1.TreeNodeType.Generic:
        case parser_1.TreeNodeType.Null:
        case parser_1.TreeNodeType.Regex:
        case parser_1.TreeNodeType.Identifier:
        case parser_1.TreeNodeType.StringLiteral:
        case parser_1.TreeNodeType.BooleanLiteral:
        case parser_1.TreeNodeType.Object:
        case parser_1.TreeNodeType.String:
        case parser_1.TreeNodeType.Number:
        case parser_1.TreeNodeType.Boolean:
        case parser_1.TreeNodeType.NumberLiteral:
        case parser_1.TreeNodeType.RegexLiteral:
        case parser_1.TreeNodeType.Unit:
        case parser_1.TreeNodeType.Class:
        case parser_1.TreeNodeType.ClassLiteral:
        case parser_1.TreeNodeType.ArithmeticExpression:
            width = getLeafWidth(value, fontSize, measureText);
            break;
        case parser_1.TreeNodeType.ArrayLiteral:
        case parser_1.TreeNodeType.ObjectLiteral:
            horizontalWidth = calcHorizontalWidth(data, fontSize, measureText);
            if (horizontalWidth <= exports.MAX_WIDTH) {
                width = horizontalWidth;
            }
            else {
                width =
                    exports.CONTAINER_COLUMN_LEFT_MARGIN +
                        exports.CONTAINER_COLUMN_RIGHT_MARGIN +
                        Math.max(data.children.reduce((acc, c, index) => Math.max(acc, getDatumWidth(c, fontSize, measureText) +
                            (index < data.children.length - 1
                                ? exports.COMMA_WIDTH + exports.SPACE_WIDTH
                                : 0)), 0), exports.MIN_WIDTH);
            }
            break;
        case parser_1.TreeNodeType.Or:
        case parser_1.TreeNodeType.And:
            width = data.children.reduce((acc, c, index) => {
                const _width = calcHorizontalWidth(c, fontSize, measureText);
                const nextWidth = acc +
                    getDatumWidth(c, fontSize, measureText) +
                    (index < data.children.length - 1 ? exports.DELIMITER_WIDTH : 0);
                if (nextWidth <= exports.MAX_WIDTH) {
                    return nextWidth;
                }
                else {
                    return Math.max(acc, _width);
                }
            }, 0);
            break;
        case parser_1.TreeNodeType.PropExpression:
            width = data.children.reduce((acc, c, index) => {
                const _width = calcHorizontalWidth(c, fontSize, measureText);
                const nextWidth = acc + getDatumWidth(c, fontSize, measureText);
                if (nextWidth <= exports.MAX_WIDTH) {
                    return nextWidth;
                }
                else {
                    return Math.max(acc, _width);
                }
            }, 0);
            break;
        case parser_1.TreeNodeType.ArrayExpression:
        case parser_1.TreeNodeType.ObjectExpression:
            horizontalWidth =
                calcHorizontalWidth(data.children[0], fontSize, measureText) +
                    2 * exports.DELIMITER_WIDTH;
            if (horizontalWidth <= exports.MAX_WIDTH) {
                width = horizontalWidth;
            }
            else {
                width = getDatumWidth(data.children[0], fontSize, measureText);
            }
            break;
        case parser_1.TreeNodeType.Expression:
            width =
                getDatumWidth(data.children[0], fontSize, measureText) +
                    2 * exports.DELIMITER_WIDTH;
            break;
        case parser_1.TreeNodeType.KeyValue:
            {
                const valueOverflow = (0, exports.childrenOverflow)(data.children[1], fontSize, measureText);
                if (valueOverflow) {
                    width = Math.max(getDatumWidth(data.children[0], fontSize, measureText) +
                        exports.COLON_WIDTH, getDatumWidth(data.children[1], fontSize, measureText));
                }
                else {
                    width =
                        getDatumWidth(data.children[0], fontSize, measureText) +
                            getDatumWidth(data.children[1], fontSize, measureText) +
                            exports.COLON_WIDTH +
                            exports.SPACE_WIDTH;
                }
            }
            break;
        default:
            width = exports.MIN_WIDTH;
    }
    return width;
}
function getLeafHeight(value, fontSize, measureText) {
    const { height } = measureSpecialCharacters(value, fontSize, measureText);
    return Math.max(height, (exports.LEAF_HEIGHT * fontSize) / 12);
}
function getDatumHeight(data, fontSize, measureText) {
    let horizontalWidth;
    let verticalHeight;
    let overflowWidth;
    let height = getLeafHeight(data.value, fontSize, measureText);
    switch (data.type) {
        case parser_1.TreeNodeType.Generic:
        case parser_1.TreeNodeType.Identifier:
        case parser_1.TreeNodeType.Regex:
        case parser_1.TreeNodeType.StringLiteral:
        case parser_1.TreeNodeType.BooleanLiteral:
        case parser_1.TreeNodeType.Object:
        case parser_1.TreeNodeType.String:
        case parser_1.TreeNodeType.Number:
        case parser_1.TreeNodeType.Boolean:
        case parser_1.TreeNodeType.NumberLiteral:
        case parser_1.TreeNodeType.RegexLiteral:
        case parser_1.TreeNodeType.Unit:
        case parser_1.TreeNodeType.Class:
        case parser_1.TreeNodeType.ClassLiteral:
            break;
        case parser_1.TreeNodeType.ObjectLiteral:
        case parser_1.TreeNodeType.ArrayLiteral:
        case parser_1.TreeNodeType.Or:
        case parser_1.TreeNodeType.And:
        case parser_1.TreeNodeType.Expression:
            horizontalWidth = calcHorizontalWidth(data, fontSize, measureText);
            overflowWidth = horizontalWidth > exports.MAX_WIDTH;
            if (!overflowWidth) {
                //
            }
            else {
                verticalHeight = calcVerticalHeight(data, fontSize, measureText);
                height = verticalHeight;
            }
            break;
        case parser_1.TreeNodeType.ArrayExpression:
        case parser_1.TreeNodeType.ObjectExpression:
            horizontalWidth =
                calcHorizontalWidth(data.children[0], fontSize, measureText) +
                    2 * exports.DELIMITER_WIDTH;
            overflowWidth = horizontalWidth > exports.MAX_WIDTH;
            if (!overflowWidth) {
                //
            }
            else {
                verticalHeight = calcVerticalHeight(data.children[0], fontSize, measureText);
                height = verticalHeight;
            }
            break;
        case parser_1.TreeNodeType.KeyValue:
            height = getDatumHeight(data.children[1], fontSize, measureText);
            break;
        default:
        //
    }
    return height;
}
// datum size can be safely cached
const _sizeCache = {};
function getDatumSize(data, fontSize, measureText) {
    const { value } = data;
    if (_sizeCache[value]) {
        return _sizeCache[value];
    }
    const size = {
        width: getDatumWidth(data, fontSize, measureText),
        height: getDatumHeight(data, fontSize, measureText),
    };
    _sizeCache[value] = size;
    return size;
}
//# sourceMappingURL=getDatumSize.js.map