import isFunction_default from "../../../../lodash-es/isFunction.js";
import isArray_default from "../../../../lodash-es/isArray.js";
import isEmpty_default from "../../../../lodash-es/isEmpty.js";
import { BaseRegExpVisitor } from "../../../../@chevrotain/regexp-to-ast/lib/src/base-regexp-visitor.js";
import "../../../../@chevrotain/regexp-to-ast/lib/src/api.js";
import keys_default from "../../../../lodash-es/keys.js";
import flatten_default from "../../../../lodash-es/flatten.js";
import compact_default from "../../../../lodash-es/compact.js";
import defaults_default from "../../../../lodash-es/defaults.js";
import difference_default from "../../../../lodash-es/difference.js";
import forEach_default from "../../../../lodash-es/forEach.js";
import filter_default from "../../../../lodash-es/filter.js";
import find_default from "../../../../lodash-es/find.js";
import head_default from "../../../../lodash-es/head.js";
import map_default from "../../../../lodash-es/map.js";
import has_default from "../../../../lodash-es/has.js";
import isString_default from "../../../../lodash-es/isString.js";
import values_default from "../../../../lodash-es/values.js";
import includes_default from "../../../../lodash-es/includes.js";
import indexOf_default from "../../../../lodash-es/indexOf.js";
import isRegExp_default from "../../../../lodash-es/isRegExp.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_ERROR } from "../../../../@chevrotain/utils/lib/src/print.js";
import { getRegExpAst } from "./reg_exp_parser.js";
import { canMatchCharCode, failedOptimizationPrefixMsg, getOptimizedStartCodesIndices } from "./reg_exp.js";
import { Lexer, LexerDefinitionErrorType } from "./lexer_public.js";
var PATTERN = "PATTERN";
const DEFAULT_MODE = "defaultMode";
function analyzeTokenTypes(F, I) {
	I = defaults_default(I, {
		debug: !1,
		safeMode: !1,
		positionTracking: "full",
		lineTerminatorCharacters: ["\r", "\n"],
		tracer: (i, N) => N()
	});
	let L = I.tracer;
	L("initCharCodeToOptimizedIndexMap", () => {
		initCharCodeToOptimizedIndexMap();
	});
	let R;
	L("Reject Lexer.NA", () => {
		R = reject_default(F, (i) => i[PATTERN] === Lexer.NA);
	});
	let B = !1, V;
	L("Transform Patterns", () => {
		B = !1, V = map_default(R, (N) => {
			let P = N[PATTERN];
			/* istanbul ignore else */
			if (isRegExp_default(P)) {
				let i = P.source;
				return i.length === 1 && i !== "^" && i !== "$" && i !== "." && !P.ignoreCase ? i : i.length === 2 && i[0] === "\\" && !includes_default([
					"d",
					"D",
					"s",
					"S",
					"t",
					"r",
					"n",
					"t",
					"0",
					"c",
					"b",
					"B",
					"f",
					"v",
					"w",
					"W"
				], i[1]) ? i[1] : addStickyFlag(P);
			} else if (isFunction_default(P)) return B = !0, { exec: P };
			else if (typeof P == "object") return B = !0, P;
			else if (typeof P == "string") {
				if (P.length === 1) return P;
				{
					let i = P.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
					return addStickyFlag(new RegExp(i));
				}
			} else throw Error("non exhaustive match");
		});
	});
	let H, U, W, G, K;
	L("misc mapping", () => {
		H = map_default(R, (i) => i.tokenTypeIdx), U = map_default(R, (i) => {
			let N = i.GROUP;
			if (N !== Lexer.SKIPPED) {
				if (isString_default(N)) return N;
				if (isUndefined_default(N)) return !1;
				throw Error("non exhaustive match");
			}
		}), W = map_default(R, (i) => {
			let P = i.LONGER_ALT;
			if (P) return isArray_default(P) ? map_default(P, (i) => indexOf_default(R, i)) : [indexOf_default(R, P)];
		}), G = map_default(R, (i) => i.PUSH_MODE), K = map_default(R, (i) => has_default(i, "POP_MODE"));
	});
	let q;
	L("Line Terminator Handling", () => {
		let i = getCharCodes(I.lineTerminatorCharacters);
		q = map_default(R, (i) => !1), I.positionTracking !== "onlyOffset" && (q = map_default(R, (N) => has_default(N, "LINE_BREAKS") ? !!N.LINE_BREAKS : checkLineBreaksIssues(N, i) === !1 && canMatchCharCode(i, N.PATTERN)));
	});
	let J, Y, X, Z;
	L("Misc Mapping #2", () => {
		J = map_default(R, isCustomPattern), Y = map_default(V, isShortPattern), X = reduce_default(R, (i, N) => {
			let P = N.GROUP;
			return isString_default(P) && P !== Lexer.SKIPPED && (i[P] = []), i;
		}, {}), Z = map_default(V, (i, N) => ({
			pattern: V[N],
			longerAlt: W[N],
			canLineTerminator: q[N],
			isCustom: J[N],
			short: Y[N],
			group: U[N],
			push: G[N],
			pop: K[N],
			tokenTypeIdx: H[N],
			tokenType: R[N]
		}));
	});
	let Q = !0, $ = [];
	return I.safeMode || L("First Char Optimization", () => {
		$ = reduce_default(R, (i, F, L) => {
			if (typeof F.PATTERN == "string") addToMapOfArrays(i, charCodeToOptimizedIndex(F.PATTERN.charCodeAt(0)), Z[L]);
			else if (isArray_default(F.START_CHARS_HINT)) {
				let N;
				forEach_default(F.START_CHARS_HINT, (P) => {
					let F = charCodeToOptimizedIndex(typeof P == "string" ? P.charCodeAt(0) : P);
					/* istanbul ignore else */
					N !== F && (N = F, addToMapOfArrays(i, F, Z[L]));
				});
			} else if (isRegExp_default(F.PATTERN)) if (F.PATTERN.unicode) Q = !1, I.ensureOptimizations && PRINT_ERROR(`${failedOptimizationPrefixMsg}\tUnable to analyze < ${F.PATTERN.toString()} > pattern.\n	The regexp unicode flag is not currently supported by the regexp-to-ast library.
	This will disable the lexer's first char optimizations.
	For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNICODE_OPTIMIZE`);
			else {
				let N = getOptimizedStartCodesIndices(F.PATTERN, I.ensureOptimizations);
				isEmpty_default(N) && (Q = !1), forEach_default(N, (N) => {
					addToMapOfArrays(i, N, Z[L]);
				});
			}
			else I.ensureOptimizations && PRINT_ERROR(`${failedOptimizationPrefixMsg}\tTokenType: <${F.name}> is using a custom token pattern without providing <start_chars_hint> parameter.\n	This will disable the lexer's first char optimizations.
	For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_OPTIMIZE`), Q = !1;
			return i;
		}, []);
	}), {
		emptyGroups: X,
		patternIdxToConfig: Z,
		charCodeToPatternIdxToConfig: $,
		hasCustom: B,
		canBeOptimized: Q
	};
}
function validatePatterns(i, N) {
	let P = [], F = findMissingPatterns(i);
	P = P.concat(F.errors);
	let I = findInvalidPatterns(F.valid), L = I.valid;
	return P = P.concat(I.errors), P = P.concat(validateRegExpPattern(L)), P = P.concat(findInvalidGroupType(L)), P = P.concat(findModesThatDoNotExist(L, N)), P = P.concat(findUnreachablePatterns(L)), P;
}
function validateRegExpPattern(i) {
	let N = [], P = filter_default(i, (i) => isRegExp_default(i[PATTERN]));
	return N = N.concat(findEndOfInputAnchor(P)), N = N.concat(findStartOfInputAnchor(P)), N = N.concat(findUnsupportedFlags(P)), N = N.concat(findDuplicatePatterns(P)), N = N.concat(findEmptyMatchRegExps(P)), N;
}
function findMissingPatterns(i) {
	let N = filter_default(i, (i) => !has_default(i, PATTERN));
	return {
		errors: map_default(N, (i) => ({
			message: "Token Type: ->" + i.name + "<- missing static 'PATTERN' property",
			type: LexerDefinitionErrorType.MISSING_PATTERN,
			tokenTypes: [i]
		})),
		valid: difference_default(i, N)
	};
}
function findInvalidPatterns(N) {
	let P = filter_default(N, (N) => {
		let P = N[PATTERN];
		return !isRegExp_default(P) && !isFunction_default(P) && !has_default(P, "exec") && !isString_default(P);
	});
	return {
		errors: map_default(P, (i) => ({
			message: "Token Type: ->" + i.name + "<- static 'PATTERN' can only be a RegExp, a Function matching the {CustomPatternMatcherFunc} type or an Object matching the {ICustomPattern} interface.",
			type: LexerDefinitionErrorType.INVALID_PATTERN,
			tokenTypes: [i]
		})),
		valid: difference_default(N, P)
	};
}
var end_of_input = /[^\\][$]/;
function findEndOfInputAnchor(i) {
	class N extends BaseRegExpVisitor {
		constructor() {
			super(...arguments), this.found = !1;
		}
		visitEndAnchor(i) {
			this.found = !0;
		}
	}
	return map_default(filter_default(i, (i) => {
		let P = i.PATTERN;
		try {
			let i = getRegExpAst(P), F = new N();
			return F.visit(i), F.found;
		} catch {
			/* istanbul ignore next - cannot ensure an error in regexp-to-ast*/
			return end_of_input.test(P.source);
		}
	}), (i) => ({
		message: "Unexpected RegExp Anchor Error:\n	Token Type: ->" + i.name + "<- static 'PATTERN' cannot contain end of input anchor '$'\n	See chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS	for details.",
		type: LexerDefinitionErrorType.EOI_ANCHOR_FOUND,
		tokenTypes: [i]
	}));
}
function findEmptyMatchRegExps(i) {
	return map_default(filter_default(i, (i) => i.PATTERN.test("")), (i) => ({
		message: "Token Type: ->" + i.name + "<- static 'PATTERN' must not match an empty string",
		type: LexerDefinitionErrorType.EMPTY_MATCH_PATTERN,
		tokenTypes: [i]
	}));
}
var start_of_input = /[^\\[][\^]|^\^/;
function findStartOfInputAnchor(i) {
	class N extends BaseRegExpVisitor {
		constructor() {
			super(...arguments), this.found = !1;
		}
		visitStartAnchor(i) {
			this.found = !0;
		}
	}
	return map_default(filter_default(i, (i) => {
		let P = i.PATTERN;
		try {
			let i = getRegExpAst(P), F = new N();
			return F.visit(i), F.found;
		} catch {
			/* istanbul ignore next - cannot ensure an error in regexp-to-ast*/
			return start_of_input.test(P.source);
		}
	}), (i) => ({
		message: "Unexpected RegExp Anchor Error:\n	Token Type: ->" + i.name + "<- static 'PATTERN' cannot contain start of input anchor '^'\n	See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#ANCHORS	for details.",
		type: LexerDefinitionErrorType.SOI_ANCHOR_FOUND,
		tokenTypes: [i]
	}));
}
function findUnsupportedFlags(i) {
	return map_default(filter_default(i, (i) => {
		let N = i[PATTERN];
		return N instanceof RegExp && (N.multiline || N.global);
	}), (i) => ({
		message: "Token Type: ->" + i.name + "<- static 'PATTERN' may NOT contain global('g') or multiline('m')",
		type: LexerDefinitionErrorType.UNSUPPORTED_FLAGS_FOUND,
		tokenTypes: [i]
	}));
}
function findDuplicatePatterns(i) {
	let N = [], P = map_default(i, (P) => reduce_default(i, (i, F) => P.PATTERN.source === F.PATTERN.source && !includes_default(N, F) && F.PATTERN !== Lexer.NA ? (N.push(F), i.push(F), i) : i, []));
	return P = compact_default(P), map_default(filter_default(P, (i) => i.length > 1), (i) => {
		let N = map_default(i, (i) => i.name);
		return {
			message: `The same RegExp pattern ->${head_default(i).PATTERN}<-has been used in all of the following Token Types: ${N.join(", ")} <-`,
			type: LexerDefinitionErrorType.DUPLICATE_PATTERNS_FOUND,
			tokenTypes: i
		};
	});
}
function findInvalidGroupType(i) {
	return map_default(filter_default(i, (i) => {
		if (!has_default(i, "GROUP")) return !1;
		let N = i.GROUP;
		return N !== Lexer.SKIPPED && N !== Lexer.NA && !isString_default(N);
	}), (i) => ({
		message: "Token Type: ->" + i.name + "<- static 'GROUP' can only be Lexer.SKIPPED/Lexer.NA/A String",
		type: LexerDefinitionErrorType.INVALID_GROUP_TYPE_FOUND,
		tokenTypes: [i]
	}));
}
function findModesThatDoNotExist(i, N) {
	return map_default(filter_default(i, (i) => i.PUSH_MODE !== void 0 && !includes_default(N, i.PUSH_MODE)), (i) => ({
		message: `Token Type: ->${i.name}<- static 'PUSH_MODE' value cannot refer to a Lexer Mode ->${i.PUSH_MODE}<-which does not exist`,
		type: LexerDefinitionErrorType.PUSH_MODE_DOES_NOT_EXIST,
		tokenTypes: [i]
	}));
}
function findUnreachablePatterns(i) {
	let N = [], P = reduce_default(i, (i, N, P) => {
		let F = N.PATTERN;
		return F === Lexer.NA || (isString_default(F) ? i.push({
			str: F,
			idx: P,
			tokenType: N
		}) : isRegExp_default(F) && noMetaChar(F) && i.push({
			str: F.source,
			idx: P,
			tokenType: N
		})), i;
	}, []);
	return forEach_default(i, (i, F) => {
		forEach_default(P, ({ str: P, idx: I, tokenType: L }) => {
			if (F < I && tryToMatchStrToPattern(P, i.PATTERN)) {
				let P = `Token: ->${L.name}<- can never be matched.\nBecause it appears AFTER the Token Type ->${i.name}<-in the lexer's definition.\nSee https://chevrotain.io/docs/guide/resolving_lexer_errors.html#UNREACHABLE`;
				N.push({
					message: P,
					type: LexerDefinitionErrorType.UNREACHABLE_PATTERN,
					tokenTypes: [i, L]
				});
			}
		});
	}), N;
}
function tryToMatchStrToPattern(N, P) {
	if (isRegExp_default(P)) {
		if (usesLookAheadOrBehind(P)) return !1;
		let i = P.exec(N);
		return i !== null && i.index === 0;
	} else if (isFunction_default(P)) return P(N, 0, [], {});
	else if (has_default(P, "exec")) return P.exec(N, 0, [], {});
	else if (typeof P == "string") return P === N;
	else throw Error("non exhaustive match");
}
function noMetaChar(i) {
	return find_default([
		".",
		"\\",
		"[",
		"]",
		"|",
		"^",
		"$",
		"(",
		")",
		"?",
		"*",
		"+",
		"{"
	], (N) => i.source.indexOf(N) !== -1) === void 0;
}
function usesLookAheadOrBehind(i) {
	return /(\(\?=)|(\(\?!)|(\(\?<=)|(\(\?<!)/.test(i.source);
}
function addStickyFlag(i) {
	let N = i.ignoreCase ? "iy" : "y";
	return RegExp(`${i.source}`, N);
}
function performRuntimeChecks(i, P, F) {
	let I = [];
	return has_default(i, "defaultMode") || I.push({
		message: "A MultiMode Lexer cannot be initialized without a <" + DEFAULT_MODE + "> property in its definition\n",
		type: LexerDefinitionErrorType.MULTI_MODE_LEXER_WITHOUT_DEFAULT_MODE
	}), has_default(i, "modes") || I.push({
		message: "A MultiMode Lexer cannot be initialized without a <modes> property in its definition\n",
		type: LexerDefinitionErrorType.MULTI_MODE_LEXER_WITHOUT_MODES_PROPERTY
	}), has_default(i, "modes") && has_default(i, "defaultMode") && !has_default(i.modes, i.defaultMode) && I.push({
		message: `A MultiMode Lexer cannot be initialized with a ${DEFAULT_MODE}: <${i.defaultMode}>which does not exist\n`,
		type: LexerDefinitionErrorType.MULTI_MODE_LEXER_DEFAULT_MODE_VALUE_DOES_NOT_EXIST
	}), has_default(i, "modes") && forEach_default(i.modes, (i, P) => {
		forEach_default(i, (F, L) => {
			isUndefined_default(F) ? I.push({
				message: `A Lexer cannot be initialized using an undefined Token Type. Mode:<${P}> at index: <${L}>\n`,
				type: LexerDefinitionErrorType.LEXER_DEFINITION_CANNOT_CONTAIN_UNDEFINED
			}) : has_default(F, "LONGER_ALT") && forEach_default(isArray_default(F.LONGER_ALT) ? F.LONGER_ALT : [F.LONGER_ALT], (N) => {
				!isUndefined_default(N) && !includes_default(i, N) && I.push({
					message: `A MultiMode Lexer cannot be initialized with a longer_alt <${N.name}> on token <${F.name}> outside of mode <${P}>\n`,
					type: LexerDefinitionErrorType.MULTI_MODE_LEXER_LONGER_ALT_NOT_IN_CURRENT_MODE
				});
			});
		});
	}), I;
}
function performWarningRuntimeChecks(i, N, P) {
	let F = [], I = !1, z = reject_default(compact_default(flatten_default(values_default(i.modes))), (i) => i[PATTERN] === Lexer.NA), B = getCharCodes(P);
	return N && forEach_default(z, (i) => {
		let N = checkLineBreaksIssues(i, B);
		if (N !== !1) {
			let P = {
				message: buildLineBreakIssueMessage(i, N),
				type: N.issue,
				tokenType: i
			};
			F.push(P);
		} else has_default(i, "LINE_BREAKS") ? i.LINE_BREAKS === !0 && (I = !0) : canMatchCharCode(B, i.PATTERN) && (I = !0);
	}), N && !I && F.push({
		message: "Warning: No LINE_BREAKS Found.\n	This Lexer has been defined to track line and column information,\n	But none of the Token Types can be identified as matching a line terminator.\n	See https://chevrotain.io/docs/guide/resolving_lexer_errors.html#LINE_BREAKS \n	for details.",
		type: LexerDefinitionErrorType.NO_LINE_BREAKS_FLAGS
	}), F;
}
function cloneEmptyGroups(i) {
	let P = {};
	return forEach_default(keys_default(i), (F) => {
		let I = i[F];
		/* istanbul ignore else */
		if (isArray_default(I)) P[F] = [];
		else throw Error("non exhaustive match");
	}), P;
}
function isCustomPattern(N) {
	let P = N.PATTERN;
	/* istanbul ignore else */
	if (isRegExp_default(P)) return !1;
	if (isFunction_default(P) || has_default(P, "exec")) return !0;
	if (isString_default(P)) return !1;
	throw Error("non exhaustive match");
}
function isShortPattern(i) {
	return isString_default(i) && i.length === 1 ? i.charCodeAt(0) : !1;
}
const LineTerminatorOptimizedTester = {
	test: function(i) {
		let N = i.length;
		for (let P = this.lastIndex; P < N; P++) {
			let N = i.charCodeAt(P);
			if (N === 10) return this.lastIndex = P + 1, !0;
			if (N === 13) return i.charCodeAt(P + 1) === 10 ? this.lastIndex = P + 2 : this.lastIndex = P + 1, !0;
		}
		return !1;
	},
	lastIndex: 0
};
function checkLineBreaksIssues(i, N) {
	if (has_default(i, "LINE_BREAKS")) return !1;
	if (isRegExp_default(i.PATTERN)) {
		try {
			canMatchCharCode(N, i.PATTERN);
		} catch (i) {
			/* istanbul ignore next - to test this we would have to mock <canMatchCharCode> to throw an error */
			return {
				issue: LexerDefinitionErrorType.IDENTIFY_TERMINATOR,
				errMsg: i.message
			};
		}
		return !1;
	} else if (isString_default(i.PATTERN)) return !1;
	else if (isCustomPattern(i)) return { issue: LexerDefinitionErrorType.CUSTOM_LINE_BREAK };
	else throw Error("non exhaustive match");
}
function buildLineBreakIssueMessage(i, N) {
	/* istanbul ignore else */
	if (N.issue === LexerDefinitionErrorType.IDENTIFY_TERMINATOR) return `Warning: unable to identify line terminator usage in pattern.
\tThe problem is in the <${i.name}> Token Type\n\t Root cause: ${N.errMsg}.\n	For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#IDENTIFY_TERMINATOR`;
	if (N.issue === LexerDefinitionErrorType.CUSTOM_LINE_BREAK) return `Warning: A Custom Token Pattern should specify the <line_breaks> option.
\tThe problem is in the <${i.name}> Token Type\n	For details See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#CUSTOM_LINE_BREAK`;
	throw Error("non exhaustive match");
}
function getCharCodes(i) {
	return map_default(i, (i) => isString_default(i) ? i.charCodeAt(0) : i);
}
function addToMapOfArrays(i, N, P) {
	i[N] === void 0 ? i[N] = [P] : i[N].push(P);
}
const minOptimizationVal = 256;
var charCodeToOptimizedIdxMap = [];
function charCodeToOptimizedIndex(i) {
	return i < 256 ? i : charCodeToOptimizedIdxMap[i];
}
function initCharCodeToOptimizedIndexMap() {
	if (isEmpty_default(charCodeToOptimizedIdxMap)) {
		charCodeToOptimizedIdxMap = Array(65536);
		for (let i = 0; i < 65536; i++) charCodeToOptimizedIdxMap[i] = i > 255 ? 255 + ~~(i / 255) : i;
	}
}
export { DEFAULT_MODE, LineTerminatorOptimizedTester, analyzeTokenTypes, charCodeToOptimizedIndex, cloneEmptyGroups, minOptimizationVal, performRuntimeChecks, performWarningRuntimeChecks, validatePatterns };
