import { htmlDecodeTree } from "./generated/decode-data-html.js";
import { replaceCodePoint } from "./decode-codepoint.js";
var CharCodes;
(function(e) {
	e[e.NUM = 35] = "NUM", e[e.SEMI = 59] = "SEMI", e[e.EQUALS = 61] = "EQUALS", e[e.ZERO = 48] = "ZERO", e[e.NINE = 57] = "NINE", e[e.LOWER_A = 97] = "LOWER_A", e[e.LOWER_F = 102] = "LOWER_F", e[e.LOWER_X = 120] = "LOWER_X", e[e.LOWER_Z = 122] = "LOWER_Z", e[e.UPPER_A = 65] = "UPPER_A", e[e.UPPER_F = 70] = "UPPER_F", e[e.UPPER_Z = 90] = "UPPER_Z";
})(CharCodes ||= {});
var TO_LOWER_BIT = 32, BinTrieFlags;
(function(e) {
	e[e.VALUE_LENGTH = 49152] = "VALUE_LENGTH", e[e.BRANCH_LENGTH = 16256] = "BRANCH_LENGTH", e[e.JUMP_TABLE = 127] = "JUMP_TABLE";
})(BinTrieFlags ||= {});
function isNumber(e) {
	return e >= CharCodes.ZERO && e <= CharCodes.NINE;
}
function isHexadecimalCharacter(e) {
	return e >= CharCodes.UPPER_A && e <= CharCodes.UPPER_F || e >= CharCodes.LOWER_A && e <= CharCodes.LOWER_F;
}
function isAsciiAlphaNumeric(e) {
	return e >= CharCodes.UPPER_A && e <= CharCodes.UPPER_Z || e >= CharCodes.LOWER_A && e <= CharCodes.LOWER_Z || isNumber(e);
}
function isEntityInAttributeInvalidEnd(e) {
	return e === CharCodes.EQUALS || isAsciiAlphaNumeric(e);
}
var EntityDecoderState;
(function(e) {
	e[e.EntityStart = 0] = "EntityStart", e[e.NumericStart = 1] = "NumericStart", e[e.NumericDecimal = 2] = "NumericDecimal", e[e.NumericHex = 3] = "NumericHex", e[e.NamedEntity = 4] = "NamedEntity";
})(EntityDecoderState ||= {});
var DecodingMode;
(function(e) {
	e[e.Legacy = 0] = "Legacy", e[e.Strict = 1] = "Strict", e[e.Attribute = 2] = "Attribute";
})(DecodingMode ||= {});
var EntityDecoder = class {
	constructor(e, a, o) {
		this.decodeTree = e, this.emitCodePoint = a, this.errors = o, this.state = EntityDecoderState.EntityStart, this.consumed = 1, this.result = 0, this.treeIndex = 0, this.excess = 1, this.decodeMode = DecodingMode.Strict;
	}
	startEntity(e) {
		this.decodeMode = e, this.state = EntityDecoderState.EntityStart, this.result = 0, this.treeIndex = 0, this.excess = 1, this.consumed = 1;
	}
	write(e, a) {
		switch (this.state) {
			case EntityDecoderState.EntityStart: return e.charCodeAt(a) === CharCodes.NUM ? (this.state = EntityDecoderState.NumericStart, this.consumed += 1, this.stateNumericStart(e, a + 1)) : (this.state = EntityDecoderState.NamedEntity, this.stateNamedEntity(e, a));
			case EntityDecoderState.NumericStart: return this.stateNumericStart(e, a);
			case EntityDecoderState.NumericDecimal: return this.stateNumericDecimal(e, a);
			case EntityDecoderState.NumericHex: return this.stateNumericHex(e, a);
			case EntityDecoderState.NamedEntity: return this.stateNamedEntity(e, a);
		}
	}
	stateNumericStart(e, a) {
		return a >= e.length ? -1 : (e.charCodeAt(a) | TO_LOWER_BIT) === CharCodes.LOWER_X ? (this.state = EntityDecoderState.NumericHex, this.consumed += 1, this.stateNumericHex(e, a + 1)) : (this.state = EntityDecoderState.NumericDecimal, this.stateNumericDecimal(e, a));
	}
	addToNumericResult(e, a, o, s) {
		if (a !== o) {
			let c = o - a;
			this.result = this.result * s ** +c + Number.parseInt(e.substr(a, c), s), this.consumed += c;
		}
	}
	stateNumericHex(e, a) {
		let o = a;
		for (; a < e.length;) {
			let s = e.charCodeAt(a);
			if (isNumber(s) || isHexadecimalCharacter(s)) a += 1;
			else return this.addToNumericResult(e, o, a, 16), this.emitNumericEntity(s, 3);
		}
		return this.addToNumericResult(e, o, a, 16), -1;
	}
	stateNumericDecimal(e, a) {
		let o = a;
		for (; a < e.length;) {
			let s = e.charCodeAt(a);
			if (isNumber(s)) a += 1;
			else return this.addToNumericResult(e, o, a, 10), this.emitNumericEntity(s, 2);
		}
		return this.addToNumericResult(e, o, a, 10), -1;
	}
	emitNumericEntity(e, s) {
		var c;
		if (this.consumed <= s) return (c = this.errors) == null || c.absenceOfDigitsInNumericCharacterReference(this.consumed), 0;
		if (e === CharCodes.SEMI) this.consumed += 1;
		else if (this.decodeMode === DecodingMode.Strict) return 0;
		return this.emitCodePoint(replaceCodePoint(this.result), this.consumed), this.errors && (e !== CharCodes.SEMI && this.errors.missingSemicolonAfterCharacterReference(), this.errors.validateNumericCharacterReference(this.result)), this.consumed;
	}
	stateNamedEntity(e, a) {
		let { decodeTree: s } = this, l = s[this.treeIndex], u = (l & BinTrieFlags.VALUE_LENGTH) >> 14;
		for (; a < e.length; a++, this.excess++) {
			let d = e.charCodeAt(a);
			if (this.treeIndex = determineBranch(s, l, this.treeIndex + Math.max(1, u), d), this.treeIndex < 0) return this.result === 0 || this.decodeMode === DecodingMode.Attribute && (u === 0 || isEntityInAttributeInvalidEnd(d)) ? 0 : this.emitNotTerminatedNamedEntity();
			if (l = s[this.treeIndex], u = (l & BinTrieFlags.VALUE_LENGTH) >> 14, u !== 0) {
				if (d === CharCodes.SEMI) return this.emitNamedEntityData(this.treeIndex, u, this.consumed + this.excess);
				this.decodeMode !== DecodingMode.Strict && (this.result = this.treeIndex, this.consumed += this.excess, this.excess = 0);
			}
		}
		return -1;
	}
	emitNotTerminatedNamedEntity() {
		var e;
		let { result: a, decodeTree: o } = this, s = (o[a] & BinTrieFlags.VALUE_LENGTH) >> 14;
		return this.emitNamedEntityData(a, s, this.consumed), (e = this.errors) == null || e.missingSemicolonAfterCharacterReference(), this.consumed;
	}
	emitNamedEntityData(e, a, o) {
		let { decodeTree: s } = this;
		return this.emitCodePoint(a === 1 ? s[e] & ~BinTrieFlags.VALUE_LENGTH : s[e + 1], o), a === 3 && this.emitCodePoint(s[e + 2], o), o;
	}
	end() {
		var e;
		switch (this.state) {
			case EntityDecoderState.NamedEntity: return this.result !== 0 && (this.decodeMode !== DecodingMode.Attribute || this.result === this.treeIndex) ? this.emitNotTerminatedNamedEntity() : 0;
			case EntityDecoderState.NumericDecimal: return this.emitNumericEntity(0, 2);
			case EntityDecoderState.NumericHex: return this.emitNumericEntity(0, 3);
			case EntityDecoderState.NumericStart: return (e = this.errors) == null || e.absenceOfDigitsInNumericCharacterReference(this.consumed), 0;
			case EntityDecoderState.EntityStart: return 0;
		}
	}
};
function determineBranch(e, a, o, s) {
	let l = (a & BinTrieFlags.BRANCH_LENGTH) >> 7, u = a & BinTrieFlags.JUMP_TABLE;
	if (l === 0) return u !== 0 && s === u ? o : -1;
	if (u) {
		let a = s - u;
		return a < 0 || a >= l ? -1 : e[o + a] - 1;
	}
	let d = o, f = d + l - 1;
	for (; d <= f;) {
		let a = d + f >>> 1, o = e[a];
		if (o < s) d = a + 1;
		else if (o > s) f = a - 1;
		else return e[a + l];
	}
	return -1;
}
export { DecodingMode, EntityDecoder };
