import { Lexer } from "../../../chevrotain/lib/src/scan/lexer_public.js";
import { createToken, createTokenInstance } from "../../../chevrotain/lib/src/scan/tokens_public.js";
import "../../../chevrotain/lib/src/api.js";
import { DefaultTokenBuilder } from "./token-builder.js";
import { DEFAULT_TOKENIZE_OPTIONS, DefaultLexer, isTokenTypeArray } from "./lexer.js";
const indentationBuilderDefaultOptions = {
	indentTokenName: "INDENT",
	dedentTokenName: "DEDENT",
	whitespaceTokenName: "WS",
	ignoreIndentationDelimiters: []
};
var LexingMode;
(function(e) {
	e.REGULAR = "indentation-sensitive", e.IGNORE_INDENTATION = "ignore-indentation";
})(LexingMode ||= {});
var IndentationAwareTokenBuilder = class extends DefaultTokenBuilder {
	constructor(e = indentationBuilderDefaultOptions) {
		super(), this.indentationStack = [0], this.whitespaceRegExp = /[ \t]+/y, this.options = {
			...indentationBuilderDefaultOptions,
			...e
		}, this.indentTokenType = createToken({
			name: this.options.indentTokenName,
			pattern: this.indentMatcher.bind(this),
			line_breaks: !1
		}), this.dedentTokenType = createToken({
			name: this.options.dedentTokenName,
			pattern: this.dedentMatcher.bind(this),
			line_breaks: !1
		});
	}
	buildTokens(e, t) {
		let n = super.buildTokens(e, t);
		if (!isTokenTypeArray(n)) throw Error("Invalid tokens built by default builder");
		let { indentTokenName: r, dedentTokenName: i, whitespaceTokenName: a, ignoreIndentationDelimiters: s } = this.options, l, u, d, f = [];
		for (let e of n) {
			for (let [t, n] of s) e.name === t ? e.PUSH_MODE = LexingMode.IGNORE_INDENTATION : e.name === n && (e.POP_MODE = !0);
			e.name === i ? l = e : e.name === r ? u = e : e.name === a ? d = e : f.push(e);
		}
		if (!l || !u || !d) throw Error("Some indentation/whitespace tokens not found!");
		return s.length > 0 ? {
			modes: {
				[LexingMode.REGULAR]: [
					l,
					u,
					...f,
					d
				],
				[LexingMode.IGNORE_INDENTATION]: [...f, d]
			},
			defaultMode: LexingMode.REGULAR
		} : [
			l,
			u,
			d,
			...f
		];
	}
	flushLexingReport(e) {
		return {
			...super.flushLexingReport(e),
			remainingDedents: this.flushRemainingDedents(e)
		};
	}
	isStartOfLine(e, t) {
		return t === 0 || "\r\n".includes(e[t - 1]);
	}
	matchWhitespace(e, t, n, r) {
		this.whitespaceRegExp.lastIndex = t;
		let i = this.whitespaceRegExp.exec(e);
		return {
			currIndentLevel: i?.[0].length ?? 0,
			prevIndentLevel: this.indentationStack.at(-1),
			match: i
		};
	}
	createIndentationTokenInstance(e, t, r, i) {
		let a = this.getLineNumber(t, i);
		return createTokenInstance(e, r, i, i + r.length, a, a, 1, r.length);
	}
	getLineNumber(e, t) {
		return e.substring(0, t).split(/\r\n|\r|\n/).length;
	}
	indentMatcher(e, t, n, r) {
		if (!this.isStartOfLine(e, t)) return null;
		let { currIndentLevel: i, prevIndentLevel: a, match: o } = this.matchWhitespace(e, t, n, r);
		return i <= a ? null : (this.indentationStack.push(i), o);
	}
	dedentMatcher(e, t, n, r) {
		if (!this.isStartOfLine(e, t)) return null;
		let { currIndentLevel: i, prevIndentLevel: a, match: o } = this.matchWhitespace(e, t, n, r);
		if (i >= a) return null;
		let s = this.indentationStack.lastIndexOf(i);
		if (s === -1) return this.diagnostics.push({
			severity: "error",
			message: `Invalid dedent level ${i} at offset: ${t}. Current indentation stack: ${this.indentationStack}`,
			offset: t,
			length: o?.[0]?.length ?? 0,
			line: this.getLineNumber(e, t),
			column: 1
		}), null;
		let c = this.indentationStack.length - s - 1, l = e.substring(0, t).match(/[\r\n]+$/)?.[0].length ?? 1;
		for (let r = 0; r < c; r++) {
			let r = this.createIndentationTokenInstance(this.dedentTokenType, e, "", t - (l - 1));
			n.push(r), this.indentationStack.pop();
		}
		return null;
	}
	buildTerminalToken(n) {
		let r = super.buildTerminalToken(n), { indentTokenName: i, dedentTokenName: a, whitespaceTokenName: o } = this.options;
		return r.name === i ? this.indentTokenType : r.name === a ? this.dedentTokenType : r.name === o ? createToken({
			name: o,
			pattern: this.whitespaceRegExp,
			group: Lexer.SKIPPED
		}) : r;
	}
	flushRemainingDedents(e) {
		let t = [];
		for (; this.indentationStack.length > 1;) t.push(this.createIndentationTokenInstance(this.dedentTokenType, e, "", e.length)), this.indentationStack.pop();
		return this.indentationStack = [0], t;
	}
}, IndentationAwareLexer = class extends DefaultLexer {
	constructor(e) {
		if (super(e), e.parser.TokenBuilder instanceof IndentationAwareTokenBuilder) this.indentationTokenBuilder = e.parser.TokenBuilder;
		else throw Error("IndentationAwareLexer requires an accompanying IndentationAwareTokenBuilder");
	}
	tokenize(e, t = DEFAULT_TOKENIZE_OPTIONS) {
		let n = super.tokenize(e), r = n.report;
		t?.mode === "full" && n.tokens.push(...r.remainingDedents), r.remainingDedents = [];
		let { indentTokenType: a, dedentTokenType: o } = this.indentationTokenBuilder, s = a.tokenTypeIdx, c = o.tokenTypeIdx, l = [], u = n.tokens.length - 1;
		for (let e = 0; e < u; e++) {
			let t = n.tokens[e], r = n.tokens[e + 1];
			if (t.tokenTypeIdx === s && r.tokenTypeIdx === c) {
				e++;
				continue;
			}
			l.push(t);
		}
		return u >= 0 && l.push(n.tokens[u]), n.tokens = l, n;
	}
};
export { IndentationAwareLexer, IndentationAwareTokenBuilder, LexingMode, indentationBuilderDefaultOptions };
