export const GENERATION_TREE_ROOT_NAME = "__ROOT__";
export const buildGenerationTree = ({ articles, include = "all", apiUtil, }) => {
    const elementList = new Map();
    /**
     * Recursively adds an element + children and neighbors to the tree
     *
     * @param {string} comId
     * @param {GenerationTreeMatchPointInfo} [snapPoint]
     * @returns
     */
    const addElement = (comId, snapPoint) => {
        // element and children have already been added to the tree. we can skip scanning the subtree again
        if (elementList.has(comId)) {
            return;
        }
        const nodeData = {
            comId: comId,
            children: apiUtil.getDirectChildren(comId),
            snapPoint,
        };
        elementList.set(comId, nodeData);
        // skip snap point and neighbor detection for root element
        if (comId !== GENERATION_TREE_ROOT_NAME) {
            if (snapPoint === undefined) {
                //TODO: use the exact match points and pass them to addElement instead of
                // calulculating the parent again
                nodeData.snapPoint = apiUtil.getDirectParent(comId);
            }
            if (include !== "children") {
                let neighborPoints;
                try {
                    neighborPoints = apiUtil.getNeighbors(comId);
                }
                catch (err) {
                    // no idea why this is erroring
                    console.error(err);
                }
                // add neighbors as children for this element
                if (neighborPoints !== undefined) {
                    for (const entry of neighborPoints) {
                        if (elementList.has(entry.foreignComId)) {
                            continue;
                        }
                        nodeData.children.push(entry.foreignComId);
                        addElement(entry.foreignComId, {
                            foreignComId: comId,
                            ownPointKey: entry.foreignPointKey,
                            foreignPointKey: entry.ownPointKey,
                            type: entry.type,
                        });
                    }
                }
            }
        }
        if (nodeData.children !== undefined) {
            for (const childComId of nodeData.children) {
                addElement(childComId);
            }
        }
    };
    elementList.set(GENERATION_TREE_ROOT_NAME, {
        comId: GENERATION_TREE_ROOT_NAME,
        children: articles,
    });
    const articlesWithPositions = articles.map(comId => ({
        comId,
        ...apiUtil.getScenePosition(comId),
    }));
    articlesWithPositions.sort((a, b) => a.x + a.z - (b.x + b.z));
    articlesWithPositions.forEach(element => addElement(element.comId));
    const rootElement = elementList.get(GENERATION_TREE_ROOT_NAME);
    const roots = new Set(rootElement.children);
    const findNonRoots = (list) => {
        for (const comId of list) {
            if (roots.has(comId)) {
                roots.delete(comId);
            }
            const node = elementList.get(comId);
            if (node.children !== undefined) {
                findNonRoots(node.children);
            }
        }
    };
    // remove elements from root array, which are found somewhere else in the tree
    for (const comId of roots) {
        const node = elementList.get(comId);
        if (node.children !== undefined) {
            findNonRoots(node.children);
        }
    }
    rootElement.children = Array.from(roots);
    return elementList;
};
