/******************************************************************************
 * Copyright 2022 TypeFox GmbH
 * This program and the accompanying materials are made available under the
 * terms of the MIT License, which is available in the project root.
 ******************************************************************************/
import { MultiMap } from '../../utils/collections.js';
import { stream } from '../../utils/stream.js';
import { isAction, isAlternatives, isGroup, isUnorderedGroup } from '../../languages/generated/ast.js';
import { mergeInterfaces, mergeTypesAndInterfaces } from '../type-system/types-util.js';
import { collectValidationAst } from '../type-system/ast-collector.js';
import { getActionType, getRuleTypeName } from '../../utils/grammar-utils.js';
export class LangiumGrammarValidationResourcesCollector {
    constructor(services) {
        this.services = services;
    }
    collectValidationResources(grammar) {
        try {
            const typeResources = collectValidationAst(grammar, this.services);
            return {
                typeToValidationInfo: this.collectValidationInfo(typeResources),
                typeToSuperProperties: this.collectSuperProperties(typeResources),
            };
        }
        catch (err) {
            console.error('Error collecting validation resources', err);
            return { typeToValidationInfo: new Map(), typeToSuperProperties: new Map() };
        }
    }
    collectValidationInfo({ astResources, inferred, declared }) {
        const res = new Map();
        const typeNameToRulesActions = collectNameToRulesActions(astResources);
        for (const type of mergeTypesAndInterfaces(inferred)) {
            res.set(type.name, { inferred: type, inferredNodes: typeNameToRulesActions.get(type.name) });
        }
        const typeNametoInterfacesUnions = stream(astResources.interfaces)
            .concat(astResources.types)
            .reduce((acc, type) => acc.set(type.name, type), new Map());
        for (const type of mergeTypesAndInterfaces(declared)) {
            const node = typeNametoInterfacesUnions.get(type.name);
            if (node) {
                const inferred = res.get(type.name);
                res.set(type.name, { ...inferred ?? {}, declared: type, declaredNode: node });
            }
        }
        return res;
    }
    collectSuperProperties({ inferred, declared }) {
        const typeToSuperProperties = new Map();
        const interfaces = mergeInterfaces(inferred, declared);
        const interfaceMap = new Map(interfaces.map(e => [e.name, e]));
        for (const type of mergeInterfaces(inferred, declared)) {
            typeToSuperProperties.set(type.name, this.addSuperProperties(type, interfaceMap, new Set()));
        }
        return typeToSuperProperties;
    }
    addSuperProperties(interfaceType, map, visited) {
        if (visited.has(interfaceType.name)) {
            return [];
        }
        visited.add(interfaceType.name);
        const properties = [...interfaceType.properties];
        for (const superType of interfaceType.superTypes) {
            const value = map.get(superType.name);
            if (value) {
                properties.push(...this.addSuperProperties(value, map, visited));
            }
        }
        return properties;
    }
}
function collectNameToRulesActions({ parserRules, datatypeRules }) {
    const acc = new MultiMap();
    // collect rules
    stream(parserRules)
        .concat(datatypeRules)
        .forEach(rule => acc.add(getRuleTypeName(rule), rule));
    // collect actions
    function collectActions(element) {
        if (isAction(element)) {
            const name = getActionType(element);
            if (name) {
                acc.add(name, element);
            }
        }
        if (isAlternatives(element) || isGroup(element) || isUnorderedGroup(element)) {
            element.elements.forEach(e => collectActions(e));
        }
    }
    parserRules
        .forEach(rule => collectActions(rule.definition));
    return acc;
}
//# sourceMappingURL=validation-resources-collector.js.map