"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.reportRedeclare = exports.getSymbolAstNode = exports.addSymbol = exports.getAllByType = exports.getAll = exports.get = exports.hooks = exports.diagnostics = exports.readableTypeMap = void 0;
const feature_1 = require("./feature");
const plugable_record_1 = require("../helpers/plugable-record");
const diagnostics_1 = require("../diagnostics");
// the namespace that each symbol exists on
const NAMESPACES = {
    import: `main`,
    class: `main`,
    cssVar: `main`,
    element: `main`,
    keyframes: `keyframes`,
    layer: `layer`,
    container: `container`,
    var: `main`,
};
exports.readableTypeMap = {
    class: 'css class',
    element: 'css element type',
    cssVar: 'css custom property',
    import: 'stylable imported symbol',
    keyframes: 'css keyframes',
    layer: 'css layer',
    container: 'css container name',
    var: 'stylable var',
};
// state structure
function createState(clone) {
    return {
        byNS: {
            main: clone ? [...clone.byNS.main] : [],
            keyframes: clone ? [...clone.byNS.keyframes] : [],
            layer: clone ? [...clone.byNS.layer] : [],
            container: clone ? [...clone.byNS.container] : [],
        },
        byNSFlat: {
            main: clone ? { ...clone.byNSFlat.main } : {},
            keyframes: clone ? { ...clone.byNSFlat.keyframes } : {},
            layer: clone ? { ...clone.byNSFlat.layer } : {},
            container: clone ? { ...clone.byNSFlat.container } : {},
        },
        byType: {
            import: clone ? { ...clone.byType.import } : {},
            class: clone ? { ...clone.byType.class } : {},
            cssVar: clone ? { ...clone.byType.cssVar } : {},
            element: clone ? { ...clone.byType.element } : {},
            keyframes: clone ? { ...clone.byType.keyframes } : {},
            layer: clone ? { ...clone.byType.layer } : {},
            container: clone ? { ...clone.byType.container } : {},
            var: clone ? { ...clone.byType.var } : {},
        },
        symbolToAst: new WeakMap(),
    };
}
const dataKey = plugable_record_1.plugableRecord.key('mappedSymbols');
exports.diagnostics = {
    REDECLARE_SYMBOL: (0, diagnostics_1.createDiagnosticReporter)('06001', 'warning', (name) => `redeclare symbol "${name}"`),
    REDECLARE_ROOT: (0, diagnostics_1.createDiagnosticReporter)('06002', 'error', () => `root is used for the stylesheet and cannot be overridden`),
};
// HOOKS
exports.hooks = (0, feature_1.createFeature)({
    metaInit({ meta }) {
        plugable_record_1.plugableRecord.set(meta.data, dataKey, createState());
    },
});
// API
function get(meta, name, type) {
    const { byNSFlat, byType } = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    return (type ? byType[type][name] : byNSFlat['main'][name]);
}
exports.get = get;
function getAll(meta, ns) {
    const { byNSFlat } = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    return byNSFlat[ns || `main`];
}
exports.getAll = getAll;
function getAllByType(meta, type) {
    const { byType } = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    return byType[type];
}
exports.getAllByType = getAllByType;
function addSymbol({ context, symbol, node, safeRedeclare = false, localName, }) {
    const { byNS, byNSFlat, byType, symbolToAst } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
    const name = localName || symbol.name;
    const typeTable = byType[symbol._kind];
    const nsName = NAMESPACES[symbol._kind];
    if (node && name === `root` && nsName === `main` && byNSFlat[nsName][name]) {
        context.diagnostics.report(exports.diagnostics.REDECLARE_ROOT(), {
            node,
            word: `root`,
        });
        return;
    }
    byNS[nsName].push({ name, symbol, ast: node, safeRedeclare });
    byNSFlat[nsName][name] = symbol;
    typeTable[name] = symbol;
    node && symbolToAst.set(symbol, node);
    return symbol;
}
exports.addSymbol = addSymbol;
function getSymbolAstNode(meta, symbol) {
    const { symbolToAst } = plugable_record_1.plugableRecord.getUnsafe(meta.data, dataKey);
    return symbolToAst.get(symbol);
}
exports.getSymbolAstNode = getSymbolAstNode;
function reportRedeclare(context) {
    const { byNS } = plugable_record_1.plugableRecord.getUnsafe(context.meta.data, dataKey);
    for (const symbols of Object.values(byNS)) {
        const flat = {};
        const collisions = new Set();
        for (const symbolDecl of symbols) {
            const { name, safeRedeclare } = symbolDecl;
            flat[name] = flat[name] || [];
            if (!safeRedeclare && flat[name].length) {
                collisions.add(name);
            }
            flat[name].push(symbolDecl);
        }
        for (const name of collisions) {
            for (const { safeRedeclare, ast } of flat[name]) {
                if (!safeRedeclare && ast) {
                    context.diagnostics.report(exports.diagnostics.REDECLARE_SYMBOL(name), {
                        node: ast,
                        word: name,
                    });
                }
            }
        }
    }
}
exports.reportRedeclare = reportRedeclare;
//# sourceMappingURL=st-symbol.js.map