'use strict';

var parser = require('../parser.js');
var keys = require('../../grammar/keys.js');
var visitor = require('../../../../../../../../@chevrotain_gast@11.0.3/node_modules/@chevrotain/gast/lib/src/visitor.js');
var helpers = require('../../../../../../../../@chevrotain_gast@11.0.3/node_modules/@chevrotain/gast/lib/src/helpers.js');
var llk_lookahead = require('../../grammar/llk_lookahead.js');
var has = require('../../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/has.js');
var forEach = require('../../../../../../../../lodash-es@4.17.21/node_modules/lodash-es/forEach.js');

/**
 * Trait responsible for the lookahead related utilities and optimizations.
 */
class LooksAhead {
    initLooksAhead(config) {
        this.dynamicTokensEnabled = has(config, "dynamicTokensEnabled")
            ? config.dynamicTokensEnabled // assumes end user provides the correct config value/type
            : parser.DEFAULT_PARSER_CONFIG.dynamicTokensEnabled;
        this.maxLookahead = has(config, "maxLookahead")
            ? config.maxLookahead // assumes end user provides the correct config value/type
            : parser.DEFAULT_PARSER_CONFIG.maxLookahead;
        this.lookaheadStrategy = has(config, "lookaheadStrategy")
            ? config.lookaheadStrategy // assumes end user provides the correct config value/type
            : new llk_lookahead.LLkLookaheadStrategy({ maxLookahead: this.maxLookahead });
        this.lookAheadFuncsCache = new Map();
    }
    preComputeLookaheadFunctions(rules) {
        forEach(rules, (currRule) => {
            this.TRACE_INIT(`${currRule.name} Rule Lookahead`, () => {
                const { alternation, repetition, option, repetitionMandatory, repetitionMandatoryWithSeparator, repetitionWithSeparator, } = collectMethods(currRule);
                forEach(alternation, (currProd) => {
                    const prodIdx = currProd.idx === 0 ? "" : currProd.idx;
                    this.TRACE_INIT(`${helpers.getProductionDslName(currProd)}${prodIdx}`, () => {
                        const laFunc = this.lookaheadStrategy.buildLookaheadForAlternation({
                            prodOccurrence: currProd.idx,
                            rule: currRule,
                            maxLookahead: currProd.maxLookahead || this.maxLookahead,
                            hasPredicates: currProd.hasPredicates,
                            dynamicTokensEnabled: this.dynamicTokensEnabled,
                        });
                        const key = keys.getKeyForAutomaticLookahead(this.fullRuleNameToShort[currRule.name], keys.OR_IDX, currProd.idx);
                        this.setLaFuncCache(key, laFunc);
                    });
                });
                forEach(repetition, (currProd) => {
                    this.computeLookaheadFunc(currRule, currProd.idx, keys.MANY_IDX, "Repetition", currProd.maxLookahead, helpers.getProductionDslName(currProd));
                });
                forEach(option, (currProd) => {
                    this.computeLookaheadFunc(currRule, currProd.idx, keys.OPTION_IDX, "Option", currProd.maxLookahead, helpers.getProductionDslName(currProd));
                });
                forEach(repetitionMandatory, (currProd) => {
                    this.computeLookaheadFunc(currRule, currProd.idx, keys.AT_LEAST_ONE_IDX, "RepetitionMandatory", currProd.maxLookahead, helpers.getProductionDslName(currProd));
                });
                forEach(repetitionMandatoryWithSeparator, (currProd) => {
                    this.computeLookaheadFunc(currRule, currProd.idx, keys.AT_LEAST_ONE_SEP_IDX, "RepetitionMandatoryWithSeparator", currProd.maxLookahead, helpers.getProductionDslName(currProd));
                });
                forEach(repetitionWithSeparator, (currProd) => {
                    this.computeLookaheadFunc(currRule, currProd.idx, keys.MANY_SEP_IDX, "RepetitionWithSeparator", currProd.maxLookahead, helpers.getProductionDslName(currProd));
                });
            });
        });
    }
    computeLookaheadFunc(rule, prodOccurrence, prodKey, prodType, prodMaxLookahead, dslMethodName) {
        this.TRACE_INIT(`${dslMethodName}${prodOccurrence === 0 ? "" : prodOccurrence}`, () => {
            const laFunc = this.lookaheadStrategy.buildLookaheadForOptional({
                prodOccurrence,
                rule,
                maxLookahead: prodMaxLookahead || this.maxLookahead,
                dynamicTokensEnabled: this.dynamicTokensEnabled,
                prodType,
            });
            const key = keys.getKeyForAutomaticLookahead(this.fullRuleNameToShort[rule.name], prodKey, prodOccurrence);
            this.setLaFuncCache(key, laFunc);
        });
    }
    // this actually returns a number, but it is always used as a string (object prop key)
    getKeyForAutomaticLookahead(dslMethodIdx, occurrence) {
        const currRuleShortName = this.getLastExplicitRuleShortName();
        return keys.getKeyForAutomaticLookahead(currRuleShortName, dslMethodIdx, occurrence);
    }
    getLaFuncFromCache(key) {
        return this.lookAheadFuncsCache.get(key);
    }
    /* istanbul ignore next */
    setLaFuncCache(key, value) {
        this.lookAheadFuncsCache.set(key, value);
    }
}
class DslMethodsCollectorVisitor extends visitor.GAstVisitor {
    constructor() {
        super(...arguments);
        this.dslMethods = {
            option: [],
            alternation: [],
            repetition: [],
            repetitionWithSeparator: [],
            repetitionMandatory: [],
            repetitionMandatoryWithSeparator: [],
        };
    }
    reset() {
        this.dslMethods = {
            option: [],
            alternation: [],
            repetition: [],
            repetitionWithSeparator: [],
            repetitionMandatory: [],
            repetitionMandatoryWithSeparator: [],
        };
    }
    visitOption(option) {
        this.dslMethods.option.push(option);
    }
    visitRepetitionWithSeparator(manySep) {
        this.dslMethods.repetitionWithSeparator.push(manySep);
    }
    visitRepetitionMandatory(atLeastOne) {
        this.dslMethods.repetitionMandatory.push(atLeastOne);
    }
    visitRepetitionMandatoryWithSeparator(atLeastOneSep) {
        this.dslMethods.repetitionMandatoryWithSeparator.push(atLeastOneSep);
    }
    visitRepetition(many) {
        this.dslMethods.repetition.push(many);
    }
    visitAlternation(or) {
        this.dslMethods.alternation.push(or);
    }
}
const collectorVisitor = new DslMethodsCollectorVisitor();
function collectMethods(rule) {
    collectorVisitor.reset();
    rule.accept(collectorVisitor);
    const dslMethods = collectorVisitor.dslMethods;
    // avoid uncleaned references
    collectorVisitor.reset();
    return dslMethods;
}

exports.LooksAhead = LooksAhead;
exports.collectMethods = collectMethods;
