import isEmpty_default from "../../lodash-es/isEmpty.js";
import flatten_default from "../../lodash-es/flatten.js";
import forEach_default from "../../lodash-es/forEach.js";
import map_default from "../../lodash-es/map.js";
import flatMap_default from "../../lodash-es/flatMap.js";
import min_default from "../../lodash-es/min.js";
import reduce_default from "../../lodash-es/reduce.js";
import uniqBy_default from "../../lodash-es/uniqBy.js";
import { Alternation, NonTerminal, Option, Repetition, RepetitionMandatory, RepetitionMandatoryWithSeparator, RepetitionWithSeparator, Terminal } from "../../@chevrotain/gast/lib/src/model.js";
import { tokenLabel, tokenMatcher } from "../../chevrotain/lib/src/scan/tokens_public.js";
import { getLookaheadPaths } from "../../chevrotain/lib/src/parse/grammar/lookahead.js";
import { LLkLookaheadStrategy } from "../../chevrotain/lib/src/parse/grammar/llk_lookahead.js";
import "../../chevrotain/lib/src/api.js";
import { ATN_RULE_STOP, AtomTransition, EpsilonTransition, RuleTransition, buildATNKey, createATN } from "./atn.js";
import { ATNConfigSet, DFA_ERROR, getATNConfigKey } from "./dfa.js";
function createDFACache(d, B) {
	let V = {};
	return (H) => {
		let U = H.toString(), W = V[U];
		return W === void 0 ? (W = {
			atnStartState: d,
			decision: B,
			states: {}
		}, V[U] = W, W) : W;
	};
}
var PredicateSet = class {
	constructor() {
		this.predicates = [];
	}
	is(d) {
		return d >= this.predicates.length || this.predicates[d];
	}
	set(d, B) {
		this.predicates[d] = B;
	}
	toString() {
		let d = "", B = this.predicates.length;
		for (let V = 0; V < B; V++) d += this.predicates[V] === !0 ? "1" : "0";
		return d;
	}
}, EMPTY_PREDICATES = new PredicateSet(), LLStarLookaheadStrategy = class extends LLkLookaheadStrategy {
	constructor(d) {
		super(), this.logging = d?.logging ?? ((d) => console.log(d));
	}
	initialize(d) {
		this.atn = createATN(d.rules), this.dfas = initATNSimulator(this.atn);
	}
	validateAmbiguousAlternationAlternatives() {
		return [];
	}
	validateEmptyOrAlternatives() {
		return [];
	}
	buildLookaheadForAlternation(d) {
		let { prodOccurrence: B, rule: U, hasPredicates: W, dynamicTokensEnabled: K } = d, q = this.dfas, J = this.logging, Y = buildATNKey(U, "Alternation", B), X = this.atn.decisionMap[Y].decision, Z = map_default(getLookaheadPaths({
			maxLookahead: 1,
			occurrence: B,
			prodType: "Alternation",
			rule: U
		}), (d) => map_default(d, (d) => d[0]));
		if (isLL1Sequence(Z, !1) && !K) {
			let d = reduce_default(Z, (d, B, H) => (forEach_default(B, (B) => {
				B && (d[B.tokenTypeIdx] = H, forEach_default(B.categoryMatches, (B) => {
					d[B] = H;
				}));
			}), d), {});
			return W ? function(B) {
				let V = d[this.LA(1).tokenTypeIdx];
				if (B !== void 0 && V !== void 0) {
					let d = B[V]?.GATE;
					if (d !== void 0 && d.call(this) === !1) return;
				}
				return V;
			} : function() {
				return d[this.LA(1).tokenTypeIdx];
			};
		} else if (W) return function(d) {
			let B = new PredicateSet(), V = d === void 0 ? 0 : d.length;
			for (let H = 0; H < V; H++) {
				let V = d?.[H].GATE;
				B.set(H, V === void 0 || V.call(this));
			}
			let H = adaptivePredict.call(this, q, X, B, J);
			return typeof H == "number" ? H : void 0;
		};
		else return function() {
			let d = adaptivePredict.call(this, q, X, EMPTY_PREDICATES, J);
			return typeof d == "number" ? d : void 0;
		};
	}
	buildLookaheadForOptional(U) {
		let { prodOccurrence: W, rule: K, prodType: q, dynamicTokensEnabled: J } = U, Y = this.dfas, X = this.logging, Z = buildATNKey(K, q, W), Q = this.atn.decisionMap[Z].decision, $ = map_default(getLookaheadPaths({
			maxLookahead: 1,
			occurrence: W,
			prodType: q,
			rule: K
		}), (d) => map_default(d, (d) => d[0]));
		if (isLL1Sequence($) && $[0][0] && !J) {
			let H = $[0], U = flatten_default(H);
			if (U.length === 1 && isEmpty_default(U[0].categoryMatches)) {
				let d = U[0].tokenTypeIdx;
				return function() {
					return this.LA(1).tokenTypeIdx === d;
				};
			} else {
				let d = reduce_default(U, (d, B) => (B !== void 0 && (d[B.tokenTypeIdx] = !0, forEach_default(B.categoryMatches, (B) => {
					d[B] = !0;
				})), d), {});
				return function() {
					return d[this.LA(1).tokenTypeIdx] === !0;
				};
			}
		}
		return function() {
			let d = adaptivePredict.call(this, Y, Q, EMPTY_PREDICATES, X);
			return typeof d == "object" ? !1 : d === 0;
		};
	}
};
function isLL1Sequence(d, B = !0) {
	let V = /* @__PURE__ */ new Set();
	for (let H of d) {
		let d = /* @__PURE__ */ new Set();
		for (let U of H) {
			if (U === void 0) {
				if (B) break;
				return !1;
			}
			let H = [U.tokenTypeIdx].concat(U.categoryMatches);
			for (let B of H) if (V.has(B)) {
				if (!d.has(B)) return !1;
			} else V.add(B), d.add(B);
		}
	}
	return !0;
}
function initATNSimulator(d) {
	let B = d.decisionStates.length, V = Array(B);
	for (let H = 0; H < B; H++) V[H] = createDFACache(d.decisionStates[H], H);
	return V;
}
function adaptivePredict(d, B, V, H) {
	let U = d[B](V), W = U.start;
	return W === void 0 && (W = addDFAState(U, newDFAState(computeStartState(U.atnStartState))), U.start = W), performLookahead.apply(this, [
		U,
		W,
		V,
		H
	]);
}
function performLookahead(d, B, V, H) {
	let U = B, W = 1, G = [], K = this.LA(W++);
	for (;;) {
		let B = getExistingTargetState(U, K);
		if (B === void 0 && (B = computeLookaheadTarget.apply(this, [
			d,
			U,
			K,
			W,
			V,
			H
		])), B === DFA_ERROR) return buildAdaptivePredictError(G, U, K);
		if (B.isAcceptState === !0) return B.prediction;
		U = B, G.push(K), K = this.LA(W++);
	}
}
function computeLookaheadTarget(d, B, V, H, U, G) {
	let K = computeReachSet(B.configs, V, U);
	if (K.size === 0) return addDFAEdge(d, B, V, DFA_ERROR), DFA_ERROR;
	let q = newDFAState(K), J = getUniqueAlt(K, U);
	if (J !== void 0) q.isAcceptState = !0, q.prediction = J, q.configs.uniqueAlt = J;
	else if (hasConflictTerminatingPrediction(K)) {
		let B = min_default(K.alts);
		q.isAcceptState = !0, q.prediction = B, q.configs.uniqueAlt = B, reportLookaheadAmbiguity.apply(this, [
			d,
			H,
			K.alts,
			G
		]);
	}
	return q = addDFAEdge(d, B, V, q), q;
}
function reportLookaheadAmbiguity(d, B, V, H) {
	let U = [];
	for (let d = 1; d <= B; d++) U.push(this.LA(d).tokenType);
	let W = d.atnStartState, G = W.rule, K = W.production;
	H(buildAmbiguityError({
		topLevelRule: G,
		ambiguityIndices: V,
		production: K,
		prefixPath: U
	}));
}
function buildAmbiguityError(d) {
	let B = map_default(d.prefixPath, (d) => tokenLabel(d)).join(", "), V = d.production.idx === 0 ? "" : d.production.idx, U = `Ambiguous Alternatives Detected: <${d.ambiguityIndices.join(", ")}> in <${getProductionDslName(d.production)}${V}> inside <${d.topLevelRule.name}> Rule,\n<${B}> may appears as a prefix path in all these alternatives.\n`;
	return U += "See: https://chevrotain.io/docs/guide/resolving_grammar_errors.html#AMBIGUOUS_ALTERNATIVES\nFor Further details.", U;
}
function getProductionDslName(d) {
	if (d instanceof NonTerminal) return "SUBRULE";
	if (d instanceof Option) return "OPTION";
	if (d instanceof Alternation) return "OR";
	if (d instanceof RepetitionMandatory) return "AT_LEAST_ONE";
	if (d instanceof RepetitionMandatoryWithSeparator) return "AT_LEAST_ONE_SEP";
	if (d instanceof RepetitionWithSeparator) return "MANY_SEP";
	if (d instanceof Repetition) return "MANY";
	if (d instanceof Terminal) return "CONSUME";
	throw Error("non exhaustive match");
}
function buildAdaptivePredictError(d, B, V) {
	return {
		actualToken: V,
		possibleTokenTypes: uniqBy_default(flatMap_default(B.configs.elements, (d) => d.state.transitions).filter((d) => d instanceof AtomTransition).map((d) => d.tokenType), (d) => d.tokenTypeIdx),
		tokenPath: d
	};
}
function getExistingTargetState(d, B) {
	return d.edges[B.tokenTypeIdx];
}
function computeReachSet(d, B, V) {
	let H = new ATNConfigSet(), U = [];
	for (let W of d.elements) {
		if (V.is(W.alt) === !1) continue;
		if (W.state.type === 7) {
			U.push(W);
			continue;
		}
		let d = W.state.transitions.length;
		for (let V = 0; V < d; V++) {
			let d = W.state.transitions[V], U = getReachableTarget(d, B);
			U !== void 0 && H.add({
				state: U,
				alt: W.alt,
				stack: W.stack
			});
		}
	}
	let W;
	if (U.length === 0 && H.size === 1 && (W = H), W === void 0) {
		W = new ATNConfigSet();
		for (let d of H.elements) closure(d, W);
	}
	if (U.length > 0 && !hasConfigInRuleStopState(W)) for (let d of U) W.add(d);
	return W;
}
function getReachableTarget(d, B) {
	if (d instanceof AtomTransition && tokenMatcher(B, d.tokenType)) return d.target;
}
function getUniqueAlt(d, B) {
	let V;
	for (let H of d.elements) if (B.is(H.alt) === !0) {
		if (V === void 0) V = H.alt;
		else if (V !== H.alt) return;
	}
	return V;
}
function newDFAState(d) {
	return {
		configs: d,
		edges: {},
		isAcceptState: !1,
		prediction: -1
	};
}
function addDFAEdge(d, B, V, H) {
	return H = addDFAState(d, H), B.edges[V.tokenTypeIdx] = H, H;
}
function addDFAState(d, B) {
	if (B === DFA_ERROR) return B;
	let V = B.configs.key, H = d.states[V];
	return H === void 0 ? (B.configs.finalize(), d.states[V] = B, B) : H;
}
function computeStartState(d) {
	let B = new ATNConfigSet(), V = d.transitions.length;
	for (let H = 0; H < V; H++) closure({
		state: d.transitions[H].target,
		alt: H,
		stack: []
	}, B);
	return B;
}
function closure(d, B) {
	let V = d.state;
	if (V.type === 7) {
		if (d.stack.length > 0) {
			let V = [...d.stack];
			closure({
				state: V.pop(),
				alt: d.alt,
				stack: V
			}, B);
		} else B.add(d);
		return;
	}
	V.epsilonOnlyTransitions || B.add(d);
	let H = V.transitions.length;
	for (let U = 0; U < H; U++) {
		let H = V.transitions[U], W = getEpsilonTarget(d, H);
		W !== void 0 && closure(W, B);
	}
}
function getEpsilonTarget(d, B) {
	if (B instanceof EpsilonTransition) return {
		state: B.target,
		alt: d.alt,
		stack: d.stack
	};
	if (B instanceof RuleTransition) {
		let V = [...d.stack, B.followState];
		return {
			state: B.target,
			alt: d.alt,
			stack: V
		};
	}
}
function hasConfigInRuleStopState(d) {
	for (let B of d.elements) if (B.state.type === 7) return !0;
	return !1;
}
function allConfigsInRuleStopStates(d) {
	for (let B of d.elements) if (B.state.type !== 7) return !1;
	return !0;
}
function hasConflictTerminatingPrediction(d) {
	if (allConfigsInRuleStopStates(d)) return !0;
	let B = getConflictingAltSets(d.elements);
	return hasConflictingAltSet(B) && !hasStateAssociatedWithOneAlt(B);
}
function getConflictingAltSets(d) {
	let B = /* @__PURE__ */ new Map();
	for (let V of d) {
		let d = getATNConfigKey(V, !1), H = B.get(d);
		H === void 0 && (H = {}, B.set(d, H)), H[V.alt] = !0;
	}
	return B;
}
function hasConflictingAltSet(d) {
	for (let B of Array.from(d.values())) if (Object.keys(B).length > 1) return !0;
	return !1;
}
function hasStateAssociatedWithOneAlt(d) {
	for (let B of Array.from(d.values())) if (Object.keys(B).length === 1) return !0;
	return !1;
}
export { LLStarLookaheadStrategy };
