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 every_default from "../../../../../lodash-es/every.js";
import map_default from "../../../../../lodash-es/map.js";
import has_default from "../../../../../lodash-es/has.js";
import reduce_default from "../../../../../lodash-es/reduce.js";
import { Alternation, Alternative, Option, Repetition, RepetitionMandatory, RepetitionMandatoryWithSeparator, RepetitionWithSeparator } from "../../../../../@chevrotain/gast/lib/src/model.js";
import { GAstVisitor } from "../../../../../@chevrotain/gast/lib/src/visitor.js";
import { RestWalker } from "./rest.js";
import { tokenStructuredMatcher, tokenStructuredMatcherNoCategories } from "../../scan/tokens.js";
import { possiblePathsFrom } from "./interpreter.js";
var PROD_TYPE;
(function(a) {
	a[a.OPTION = 0] = "OPTION", a[a.REPETITION = 1] = "REPETITION", a[a.REPETITION_MANDATORY = 2] = "REPETITION_MANDATORY", a[a.REPETITION_MANDATORY_WITH_SEPARATOR = 3] = "REPETITION_MANDATORY_WITH_SEPARATOR", a[a.REPETITION_WITH_SEPARATOR = 4] = "REPETITION_WITH_SEPARATOR", a[a.ALTERNATION = 5] = "ALTERNATION";
})(PROD_TYPE ||= {});
function getProdType(a) {
	/* istanbul ignore else */
	if (a instanceof Option || a === "Option") return PROD_TYPE.OPTION;
	if (a instanceof Repetition || a === "Repetition") return PROD_TYPE.REPETITION;
	if (a instanceof RepetitionMandatory || a === "RepetitionMandatory") return PROD_TYPE.REPETITION_MANDATORY;
	if (a instanceof RepetitionMandatoryWithSeparator || a === "RepetitionMandatoryWithSeparator") return PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR;
	if (a instanceof RepetitionWithSeparator || a === "RepetitionWithSeparator") return PROD_TYPE.REPETITION_WITH_SEPARATOR;
	if (a instanceof Alternation || a === "Alternation") return PROD_TYPE.ALTERNATION;
	throw Error("non exhaustive match");
}
function getLookaheadPaths(a) {
	let { occurrence: T, rule: E, prodType: D, maxLookahead: O } = a, k = getProdType(D);
	return k === PROD_TYPE.ALTERNATION ? getLookaheadPathsForOr(T, E, O) : getLookaheadPathsForOptionalProd(T, E, k, O);
}
function buildLookaheadFuncForOr(a, T, E, D, O, k) {
	let A = getLookaheadPathsForOr(a, T, E);
	return k(A, D, areTokenCategoriesNotUsed(A) ? tokenStructuredMatcherNoCategories : tokenStructuredMatcher, O);
}
function buildLookaheadFuncForOptionalProd(a, T, E, D, O, k) {
	let A = getLookaheadPathsForOptionalProd(a, T, O, E), j = areTokenCategoriesNotUsed(A) ? tokenStructuredMatcherNoCategories : tokenStructuredMatcher;
	return k(A[0], j, D);
}
function buildAlternativesLookAheadFunc(a, j, M, N) {
	let P = a.length, F = every_default(a, (a) => every_default(a, (a) => a.length === 1));
	if (j) return function(T) {
		let E = map_default(T, (a) => a.GATE);
		for (let T = 0; T < P; T++) {
			let D = a[T], O = D.length, k = E[T];
			if (!(k !== void 0 && k.call(this) === !1)) nextPath: for (let a = 0; a < O; a++) {
				let E = D[a], O = E.length;
				for (let a = 0; a < O; a++) if (M(this.LA(a + 1), E[a]) === !1) continue nextPath;
				return T;
			}
		}
	};
	if (F && !N) {
		let D = reduce_default(map_default(a, (a) => flatten_default(a)), (a, T, D) => (forEach_default(T, (T) => {
			has_default(a, T.tokenTypeIdx) || (a[T.tokenTypeIdx] = D), forEach_default(T.categoryMatches, (T) => {
				has_default(a, T) || (a[T] = D);
			});
		}), a), {});
		return function() {
			return D[this.LA(1).tokenTypeIdx];
		};
	} else return function() {
		for (let T = 0; T < P; T++) {
			let E = a[T], D = E.length;
			nextPath: for (let a = 0; a < D; a++) {
				let D = E[a], O = D.length;
				for (let a = 0; a < O; a++) if (M(this.LA(a + 1), D[a]) === !1) continue nextPath;
				return T;
			}
		}
	};
}
function buildSingleAlternativeLookaheadFunction(O, k, j) {
	let M = every_default(O, (a) => a.length === 1), N = O.length;
	if (M && !j) {
		let D = flatten_default(O);
		if (D.length === 1 && isEmpty_default(D[0].categoryMatches)) {
			let a = D[0].tokenTypeIdx;
			return function() {
				return this.LA(1).tokenTypeIdx === a;
			};
		} else {
			let a = reduce_default(D, (a, T, D) => (a[T.tokenTypeIdx] = !0, forEach_default(T.categoryMatches, (T) => {
				a[T] = !0;
			}), a), []);
			return function() {
				return a[this.LA(1).tokenTypeIdx] === !0;
			};
		}
	} else return function() {
		nextPath: for (let a = 0; a < N; a++) {
			let T = O[a], E = T.length;
			for (let a = 0; a < E; a++) if (k(this.LA(a + 1), T[a]) === !1) continue nextPath;
			return !0;
		}
		return !1;
	};
}
var RestDefinitionFinderWalker = class extends RestWalker {
	constructor(a, T, E) {
		super(), this.topProd = a, this.targetOccurrence = T, this.targetProdType = E;
	}
	startWalking() {
		return this.walk(this.topProd), this.restDef;
	}
	checkIsTarget(a, T, E, D) {
		return a.idx === this.targetOccurrence && this.targetProdType === T ? (this.restDef = E.concat(D), !0) : !1;
	}
	walkOption(a, T, E) {
		this.checkIsTarget(a, PROD_TYPE.OPTION, T, E) || super.walkOption(a, T, E);
	}
	walkAtLeastOne(a, T, E) {
		this.checkIsTarget(a, PROD_TYPE.REPETITION_MANDATORY, T, E) || super.walkOption(a, T, E);
	}
	walkAtLeastOneSep(a, T, E) {
		this.checkIsTarget(a, PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR, T, E) || super.walkOption(a, T, E);
	}
	walkMany(a, T, E) {
		this.checkIsTarget(a, PROD_TYPE.REPETITION, T, E) || super.walkOption(a, T, E);
	}
	walkManySep(a, T, E) {
		this.checkIsTarget(a, PROD_TYPE.REPETITION_WITH_SEPARATOR, T, E) || super.walkOption(a, T, E);
	}
}, InsideDefinitionFinderVisitor = class extends GAstVisitor {
	constructor(a, T, E) {
		super(), this.targetOccurrence = a, this.targetProdType = T, this.targetRef = E, this.result = [];
	}
	checkIsTarget(a, T) {
		a.idx === this.targetOccurrence && this.targetProdType === T && (this.targetRef === void 0 || a === this.targetRef) && (this.result = a.definition);
	}
	visitOption(a) {
		this.checkIsTarget(a, PROD_TYPE.OPTION);
	}
	visitRepetition(a) {
		this.checkIsTarget(a, PROD_TYPE.REPETITION);
	}
	visitRepetitionMandatory(a) {
		this.checkIsTarget(a, PROD_TYPE.REPETITION_MANDATORY);
	}
	visitRepetitionMandatoryWithSeparator(a) {
		this.checkIsTarget(a, PROD_TYPE.REPETITION_MANDATORY_WITH_SEPARATOR);
	}
	visitRepetitionWithSeparator(a) {
		this.checkIsTarget(a, PROD_TYPE.REPETITION_WITH_SEPARATOR);
	}
	visitAlternation(a) {
		this.checkIsTarget(a, PROD_TYPE.ALTERNATION);
	}
};
function initializeArrayOfArrays(a) {
	let T = Array(a);
	for (let E = 0; E < a; E++) T[E] = [];
	return T;
}
function pathToHashKeys(a) {
	let T = [""];
	for (let E = 0; E < a.length; E++) {
		let D = a[E], O = [];
		for (let a = 0; a < T.length; a++) {
			let E = T[a];
			O.push(E + "_" + D.tokenTypeIdx);
			for (let a = 0; a < D.categoryMatches.length; a++) {
				let T = "_" + D.categoryMatches[a];
				O.push(E + T);
			}
		}
		T = O;
	}
	return T;
}
function isUniquePrefixHash(a, T, E) {
	for (let D = 0; D < a.length; D++) {
		if (D === E) continue;
		let O = a[D];
		for (let a = 0; a < T.length; a++) if (O[T[a]] === !0) return !1;
	}
	return !0;
}
function lookAheadSequenceFromAlternatives(T, D) {
	let k = map_default(T, (a) => possiblePathsFrom([a], 1)), A = initializeArrayOfArrays(k.length), j = map_default(k, (a) => {
		let T = {};
		return forEach_default(a, (a) => {
			forEach_default(pathToHashKeys(a.partialPath), (a) => {
				T[a] = !0;
			});
		}), T;
	}), M = k;
	for (let T = 1; T <= D; T++) {
		let O = M;
		M = initializeArrayOfArrays(O.length);
		for (let k = 0; k < O.length; k++) {
			let N = O[k];
			for (let O = 0; O < N.length; O++) {
				let P = N[O].partialPath, F = N[O].suffixDef, I = pathToHashKeys(P);
				if (isUniquePrefixHash(j, I, k) || isEmpty_default(F) || P.length === D) {
					let a = A[k];
					if (containsPath(a, P) === !1) {
						a.push(P);
						for (let a = 0; a < I.length; a++) {
							let T = I[a];
							j[k][T] = !0;
						}
					}
				} else {
					let a = possiblePathsFrom(F, T + 1, P);
					M[k] = M[k].concat(a), forEach_default(a, (a) => {
						forEach_default(pathToHashKeys(a.partialPath), (a) => {
							j[k][a] = !0;
						});
					});
				}
			}
		}
	}
	return A;
}
function getLookaheadPathsForOr(a, T, E, D) {
	let O = new InsideDefinitionFinderVisitor(a, PROD_TYPE.ALTERNATION, D);
	return T.accept(O), lookAheadSequenceFromAlternatives(O.result, E);
}
function getLookaheadPathsForOptionalProd(a, T, E, D) {
	let O = new InsideDefinitionFinderVisitor(a, E);
	T.accept(O);
	let k = O.result, A = new RestDefinitionFinderWalker(T, a, E).startWalking();
	return lookAheadSequenceFromAlternatives([new Alternative({ definition: k }), new Alternative({ definition: A })], D);
}
function containsPath(a, T) {
	compareOtherPath: for (let E = 0; E < a.length; E++) {
		let D = a[E];
		if (D.length === T.length) {
			for (let a = 0; a < D.length; a++) {
				let E = T[a], O = D[a];
				if (!(E === O || O.categoryMatchesMap[E.tokenTypeIdx] !== void 0)) continue compareOtherPath;
			}
			return !0;
		}
	}
	return !1;
}
function isStrictPrefixOfPath(a, T) {
	return a.length < T.length && every_default(a, (a, E) => {
		let D = T[E];
		return a === D || D.categoryMatchesMap[a.tokenTypeIdx];
	});
}
function areTokenCategoriesNotUsed(T) {
	return every_default(T, (T) => every_default(T, (T) => every_default(T, (T) => isEmpty_default(T.categoryMatches))));
}
export { PROD_TYPE, buildAlternativesLookAheadFunc, buildLookaheadFuncForOptionalProd, buildLookaheadFuncForOr, buildSingleAlternativeLookaheadFunction, containsPath, getLookaheadPaths, getLookaheadPathsForOptionalProd, getLookaheadPathsForOr, getProdType, isStrictPrefixOfPath };
