"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SemanticSkeleton = void 0;
const BaseUtil = require("../common/base_util.js");
const engine_js_1 = require("../common/engine.js");
const XpathUtil = require("../common/xpath_util.js");
const enrich_attr_js_1 = require("../enrich_mathml/enrich_attr.js");
const semantic_meaning_js_1 = require("./semantic_meaning.js");
const Options = {
    tree: false
};
class SemanticSkeleton {
    static fromTree(tree) {
        return SemanticSkeleton.fromNode(tree.root);
    }
    static fromNode(node) {
        return new SemanticSkeleton(SemanticSkeleton.fromNode_(node));
    }
    static fromString(skel) {
        return new SemanticSkeleton(SemanticSkeleton.fromString_(skel));
    }
    static simpleCollapseStructure(strct) {
        return typeof strct === 'number';
    }
    static contentCollapseStructure(strct) {
        return (!!strct &&
            !SemanticSkeleton.simpleCollapseStructure(strct) &&
            strct[0] === 'c');
    }
    static interleaveIds(first, second) {
        return BaseUtil.interleaveLists(SemanticSkeleton.collapsedLeafs(first), SemanticSkeleton.collapsedLeafs(second));
    }
    static collapsedLeafs(...args) {
        const collapseStructure = (coll) => {
            if (SemanticSkeleton.simpleCollapseStructure(coll)) {
                return [coll];
            }
            coll = coll;
            return SemanticSkeleton.contentCollapseStructure(coll[1])
                ? coll.slice(2)
                : coll.slice(1);
        };
        return args.reduce((x, y) => x.concat(collapseStructure(y)), []);
    }
    static fromStructure(mml, tree) {
        return new SemanticSkeleton(SemanticSkeleton.tree_(mml, tree.root));
    }
    static combineContentChildren(type, _role, content, children) {
        switch (type) {
            case semantic_meaning_js_1.SemanticType.RELSEQ:
            case semantic_meaning_js_1.SemanticType.INFIXOP:
            case semantic_meaning_js_1.SemanticType.MULTIREL:
                return BaseUtil.interleaveLists(children, content);
            case semantic_meaning_js_1.SemanticType.PREFIXOP:
                return content.concat(children);
            case semantic_meaning_js_1.SemanticType.POSTFIXOP:
                return children.concat(content);
            case semantic_meaning_js_1.SemanticType.MATRIX:
            case semantic_meaning_js_1.SemanticType.VECTOR:
            case semantic_meaning_js_1.SemanticType.FENCED:
                children.unshift(content[0]);
                children.push(content[1]);
                return children;
            case semantic_meaning_js_1.SemanticType.CASES:
                children.unshift(content[0]);
                return children;
            case semantic_meaning_js_1.SemanticType.APPL:
                return [children[0], content[0], children[1]];
            case semantic_meaning_js_1.SemanticType.ROOT:
                return [children[0], children[1]];
            case semantic_meaning_js_1.SemanticType.ROW:
            case semantic_meaning_js_1.SemanticType.LINE:
                if (content.length) {
                    children.unshift(content[0]);
                }
                return children;
            default:
                return children;
        }
    }
    static makeSexp_(struct) {
        if (SemanticSkeleton.simpleCollapseStructure(struct)) {
            return struct.toString();
        }
        if (SemanticSkeleton.contentCollapseStructure(struct)) {
            return ('(' +
                'c ' +
                struct.slice(1).map(SemanticSkeleton.makeSexp_).join(' ') +
                ')');
        }
        return ('(' + struct.map(SemanticSkeleton.makeSexp_).join(' ') + ')');
    }
    static fromString_(skeleton) {
        let str = skeleton.replace(/\(/g, '[');
        str = str.replace(/\)/g, ']');
        str = str.replace(/ /g, ',');
        str = str.replace(/c/g, '"c"');
        return JSON.parse(str);
    }
    static fromNode_(node) {
        if (!node) {
            return [];
        }
        const content = node.contentNodes;
        let contentStructure;
        if (content.length) {
            contentStructure = content.map(SemanticSkeleton.fromNode_);
            contentStructure.unshift('c');
        }
        const children = node.childNodes;
        if (!children.length) {
            return content.length ? [node.id, contentStructure] : node.id;
        }
        const structure = children.map(SemanticSkeleton.fromNode_);
        if (content.length) {
            structure.unshift(contentStructure);
        }
        structure.unshift(node.id);
        return structure;
    }
    static tree_(mml, node, level = 0, posinset = 1, setsize = 1) {
        if (!node) {
            return [];
        }
        const id = node.id;
        const skeleton = [id];
        XpathUtil.updateEvaluator(mml);
        const mmlChild = XpathUtil.evalXPath(`.//self::*[@${enrich_attr_js_1.Attribute.ID}=${id}]`, mml)[0];
        if (!node.childNodes.length) {
            SemanticSkeleton.addAria(mmlChild, level, posinset, setsize);
            return node.id;
        }
        const children = SemanticSkeleton.combineContentChildren(node.type, node.role, node.contentNodes.map(function (x) {
            return x;
        }), node.childNodes.map(function (x) {
            return x;
        }));
        if (mmlChild) {
            SemanticSkeleton.addOwns_(mmlChild, children);
        }
        for (let i = 0, l = children.length, child; (child = children[i]); i++) {
            skeleton.push(SemanticSkeleton.tree_(mml, child, level + 1, i + 1, l));
        }
        SemanticSkeleton.addAria(mmlChild, level, posinset, setsize, !Options.tree ? 'treeitem' : level ? 'group' : 'tree');
        return skeleton;
    }
    static addAria(node, level, posinset, setsize, role = !Options.tree ? 'treeitem' : level ? 'treeitem' : 'tree') {
        if (!engine_js_1.Engine.getInstance().aria || !node) {
            return;
        }
        node.setAttribute('aria-level', level.toString());
        node.setAttribute('aria-posinset', posinset.toString());
        node.setAttribute('aria-setsize', setsize.toString());
        node.setAttribute('role', role);
        if (node.hasAttribute(enrich_attr_js_1.Attribute.OWNS)) {
            node.setAttribute('aria-owns', node.getAttribute(enrich_attr_js_1.Attribute.OWNS));
        }
    }
    static addOwns_(node, children) {
        const collapsed = node.getAttribute(enrich_attr_js_1.Attribute.COLLAPSED);
        const leafs = collapsed
            ? SemanticSkeleton.realLeafs_(SemanticSkeleton.fromString(collapsed).array)
            : children.map((x) => x.id);
        node.setAttribute(enrich_attr_js_1.Attribute.OWNS, leafs.join(' '));
    }
    static realLeafs_(sexp) {
        if (SemanticSkeleton.simpleCollapseStructure(sexp)) {
            return [sexp];
        }
        if (SemanticSkeleton.contentCollapseStructure(sexp)) {
            return [];
        }
        sexp = sexp;
        let result = [];
        for (let i = 1; i < sexp.length; i++) {
            result = result.concat(SemanticSkeleton.realLeafs_(sexp[i]));
        }
        return result;
    }
    constructor(skeleton) {
        this.parents = null;
        this.levelsMap = null;
        skeleton = skeleton === 0 ? skeleton : skeleton || [];
        this.array = skeleton;
    }
    populate() {
        if (this.parents && this.levelsMap) {
            return;
        }
        this.parents = {};
        this.levelsMap = {};
        this.populate_(this.array, this.array, []);
    }
    toString() {
        return SemanticSkeleton.makeSexp_(this.array);
    }
    populate_(element, layer, parents) {
        if (SemanticSkeleton.simpleCollapseStructure(element)) {
            element = element;
            this.levelsMap[element] = layer;
            this.parents[element] =
                element === parents[0] ? parents.slice(1) : parents;
            return;
        }
        const newElement = SemanticSkeleton.contentCollapseStructure(element)
            ? element.slice(1)
            : element;
        const newParents = [newElement[0]].concat(parents);
        for (let i = 0, l = newElement.length; i < l; i++) {
            const current = newElement[i];
            this.populate_(current, element, newParents);
        }
    }
    isRoot(id) {
        const level = this.levelsMap[id];
        return id === level[0];
    }
    directChildren(id) {
        if (!this.isRoot(id)) {
            return [];
        }
        const level = this.levelsMap[id];
        return level.slice(1).map((child) => {
            if (SemanticSkeleton.simpleCollapseStructure(child)) {
                return child;
            }
            if (SemanticSkeleton.contentCollapseStructure(child)) {
                return child[1];
            }
            return child[0];
        });
    }
    subtreeNodes(id) {
        if (!this.isRoot(id)) {
            return [];
        }
        const subtreeNodes_ = (tree, nodes) => {
            if (SemanticSkeleton.simpleCollapseStructure(tree)) {
                nodes.push(tree);
                return;
            }
            tree = tree;
            if (SemanticSkeleton.contentCollapseStructure(tree)) {
                tree = tree.slice(1);
            }
            tree.forEach((x) => subtreeNodes_(x, nodes));
        };
        const level = this.levelsMap[id];
        const subtree = [];
        subtreeNodes_(level.slice(1), subtree);
        return subtree;
    }
}
exports.SemanticSkeleton = SemanticSkeleton;
