import forEach from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/forEach.mjs';
import clone from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/clone.mjs';
import compact from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/compact.mjs';
import flatten from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/flatten.mjs';
import map from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/map.mjs';
import difference from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/difference.mjs';
import isEmpty from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/isEmpty.mjs';
import isArray from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/isArray.mjs';
import has from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/has.mjs';
import includes from '../../../../../../lodash-es@4.17.21/node_modules/lodash-es/includes.mjs';

function tokenStructuredMatcher(tokInstance, tokConstructor) {
    const instanceType = tokInstance.tokenTypeIdx;
    if (instanceType === tokConstructor.tokenTypeIdx) {
        return true;
    }
    else {
        return (tokConstructor.isParent === true &&
            tokConstructor.categoryMatchesMap[instanceType] === true);
    }
}
// Optimized tokenMatcher in case our grammar does not use token categories
// Being so tiny it is much more likely to be in-lined and this avoid the function call overhead
function tokenStructuredMatcherNoCategories(token, tokType) {
    return token.tokenTypeIdx === tokType.tokenTypeIdx;
}
let tokenShortNameIdx = 1;
const tokenIdxToClass = {};
function augmentTokenTypes(tokenTypes) {
    // collect the parent Token Types as well.
    const tokenTypesAndParents = expandCategories(tokenTypes);
    // add required tokenType and categoryMatches properties
    assignTokenDefaultProps(tokenTypesAndParents);
    // fill up the categoryMatches
    assignCategoriesMapProp(tokenTypesAndParents);
    assignCategoriesTokensProp(tokenTypesAndParents);
    forEach(tokenTypesAndParents, (tokType) => {
        tokType.isParent = tokType.categoryMatches.length > 0;
    });
}
function expandCategories(tokenTypes) {
    let result = clone(tokenTypes);
    let categories = tokenTypes;
    let searching = true;
    while (searching) {
        categories = compact(flatten(map(categories, (currTokType) => currTokType.CATEGORIES)));
        const newCategories = difference(categories, result);
        result = result.concat(newCategories);
        if (isEmpty(newCategories)) {
            searching = false;
        }
        else {
            categories = newCategories;
        }
    }
    return result;
}
function assignTokenDefaultProps(tokenTypes) {
    forEach(tokenTypes, (currTokType) => {
        if (!hasShortKeyProperty(currTokType)) {
            tokenIdxToClass[tokenShortNameIdx] = currTokType;
            currTokType.tokenTypeIdx = tokenShortNameIdx++;
        }
        // CATEGORIES? : TokenType | TokenType[]
        if (hasCategoriesProperty(currTokType) &&
            !isArray(currTokType.CATEGORIES)
        // &&
        // !isUndefined(currTokType.CATEGORIES.PATTERN)
        ) {
            currTokType.CATEGORIES = [currTokType.CATEGORIES];
        }
        if (!hasCategoriesProperty(currTokType)) {
            currTokType.CATEGORIES = [];
        }
        if (!hasExtendingTokensTypesProperty(currTokType)) {
            currTokType.categoryMatches = [];
        }
        if (!hasExtendingTokensTypesMapProperty(currTokType)) {
            currTokType.categoryMatchesMap = {};
        }
    });
}
function assignCategoriesTokensProp(tokenTypes) {
    forEach(tokenTypes, (currTokType) => {
        // avoid duplications
        currTokType.categoryMatches = [];
        forEach(currTokType.categoryMatchesMap, (val, key) => {
            currTokType.categoryMatches.push(tokenIdxToClass[key].tokenTypeIdx);
        });
    });
}
function assignCategoriesMapProp(tokenTypes) {
    forEach(tokenTypes, (currTokType) => {
        singleAssignCategoriesToksMap([], currTokType);
    });
}
function singleAssignCategoriesToksMap(path, nextNode) {
    forEach(path, (pathNode) => {
        nextNode.categoryMatchesMap[pathNode.tokenTypeIdx] = true;
    });
    forEach(nextNode.CATEGORIES, (nextCategory) => {
        const newPath = path.concat(nextNode);
        // avoids infinite loops due to cyclic categories.
        if (!includes(newPath, nextCategory)) {
            singleAssignCategoriesToksMap(newPath, nextCategory);
        }
    });
}
function hasShortKeyProperty(tokType) {
    return has(tokType, "tokenTypeIdx");
}
function hasCategoriesProperty(tokType) {
    return has(tokType, "CATEGORIES");
}
function hasExtendingTokensTypesProperty(tokType) {
    return has(tokType, "categoryMatches");
}
function hasExtendingTokensTypesMapProperty(tokType) {
    return has(tokType, "categoryMatchesMap");
}
function isTokenType(tokType) {
    return has(tokType, "tokenTypeIdx");
}

export { assignCategoriesMapProp, assignCategoriesTokensProp, assignTokenDefaultProps, augmentTokenTypes, expandCategories, hasCategoriesProperty, hasExtendingTokensTypesMapProperty, hasExtendingTokensTypesProperty, hasShortKeyProperty, isTokenType, singleAssignCategoriesToksMap, tokenIdxToClass, tokenShortNameIdx, tokenStructuredMatcher, tokenStructuredMatcherNoCategories };
