import isEmpty_default from "../../../../../lodash-es/isEmpty.js";
import clone_default from "../../../../../lodash-es/clone.js";
import last_default from "../../../../../lodash-es/last.js";
import drop_default from "../../../../../lodash-es/drop.js";
import dropRight_default from "../../../../../lodash-es/dropRight.js";
import forEach_default from "../../../../../lodash-es/forEach.js";
import head_default from "../../../../../lodash-es/head.js";
import { Alternation, Alternative, NonTerminal, Option, Repetition, RepetitionMandatory, RepetitionMandatoryWithSeparator, RepetitionWithSeparator, Rule, Terminal } from "../../../../../@chevrotain/gast/lib/src/model.js";
import { RestWalker } from "./rest.js";
import { first } from "./first.js";
var AbstractNextPossibleTokensWalker = class extends RestWalker {
	constructor(e, d) {
		super(), this.topProd = e, this.path = d, this.possibleTokTypes = [], this.nextProductionName = "", this.nextProductionOccurrence = 0, this.found = !1, this.isAtEndOfPath = !1;
	}
	startWalking() {
		if (this.found = !1, this.path.ruleStack[0] !== this.topProd.name) throw Error("The path does not start with the walker's top Rule!");
		return this.ruleStack = clone_default(this.path.ruleStack).reverse(), this.occurrenceStack = clone_default(this.path.occurrenceStack).reverse(), this.ruleStack.pop(), this.occurrenceStack.pop(), this.updateExpectedNext(), this.walk(this.topProd), this.possibleTokTypes;
	}
	walk(e, d = []) {
		this.found || super.walk(e, d);
	}
	walkProdRef(e, d, f) {
		if (e.referencedRule.name === this.nextProductionName && e.idx === this.nextProductionOccurrence) {
			let p = d.concat(f);
			this.updateExpectedNext(), this.walk(e.referencedRule, p);
		}
	}
	updateExpectedNext() {
		isEmpty_default(this.ruleStack) ? (this.nextProductionName = "", this.nextProductionOccurrence = 0, this.isAtEndOfPath = !0) : (this.nextProductionName = this.ruleStack.pop(), this.nextProductionOccurrence = this.occurrenceStack.pop());
	}
}, NextAfterTokenWalker = class extends AbstractNextPossibleTokensWalker {
	constructor(e, d) {
		super(e, d), this.path = d, this.nextTerminalName = "", this.nextTerminalOccurrence = 0, this.nextTerminalName = this.path.lastTok.name, this.nextTerminalOccurrence = this.path.lastTokOccurrence;
	}
	walkTerminal(e, d, f) {
		this.isAtEndOfPath && e.terminalType.name === this.nextTerminalName && e.idx === this.nextTerminalOccurrence && !this.found && (this.possibleTokTypes = first(new Alternative({ definition: d.concat(f) })), this.found = !0);
	}
}, AbstractNextTerminalAfterProductionWalker = class extends RestWalker {
	constructor(e, d) {
		super(), this.topRule = e, this.occurrence = d, this.result = {
			token: void 0,
			occurrence: void 0,
			isEndOfRule: void 0
		};
	}
	startWalking() {
		return this.walk(this.topRule), this.result;
	}
}, NextTerminalAfterManyWalker = class extends AbstractNextTerminalAfterProductionWalker {
	walkMany(e, d, f) {
		if (e.idx === this.occurrence) {
			let e = head_default(d.concat(f));
			this.result.isEndOfRule = e === void 0, e instanceof Terminal && (this.result.token = e.terminalType, this.result.occurrence = e.idx);
		} else super.walkMany(e, d, f);
	}
}, NextTerminalAfterManySepWalker = class extends AbstractNextTerminalAfterProductionWalker {
	walkManySep(e, d, f) {
		if (e.idx === this.occurrence) {
			let e = head_default(d.concat(f));
			this.result.isEndOfRule = e === void 0, e instanceof Terminal && (this.result.token = e.terminalType, this.result.occurrence = e.idx);
		} else super.walkManySep(e, d, f);
	}
}, NextTerminalAfterAtLeastOneWalker = class extends AbstractNextTerminalAfterProductionWalker {
	walkAtLeastOne(e, d, f) {
		if (e.idx === this.occurrence) {
			let e = head_default(d.concat(f));
			this.result.isEndOfRule = e === void 0, e instanceof Terminal && (this.result.token = e.terminalType, this.result.occurrence = e.idx);
		} else super.walkAtLeastOne(e, d, f);
	}
}, NextTerminalAfterAtLeastOneSepWalker = class extends AbstractNextTerminalAfterProductionWalker {
	walkAtLeastOneSep(e, d, f) {
		if (e.idx === this.occurrence) {
			let e = head_default(d.concat(f));
			this.result.isEndOfRule = e === void 0, e instanceof Terminal && (this.result.token = e.terminalType, this.result.occurrence = e.idx);
		} else super.walkAtLeastOneSep(e, d, f);
	}
};
function possiblePathsFrom(f, m, g = []) {
	g = clone_default(g);
	let _ = [], v = 0;
	function y(e) {
		return e.concat(drop_default(f, v + 1));
	}
	function b(e) {
		let d = possiblePathsFrom(y(e), m, g);
		return _.concat(d);
	}
	for (; g.length < m && v < f.length;) {
		let d = f[v];
		if (d instanceof Alternative || d instanceof NonTerminal) return b(d.definition);
		if (d instanceof Option) _ = b(d.definition);
		else if (d instanceof RepetitionMandatory) return b(d.definition.concat([new Repetition({ definition: d.definition })]));
		else if (d instanceof RepetitionMandatoryWithSeparator) return b([new Alternative({ definition: d.definition }), new Repetition({ definition: [new Terminal({ terminalType: d.separator })].concat(d.definition) })]);
		else if (d instanceof RepetitionWithSeparator) _ = b(d.definition.concat([new Repetition({ definition: [new Terminal({ terminalType: d.separator })].concat(d.definition) })]));
		else if (d instanceof Repetition) _ = b(d.definition.concat([new Repetition({ definition: d.definition })]));
		else if (d instanceof Alternation) return forEach_default(d.definition, (d) => {
			isEmpty_default(d.definition) === !1 && (_ = b(d.definition));
		}), _;
		else if (d instanceof Terminal) g.push(d.terminalType);
		else throw Error("non exhaustive match");
		v++;
	}
	return _.push({
		partialPath: g,
		suffixDef: drop_default(f, v)
	}), _;
}
function nextPossibleTokensAfter(h, g, v, y) {
	let b = "EXIT_NONE_TERMINAL", x = [b], S = "EXIT_ALTERNATIVE", C = !1, w = g.length, T = w - y - 1, E = [], D = [];
	for (D.push({
		idx: -1,
		def: h,
		ruleStack: [],
		occurrenceStack: []
	}); !isEmpty_default(D);) {
		let h = D.pop();
		if (h === S) {
			C && last_default(D).idx <= T && D.pop();
			continue;
		}
		let y = h.def, O = h.idx, k = h.ruleStack, A = h.occurrenceStack;
		if (isEmpty_default(y)) continue;
		let j = y[0];
		/* istanbul ignore else */
		if (j === b) {
			let e = {
				idx: O,
				def: drop_default(y),
				ruleStack: dropRight_default(k),
				occurrenceStack: dropRight_default(A)
			};
			D.push(e);
		} else if (j instanceof Terminal)
 /* istanbul ignore else */
		if (O < w - 1) {
			let e = O + 1, d = g[e];
			if (v(d, j.terminalType)) {
				let d = {
					idx: e,
					def: drop_default(y),
					ruleStack: k,
					occurrenceStack: A
				};
				D.push(d);
			}
		} else if (O === w - 1) E.push({
			nextTokenType: j.terminalType,
			nextTokenOccurrence: j.idx,
			ruleStack: k,
			occurrenceStack: A
		}), C = !0;
		else throw Error("non exhaustive match");
		else if (j instanceof NonTerminal) {
			let e = clone_default(k);
			e.push(j.nonTerminalName);
			let f = clone_default(A);
			f.push(j.idx);
			let m = {
				idx: O,
				def: j.definition.concat(x, drop_default(y)),
				ruleStack: e,
				occurrenceStack: f
			};
			D.push(m);
		} else if (j instanceof Option) {
			let e = {
				idx: O,
				def: drop_default(y),
				ruleStack: k,
				occurrenceStack: A
			};
			D.push(e), D.push(S);
			let d = {
				idx: O,
				def: j.definition.concat(drop_default(y)),
				ruleStack: k,
				occurrenceStack: A
			};
			D.push(d);
		} else if (j instanceof RepetitionMandatory) {
			let e = new Repetition({
				definition: j.definition,
				idx: j.idx
			}), d = {
				idx: O,
				def: j.definition.concat([e], drop_default(y)),
				ruleStack: k,
				occurrenceStack: A
			};
			D.push(d);
		} else if (j instanceof RepetitionMandatoryWithSeparator) {
			let e = new Repetition({
				definition: [new Terminal({ terminalType: j.separator })].concat(j.definition),
				idx: j.idx
			}), d = {
				idx: O,
				def: j.definition.concat([e], drop_default(y)),
				ruleStack: k,
				occurrenceStack: A
			};
			D.push(d);
		} else if (j instanceof RepetitionWithSeparator) {
			let e = {
				idx: O,
				def: drop_default(y),
				ruleStack: k,
				occurrenceStack: A
			};
			D.push(e), D.push(S);
			let d = new Repetition({
				definition: [new Terminal({ terminalType: j.separator })].concat(j.definition),
				idx: j.idx
			}), f = {
				idx: O,
				def: j.definition.concat([d], drop_default(y)),
				ruleStack: k,
				occurrenceStack: A
			};
			D.push(f);
		} else if (j instanceof Repetition) {
			let e = {
				idx: O,
				def: drop_default(y),
				ruleStack: k,
				occurrenceStack: A
			};
			D.push(e), D.push(S);
			let d = new Repetition({
				definition: j.definition,
				idx: j.idx
			}), f = {
				idx: O,
				def: j.definition.concat([d], drop_default(y)),
				ruleStack: k,
				occurrenceStack: A
			};
			D.push(f);
		} else if (j instanceof Alternation) for (let e = j.definition.length - 1; e >= 0; e--) {
			let d = {
				idx: O,
				def: j.definition[e].definition.concat(drop_default(y)),
				ruleStack: k,
				occurrenceStack: A
			};
			D.push(d), D.push(S);
		}
		else if (j instanceof Alternative) D.push({
			idx: O,
			def: j.definition.concat(drop_default(y)),
			ruleStack: k,
			occurrenceStack: A
		});
		else if (j instanceof Rule) D.push(expandTopLevelRule(j, O, k, A));
		else throw Error("non exhaustive match");
	}
	return E;
}
function expandTopLevelRule(e, f, p, m) {
	let h = clone_default(p);
	h.push(e.name);
	let g = clone_default(m);
	return g.push(1), {
		idx: f,
		def: e.definition,
		ruleStack: h,
		occurrenceStack: g
	};
}
export { NextAfterTokenWalker, NextTerminalAfterAtLeastOneSepWalker, NextTerminalAfterAtLeastOneWalker, NextTerminalAfterManySepWalker, NextTerminalAfterManyWalker, nextPossibleTokensAfter, possiblePathsFrom };
