import isArray_default from "../../../../lodash-es/isArray.js";
import { BaseRegExpVisitor } from "../../../../@chevrotain/regexp-to-ast/lib/src/base-regexp-visitor.js";
import "../../../../@chevrotain/regexp-to-ast/lib/src/api.js";
import forEach_default from "../../../../lodash-es/forEach.js";
import every_default from "../../../../lodash-es/every.js";
import find_default from "../../../../lodash-es/find.js";
import values_default from "../../../../lodash-es/values.js";
import includes_default from "../../../../lodash-es/includes.js";
import { PRINT_ERROR, PRINT_WARNING } from "../../../../@chevrotain/utils/lib/src/print.js";
import { getRegExpAst } from "./reg_exp_parser.js";
import { charCodeToOptimizedIndex, minOptimizationVal } from "./lexer.js";
var complementErrorMessage = "Complement Sets are not supported for first char optimization";
const failedOptimizationPrefixMsg = "Unable to use \"first char\" lexer optimizations:\n";
function getOptimizedStartCodesIndices(e, h = !1) {
	try {
		let h = getRegExpAst(e);
		return firstCharOptimizedIndices(h.value, {}, h.flags.ignoreCase);
	} catch (g) {
		/* istanbul ignore next */
		if (g.message === complementErrorMessage) h && PRINT_WARNING(`${failedOptimizationPrefixMsg}\tUnable to optimize: < ${e.toString()} >\n	Complement Sets cannot be automatically optimized.
	This will disable the lexer's first char optimizations.
	See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#COMPLEMENT for details.`);
		else {
			let g = "";
			h && (g = "\n	This will disable the lexer's first char optimizations.\n	See: https://chevrotain.io/docs/guide/resolving_lexer_errors.html#REGEXP_PARSING for details."), PRINT_ERROR(`${failedOptimizationPrefixMsg}\n\tFailed parsing: < ${e.toString()} >\n\tUsing the @chevrotain/regexp-to-ast library\n	Please open an issue at: https://github.com/chevrotain/chevrotain/issues` + g);
		}
	}
	return [];
}
function firstCharOptimizedIndices(e, h, _) {
	switch (e.type) {
		case "Disjunction":
			for (let g = 0; g < e.value.length; g++) firstCharOptimizedIndices(e.value[g], h, _);
			break;
		case "Alternative":
			let v = e.value;
			for (let e = 0; e < v.length; e++) {
				let y = v[e];
				switch (y.type) {
					case "EndAnchor":
					case "GroupBackReference":
					case "Lookahead":
					case "NegativeLookahead":
					case "Lookbehind":
					case "NegativeLookbehind":
					case "StartAnchor":
					case "WordBoundary":
					case "NonWordBoundary": continue;
				}
				let b = y;
				switch (b.type) {
					case "Character":
						addOptimizedIdxToResult(b.value, h, _);
						break;
					case "Set":
						if (b.complement === !0) throw Error(complementErrorMessage);
						forEach_default(b.value, (e) => {
							if (typeof e == "number") addOptimizedIdxToResult(e, h, _);
							else {
								let g = e;
								if (_ === !0) for (let e = g.from; e <= g.to; e++) addOptimizedIdxToResult(e, h, _);
								else {
									for (let e = g.from; e <= g.to && e < 256; e++) addOptimizedIdxToResult(e, h, _);
									if (g.to >= 256) {
										let e = g.from >= 256 ? g.from : 256, _ = g.to, v = charCodeToOptimizedIndex(e), y = charCodeToOptimizedIndex(_);
										for (let e = v; e <= y; e++) h[e] = e;
									}
								}
							}
						});
						break;
					case "Group":
						firstCharOptimizedIndices(b.value, h, _);
						break;
					default: throw Error("Non Exhaustive Match");
				}
				let x = b.quantifier !== void 0 && b.quantifier.atLeast === 0;
				if (b.type === "Group" && isWholeOptional(b) === !1 || b.type !== "Group" && x === !1) break;
			}
			break;
		default: throw Error("non exhaustive match!");
	}
	return values_default(h);
}
function addOptimizedIdxToResult(e, h, g) {
	let _ = charCodeToOptimizedIndex(e);
	h[_] = _, g === !0 && handleIgnoreCase(e, h);
}
function handleIgnoreCase(e, h) {
	let g = String.fromCharCode(e), _ = g.toUpperCase();
	/* istanbul ignore else */
	if (_ !== g) {
		let e = charCodeToOptimizedIndex(_.charCodeAt(0));
		h[e] = e;
	} else {
		let e = g.toLowerCase();
		if (e !== g) {
			let g = charCodeToOptimizedIndex(e.charCodeAt(0));
			h[g] = g;
		}
	}
}
function findCode(e, h) {
	return find_default(e.value, (e) => {
		if (typeof e == "number") return includes_default(h, e);
		{
			let g = e;
			return find_default(h, (e) => g.from <= e && e <= g.to) !== void 0;
		}
	});
}
function isWholeOptional(h) {
	let g = h.quantifier;
	return g && g.atLeast === 0 ? !0 : h.value ? isArray_default(h.value) ? every_default(h.value, isWholeOptional) : isWholeOptional(h.value) : !1;
}
var CharCodeFinder = class extends BaseRegExpVisitor {
	constructor(e) {
		super(), this.targetCharCodes = e, this.found = !1;
	}
	visitChildren(e) {
		if (this.found !== !0) {
			switch (e.type) {
				case "Lookahead":
					this.visitLookahead(e);
					return;
				case "NegativeLookahead":
					this.visitNegativeLookahead(e);
					return;
				case "Lookbehind":
					this.visitLookbehind(e);
					return;
				case "NegativeLookbehind":
					this.visitNegativeLookbehind(e);
					return;
			}
			super.visitChildren(e);
		}
	}
	visitCharacter(e) {
		includes_default(this.targetCharCodes, e.value) && (this.found = !0);
	}
	visitSet(e) {
		e.complement ? findCode(e, this.targetCharCodes) === void 0 && (this.found = !0) : findCode(e, this.targetCharCodes) !== void 0 && (this.found = !0);
	}
};
function canMatchCharCode(e, h) {
	if (h instanceof RegExp) {
		let g = getRegExpAst(h), _ = new CharCodeFinder(e);
		return _.visit(g), _.found;
	} else return find_default(h, (h) => includes_default(e, h.charCodeAt(0))) !== void 0;
}
export { canMatchCharCode, failedOptimizationPrefixMsg, getOptimizedStartCodesIndices };
