export const createArticleState = () => {
    const articles = new Map();
    const gfxComIdMap = new Map();
    const lockArticle = (article) => {
        Object.freeze(article);
        Object.freeze(article.configuration);
        Object.freeze(article.children);
        Object.freeze(article.properties);
        Object.freeze(article.info);
        Object.freeze(article.data);
    };
    const getComIdByGfxId = (gfxId) => gfxComIdMap.get(gfxId);
    const getArticleByComId = (comId) => articles.get(comId);
    const getArticleByGfxId = (gfxId) => {
        const comId = getComIdByGfxId(gfxId);
        if (comId === undefined) {
            return undefined;
        }
        return articles.get(comId);
    };
    const getAllArticles = () => Array.from(articles.keys());
    const addArticle = (article) => {
        if (articles.has(article.comId)) {
            return;
        }
        lockArticle(article);
        articles.set(article.comId, article);
        if (article.parent !== undefined) {
            addToParent(article.comId, article.parent);
        }
        if (article.gfxId !== undefined) {
            addGfxMapping(article.comId, article.gfxId);
        }
        if (article.children !== undefined) {
            updateParents(article.comId, article.children);
        }
    };
    const removeArticle = (comId) => {
        if (!articles.has(comId)) {
            return;
        }
        const article = articles.get(comId);
        if (article.gfxId !== undefined) {
            removeGfxMapping(article.gfxId);
        }
        if (article.parent !== undefined) {
            removeFromParent(comId, article.parent);
        }
        articles.delete(comId);
    };
    const updateArticleProperty = (comId, key, value) => updateArticleProperties(comId, { [key]: value });
    const updateArticleProperties = (comId, updates) => {
        const article = getArticleByComId(comId);
        if (article === undefined) {
            throw new Error(`Can not update unknown article ${comId}`);
        }
        const articleClone = {
            ...article,
        };
        for (const property in updates) {
            if (property === "gfxId") {
                updateGfxMapping(comId, updates.gfxId, article.gfxId);
            }
            else if (property === "parent") {
                updateParent(comId, updates.parent, article.parent);
            }
            else if (property === "children") {
                //TODO:
                // updateParents(comId, updates.children);
            }
            articleClone[property] = updates[property];
        }
        lockArticle(articleClone);
        articles.set(comId, articleClone);
    };
    const updateParents = (parentId, children) => {
        if (children === undefined) {
            return;
        }
        for (const comId of children) {
            addToParent(comId, parentId);
        }
    };
    const updateParent = (comId, newParent, oldParent) => {
        if (oldParent === newParent) {
            return;
        }
        if (newParent !== undefined) {
            addToParent(comId, newParent);
        }
        if (oldParent !== undefined) {
            removeFromParent(comId, oldParent);
        }
    };
    const updateGfxMapping = (comId, newGfxId, oldGfxId) => {
        if (newGfxId === oldGfxId) {
            return;
        }
        if (newGfxId !== undefined) {
            addGfxMapping(comId, newGfxId);
        }
        if (oldGfxId !== undefined) {
            removeGfxMapping(oldGfxId);
        }
    };
    const addGfxMapping = (comId, gfxId) => gfxComIdMap.set(gfxId, comId);
    const removeGfxMapping = (gfxId) => gfxComIdMap.delete(gfxId);
    const addToParent = (comId, parentId) => {
        const parent = articles.get(parentId);
        if (parent === undefined) {
            throw new Error(`Can not add "${comId}" to unknown parent "${parentId}"`);
        }
        if (parent.children.indexOf(comId) > -1) {
            return;
        }
        const newChildren = parent.children.slice(0);
        newChildren.push(comId);
        updateArticleProperty(parentId, "children", newChildren);
    };
    const removeFromParent = (comId, parentId) => {
        const parent = articles.get(parentId);
        if (parent === undefined) {
            return;
        }
        const parentIndex = parent.children.indexOf(comId);
        if (parentIndex === -1) {
            return;
        }
        const newChildren = parent.children.slice(0);
        newChildren.splice(parentIndex, 1);
        updateArticleProperty(parentId, "children", newChildren);
    };
    return {
        addArticle,
        removeArticle,
        getArticleByComId,
        getArticleByGfxId,
        getComIdByGfxId,
        getAllComIds: getAllArticles,
        updateArticleProperties,
        updateArticleProperty,
    };
};
