import isArray_default from "../../../../lodash-es/isArray.js";
import identity_default from "../../../../lodash-es/identity.js";
import isEmpty_default from "../../../../lodash-es/isEmpty.js";
import noop_default from "../../../../lodash-es/noop.js";
import keys_default from "../../../../lodash-es/keys.js";
import assign_default from "../../../../lodash-es/assign.js";
import clone_default from "../../../../lodash-es/clone.js";
import last_default from "../../../../lodash-es/last.js";
import forEach_default from "../../../../lodash-es/forEach.js";
import map_default from "../../../../lodash-es/map.js";
import isUndefined_default from "../../../../lodash-es/isUndefined.js";
import reduce_default from "../../../../lodash-es/reduce.js";
import reject_default from "../../../../lodash-es/reject.js";
import { PRINT_WARNING } from "../../../../@chevrotain/utils/lib/src/print.js";
import { timer } from "../../../../@chevrotain/utils/lib/src/timer.js";
import { toFastProperties } from "../../../../@chevrotain/utils/lib/src/to-fast-properties.js";
import { clearRegExpParserCache } from "./reg_exp_parser.js";
import { DEFAULT_MODE, LineTerminatorOptimizedTester, analyzeTokenTypes, charCodeToOptimizedIndex, cloneEmptyGroups, performRuntimeChecks, performWarningRuntimeChecks, validatePatterns } from "./lexer.js";
import { augmentTokenTypes } from "./tokens.js";
import { defaultLexerErrorProvider } from "./lexer_errors_public.js";
var LexerDefinitionErrorType;
(function(e) {
	e[e.MISSING_PATTERN = 0] = "MISSING_PATTERN", e[e.INVALID_PATTERN = 1] = "INVALID_PATTERN", e[e.EOI_ANCHOR_FOUND = 2] = "EOI_ANCHOR_FOUND", e[e.UNSUPPORTED_FLAGS_FOUND = 3] = "UNSUPPORTED_FLAGS_FOUND", e[e.DUPLICATE_PATTERNS_FOUND = 4] = "DUPLICATE_PATTERNS_FOUND", e[e.INVALID_GROUP_TYPE_FOUND = 5] = "INVALID_GROUP_TYPE_FOUND", e[e.PUSH_MODE_DOES_NOT_EXIST = 6] = "PUSH_MODE_DOES_NOT_EXIST", e[e.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE = 7] = "MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE", e[e.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY = 8] = "MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY", e[e.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST = 9] = "MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST", e[e.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED = 10] = "LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED", e[e.SOI_ANCHOR_FOUND = 11] = "SOI_ANCHOR_FOUND", e[e.EMPTY_MATCH_PATTERN = 12] = "EMPTY_MATCH_PATTERN", e[e.NO_LINE_BREAKS_FLAGS = 13] = "NO_LINE_BREAKS_FLAGS", e[e.UNREACHABLE_PATTERN = 14] = "UNREACHABLE_PATTERN", e[e.IDENTIFY_TERMINATOR = 15] = "IDENTIFY_TERMINATOR", e[e.CUSTOM_LINE_BREAK = 16] = "CUSTOM_LINE_BREAK", e[e.MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE = 17] = "MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE";
})(LexerDefinitionErrorType ||= {});
var DEFAULT_LEXER_CONFIG = {
	deferDefinitionErrorsHandling: !1,
	positionTracking: "full",
	lineTerminatorsPattern: /\n|\r\n?/g,
	lineTerminatorCharacters: ["\n", "\r"],
	ensureOptimizations: !1,
	safeMode: !1,
	errorMessageProvider: defaultLexerErrorProvider,
	traceInitPerf: !1,
	skipValidations: !1,
	recoveryEnabled: !0
};
Object.freeze(DEFAULT_LEXER_CONFIG);
var Lexer = class {
	constructor(s, b = DEFAULT_LEXER_CONFIG) {
		if (this.lexerDefinition = s, this.lexerDefinitionErrors = [], this.lexerDefinitionWarning = [], this.patternIdxToConfig = {}, this.charCodeToPatternIdxToConfig = {}, this.modes = [], this.emptyGroups = {}, this.trackStartLines = !0, this.trackEndLines = !0, this.hasCustom = !1, this.canModeBeOptimized = {}, this.TRACE_INIT = (e, t) => {
			if (this.traceInitPerf === !0) {
				this.traceInitIndent++;
				let n = Array(this.traceInitIndent + 1).join("	");
				this.traceInitIndent < this.traceInitMaxIdent && console.log(`${n}--> <${e}>`);
				let { time: r, value: i } = timer(t), a = r > 10 ? console.warn : console.log;
				return this.traceInitIndent < this.traceInitMaxIdent && a(`${n}<-- <${e}> time: ${r}ms`), this.traceInitIndent--, i;
			} else return t();
		}, typeof b == "boolean") throw Error("The second argument to the Lexer constructor is now an ILexerConfig Object.\na boolean 2nd argument is no longer supported");
		this.config = assign_default({}, DEFAULT_LEXER_CONFIG, b);
		let x = this.config.traceInitPerf;
		x === !0 ? (this.traceInitMaxIdent = Infinity, this.traceInitPerf = !0) : typeof x == "number" && (this.traceInitMaxIdent = x, this.traceInitPerf = !0), this.traceInitIndent = -1, this.TRACE_INIT("Lexer Constructor", () => {
			let m, x = !0;
			this.TRACE_INIT("Lexer Config handling", () => {
				if (this.config.lineTerminatorsPattern === DEFAULT_LEXER_CONFIG.lineTerminatorsPattern) this.config.lineTerminatorsPattern = LineTerminatorOptimizedTester;
				else if (this.config.lineTerminatorCharacters === DEFAULT_LEXER_CONFIG.lineTerminatorCharacters) throw Error("Error: Missing <lineTerminatorCharacters> property on the Lexer config.\n	For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#MISSING_LINE_TERM_CHARS");
				if (b.safeMode && b.ensureOptimizations) throw Error("\"safeMode\" and \"ensureOptimizations\" flags are mutually exclusive.");
				this.trackStartLines = /full|onlyStart/i.test(this.config.positionTracking), this.trackEndLines = /full/i.test(this.config.positionTracking), isArray_default(s) ? m = {
					modes: { defaultMode: clone_default(s) },
					defaultMode: DEFAULT_MODE
				} : (x = !1, m = clone_default(s));
			}), this.config.skipValidations === !1 && (this.TRACE_INIT("performRuntimeChecks", () => {
				this.lexerDefinitionErrors = this.lexerDefinitionErrors.concat(performRuntimeChecks(m, this.trackStartLines, this.config.lineTerminatorCharacters));
			}), this.TRACE_INIT("performWarningRuntimeChecks", () => {
				this.lexerDefinitionWarning = this.lexerDefinitionWarning.concat(performWarningRuntimeChecks(m, this.trackStartLines, this.config.lineTerminatorCharacters));
			})), m.modes = m.modes ? m.modes : {}, forEach_default(m.modes, (e, t) => {
				m.modes[t] = reject_default(e, (e) => isUndefined_default(e));
			});
			let E = keys_default(m.modes);
			if (forEach_default(m.modes, (e, t) => {
				this.TRACE_INIT(`Mode: <${t}> processing`, () => {
					if (this.modes.push(t), this.config.skipValidations === !1 && this.TRACE_INIT("validatePatterns", () => {
						this.lexerDefinitionErrors = this.lexerDefinitionErrors.concat(validatePatterns(e, E));
					}), isEmpty_default(this.lexerDefinitionErrors)) {
						augmentTokenTypes(e);
						let n;
						this.TRACE_INIT("analyzeTokenTypes", () => {
							n = analyzeTokenTypes(e, {
								lineTerminatorCharacters: this.config.lineTerminatorCharacters,
								positionTracking: b.positionTracking,
								ensureOptimizations: b.ensureOptimizations,
								safeMode: b.safeMode,
								tracer: this.TRACE_INIT
							});
						}), this.patternIdxToConfig[t] = n.patternIdxToConfig, this.charCodeToPatternIdxToConfig[t] = n.charCodeToPatternIdxToConfig, this.emptyGroups = assign_default({}, this.emptyGroups, n.emptyGroups), this.hasCustom = n.hasCustom || this.hasCustom, this.canModeBeOptimized[t] = n.canBeOptimized;
					}
				});
			}), this.defaultMode = m.defaultMode, !isEmpty_default(this.lexerDefinitionErrors) && !this.config.deferDefinitionErrorsHandling) {
				let e = map_default(this.lexerDefinitionErrors, (e) => e.message).join("-----------------------\n");
				throw Error("Errors detected in definition of Lexer:\n" + e);
			}
			forEach_default(this.lexerDefinitionWarning, (e) => {
				PRINT_WARNING(e.message);
			}), this.TRACE_INIT("Choosing sub-methods implementations", () => {
				if (x && (this.handleModes = noop_default), this.trackStartLines === !1 && (this.computeNewColumn = identity_default), this.trackEndLines === !1 && (this.updateTokenEndLineColumnLocation = noop_default), /full/i.test(this.config.positionTracking)) this.createTokenInstance = this.createFullToken;
				else if (/onlyStart/i.test(this.config.positionTracking)) this.createTokenInstance = this.createStartOnlyToken;
				else if (/onlyOffset/i.test(this.config.positionTracking)) this.createTokenInstance = this.createOffsetOnlyToken;
				else throw Error(`Invalid <positionTracking> config option: "${this.config.positionTracking}"`);
				this.hasCustom ? (this.addToken = this.addTokenUsingPush, this.handlePayload = this.handlePayloadWithCustom) : (this.addToken = this.addTokenUsingMemberAccess, this.handlePayload = this.handlePayloadNoCustom);
			}), this.TRACE_INIT("Failed Optimization Warnings", () => {
				let e = reduce_default(this.canModeBeOptimized, (e, t, n) => (t === !1 && e.push(n), e), []);
				if (b.ensureOptimizations && !isEmpty_default(e)) throw Error(`Lexer Modes: < ${e.join(", ")} > cannot be optimized.\n	 Disable the "ensureOptimizations" lexer config flag to silently ignore this and run the lexer in an un-optimized mode.
	 Or inspect the console log for details on how to resolve these issues.`);
			}), this.TRACE_INIT("clearRegExpParserCache", () => {
				clearRegExpParserCache();
			}), this.TRACE_INIT("toFastProperties", () => {
				toFastProperties(this);
			});
		});
	}
	tokenize(e, t = this.defaultMode) {
		if (!isEmpty_default(this.lexerDefinitionErrors)) {
			let e = map_default(this.lexerDefinitionErrors, (e) => e.message).join("-----------------------\n");
			throw Error("Unable to Tokenize because Errors detected in definition of Lexer:\n" + e);
		}
		return this.tokenizeInternal(e, t);
	}
	tokenizeInternal(e, t) {
		let n, r, i, a, o, c, l, u, d, f, p, m, h, g, _, v = e, y = v.length, S = 0, C = 0, w = this.hasCustom ? 0 : Math.floor(e.length / 10), T = Array(w), E = [], D = this.trackStartLines ? 1 : void 0, O = this.trackStartLines ? 1 : void 0, k = cloneEmptyGroups(this.emptyGroups), A = this.trackStartLines, j = this.config.lineTerminatorsPattern, M = 0, N = [], P = [], F = [], I = [];
		Object.freeze(I);
		let L = !1, R = (e) => {
			if (F.length === 1 && e.tokenType.PUSH_MODE === void 0) {
				let t = this.config.errorMessageProvider.buildUnableToPopLexerModeMessage(e);
				E.push({
					offset: e.startOffset,
					line: e.startLine,
					column: e.startColumn,
					length: e.image.length,
					message: t
				});
			} else {
				F.pop();
				let e = last_default(F);
				N = this.patternIdxToConfig[e], P = this.charCodeToPatternIdxToConfig[e], M = N.length;
				let t = this.canModeBeOptimized[e] && this.config.safeMode === !1;
				L = !!(P && t);
			}
		};
		function z(e) {
			F.push(e), P = this.charCodeToPatternIdxToConfig[e], N = this.patternIdxToConfig[e], M = N.length, M = N.length;
			let t = this.canModeBeOptimized[e] && this.config.safeMode === !1;
			L = !!(P && t);
		}
		z.call(this, t);
		let B, V = this.config.recoveryEnabled;
		for (; S < y;) {
			c = null, d = -1;
			let t = v.charCodeAt(S), x;
			if (L) {
				let e = charCodeToOptimizedIndex(t), n = P[e];
				x = n === void 0 ? I : n;
			} else x = N;
			let w = x.length;
			for (n = 0; n < w; n++) {
				B = x[n];
				let r = B.pattern;
				l = null;
				let s = B.short;
				if (s === !1 ? B.isCustom === !0 ? (_ = r.exec(v, S, T, k), _ === null ? c = null : (c = _[0], d = c.length, _.payload !== void 0 && (l = _.payload))) : (r.lastIndex = S, d = this.matchLength(r, e, S)) : t === s && (d = 1, c = r), d !== -1) {
					if (o = B.longerAlt, o !== void 0) {
						c = e.substring(S, S + d);
						let t = o.length;
						for (i = 0; i < t; i++) {
							let t = N[o[i]], n = t.pattern;
							if (u = null, t.isCustom === !0 ? (_ = n.exec(v, S, T, k), _ === null ? a = null : (a = _[0], _.payload !== void 0 && (u = _.payload))) : (n.lastIndex = S, a = this.match(n, e, S)), a && a.length > c.length) {
								c = a, d = a.length, l = u, B = t;
								break;
							}
						}
					}
					break;
				}
			}
			if (d !== -1) {
				if (f = B.group, f !== void 0 && (c = c === null ? e.substring(S, S + d) : c, p = B.tokenTypeIdx, m = this.createTokenInstance(c, S, p, B.tokenType, D, O, d), this.handlePayload(m, l), f === !1 ? C = this.addToken(T, C, m) : k[f].push(m)), A === !0 && B.canLineTerminator === !0) {
					let t = 0, n, r;
					j.lastIndex = 0;
					do
						c = c === null ? e.substring(S, S + d) : c, n = j.test(c), n === !0 && (r = j.lastIndex - 1, t++);
					while (n === !0);
					t === 0 ? O = this.computeNewColumn(O, d) : (D += t, O = d - r, this.updateTokenEndLineColumnLocation(m, f, r, t, D, O, d));
				} else O = this.computeNewColumn(O, d);
				S += d, this.handleModes(B, R, z, m);
			} else {
				let t = S, n = D, i = O, a = V === !1;
				for (; a === !1 && S < y;) for (S++, r = 0; r < M; r++) {
					let t = N[r], n = t.pattern, i = t.short;
					if (i === !1 ? t.isCustom === !0 ? a = n.exec(v, S, T, k) !== null : (n.lastIndex = S, a = n.exec(e) !== null) : v.charCodeAt(S) === i && (a = !0), a === !0) break;
				}
				if (h = S - t, O = this.computeNewColumn(O, h), g = this.config.errorMessageProvider.buildUnexpectedCharactersMessage(v, t, h, n, i, last_default(F)), E.push({
					offset: t,
					line: n,
					column: i,
					length: h,
					message: g
				}), V === !1) break;
			}
		}
		return this.hasCustom || (T.length = C), {
			tokens: T,
			groups: k,
			errors: E
		};
	}
	handleModes(e, t, n, r) {
		if (e.pop === !0) {
			let i = e.push;
			t(r), i !== void 0 && n.call(this, i);
		} else e.push !== void 0 && n.call(this, e.push);
	}
	updateTokenEndLineColumnLocation(e, t, n, r, i, a, o) {
		let s, c;
		t !== void 0 && (s = n === o - 1, c = s ? -1 : 0, r === 1 && s === !0 || (e.endLine = i + c, e.endColumn = a - 1 + -c));
	}
	computeNewColumn(e, t) {
		return e + t;
	}
	createOffsetOnlyToken(e, t, n, r) {
		return {
			image: e,
			startOffset: t,
			tokenTypeIdx: n,
			tokenType: r
		};
	}
	createStartOnlyToken(e, t, n, r, i, a) {
		return {
			image: e,
			startOffset: t,
			startLine: i,
			startColumn: a,
			tokenTypeIdx: n,
			tokenType: r
		};
	}
	createFullToken(e, t, n, r, i, a, o) {
		return {
			image: e,
			startOffset: t,
			endOffset: t + o - 1,
			startLine: i,
			endLine: i,
			startColumn: a,
			endColumn: a + o - 1,
			tokenTypeIdx: n,
			tokenType: r
		};
	}
	addTokenUsingPush(e, t, n) {
		return e.push(n), t;
	}
	addTokenUsingMemberAccess(e, t, n) {
		return e[t] = n, t++, t;
	}
	handlePayloadNoCustom(e, t) {}
	handlePayloadWithCustom(e, t) {
		t !== null && (e.payload = t);
	}
	match(e, t, n) {
		return e.test(t) === !0 ? t.substring(n, e.lastIndex) : null;
	}
	matchLength(e, t, n) {
		return e.test(t) === !0 ? e.lastIndex - n : -1;
	}
};
Lexer.SKIPPED = "This marks a skipped Token pattern, this means each token identified by it will be consumed and then thrown into oblivion, this can be used to for example to completely ignore whitespace.", Lexer.NA = /NOT_APPLICABLE/;
export { Lexer, LexerDefinitionErrorType };
