import L from "./generated/decode-data-html.js";
import R from "./generated/decode-data-xml.js";
import { fromCodePoint as _, replaceCodePoint as A } from "./decode_codepoint.js";
var c;
(function(t) {
  t[t.NUM = 35] = "NUM", t[t.SEMI = 59] = "SEMI", t[t.EQUALS = 61] = "EQUALS", t[t.ZERO = 48] = "ZERO", t[t.NINE = 57] = "NINE", t[t.LOWER_A = 97] = "LOWER_A", t[t.LOWER_F = 102] = "LOWER_F", t[t.LOWER_X = 120] = "LOWER_X", t[t.LOWER_Z = 122] = "LOWER_Z", t[t.UPPER_A = 65] = "UPPER_A", t[t.UPPER_F = 70] = "UPPER_F", t[t.UPPER_Z = 90] = "UPPER_Z";
})(c || (c = {}));
const x = 32;
var N;
(function(t) {
  t[t.VALUE_LENGTH = 49152] = "VALUE_LENGTH", t[t.BRANCH_LENGTH = 16256] = "BRANCH_LENGTH", t[t.JUMP_TABLE = 127] = "JUMP_TABLE";
})(N || (N = {}));
function d(t) {
  return t >= c.ZERO && t <= c.NINE;
}
function P(t) {
  return t >= c.UPPER_A && t <= c.UPPER_F || t >= c.LOWER_A && t <= c.LOWER_F;
}
function I(t) {
  return t >= c.UPPER_A && t <= c.UPPER_Z || t >= c.LOWER_A && t <= c.LOWER_Z || d(t);
}
function U(t) {
  return t === c.EQUALS || I(t);
}
var s;
(function(t) {
  t[t.EntityStart = 0] = "EntityStart", t[t.NumericStart = 1] = "NumericStart", t[t.NumericDecimal = 2] = "NumericDecimal", t[t.NumericHex = 3] = "NumericHex", t[t.NamedEntity = 4] = "NamedEntity";
})(s || (s = {}));
var E;
(function(t) {
  t[t.Legacy = 0] = "Legacy", t[t.Strict = 1] = "Strict", t[t.Attribute = 2] = "Attribute";
})(E || (E = {}));
class y {
  constructor(e, i, r) {
    this.decodeTree = e, this.emitCodePoint = i, this.errors = r, this.state = s.EntityStart, this.consumed = 1, this.result = 0, this.treeIndex = 0, this.excess = 1, this.decodeMode = E.Strict;
  }
  /** Resets the instance to make it reusable. */
  startEntity(e) {
    this.decodeMode = e, this.state = s.EntityStart, this.result = 0, this.treeIndex = 0, this.excess = 1, this.consumed = 1;
  }
  /**
   * Write an entity to the decoder. This can be called multiple times with partial entities.
   * If the entity is incomplete, the decoder will return -1.
   *
   * Mirrors the implementation of `getDecoder`, but with the ability to stop decoding if the
   * entity is incomplete, and resume when the next string is written.
   *
   * @param string The string containing the entity (or a continuation of the entity).
   * @param offset The offset at which the entity begins. Should be 0 if this is not the first call.
   * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
   */
  write(e, i) {
    switch (this.state) {
      case s.EntityStart:
        return e.charCodeAt(i) === c.NUM ? (this.state = s.NumericStart, this.consumed += 1, this.stateNumericStart(e, i + 1)) : (this.state = s.NamedEntity, this.stateNamedEntity(e, i));
      case s.NumericStart:
        return this.stateNumericStart(e, i);
      case s.NumericDecimal:
        return this.stateNumericDecimal(e, i);
      case s.NumericHex:
        return this.stateNumericHex(e, i);
      case s.NamedEntity:
        return this.stateNamedEntity(e, i);
    }
  }
  /**
   * Switches between the numeric decimal and hexadecimal states.
   *
   * Equivalent to the `Numeric character reference state` in the HTML spec.
   *
   * @param str The string containing the entity (or a continuation of the entity).
   * @param offset The current offset.
   * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
   */
  stateNumericStart(e, i) {
    return i >= e.length ? -1 : (e.charCodeAt(i) | x) === c.LOWER_X ? (this.state = s.NumericHex, this.consumed += 1, this.stateNumericHex(e, i + 1)) : (this.state = s.NumericDecimal, this.stateNumericDecimal(e, i));
  }
  addToNumericResult(e, i, r, n) {
    if (i !== r) {
      const u = r - i;
      this.result = this.result * Math.pow(n, u) + parseInt(e.substr(i, u), n), this.consumed += u;
    }
  }
  /**
   * Parses a hexadecimal numeric entity.
   *
   * Equivalent to the `Hexademical character reference state` in the HTML spec.
   *
   * @param str The string containing the entity (or a continuation of the entity).
   * @param offset The current offset.
   * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
   */
  stateNumericHex(e, i) {
    const r = i;
    for (; i < e.length; ) {
      const n = e.charCodeAt(i);
      if (d(n) || P(n))
        i += 1;
      else
        return this.addToNumericResult(e, r, i, 16), this.emitNumericEntity(n, 3);
    }
    return this.addToNumericResult(e, r, i, 16), -1;
  }
  /**
   * Parses a decimal numeric entity.
   *
   * Equivalent to the `Decimal character reference state` in the HTML spec.
   *
   * @param str The string containing the entity (or a continuation of the entity).
   * @param offset The current offset.
   * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
   */
  stateNumericDecimal(e, i) {
    const r = i;
    for (; i < e.length; ) {
      const n = e.charCodeAt(i);
      if (d(n))
        i += 1;
      else
        return this.addToNumericResult(e, r, i, 10), this.emitNumericEntity(n, 2);
    }
    return this.addToNumericResult(e, r, i, 10), -1;
  }
  /**
   * Validate and emit a numeric entity.
   *
   * Implements the logic from the `Hexademical character reference start
   * state` and `Numeric character reference end state` in the HTML spec.
   *
   * @param lastCp The last code point of the entity. Used to see if the
   *               entity was terminated with a semicolon.
   * @param expectedLength The minimum number of characters that should be
   *                       consumed. Used to validate that at least one digit
   *                       was consumed.
   * @returns The number of characters that were consumed.
   */
  emitNumericEntity(e, i) {
    var r;
    if (this.consumed <= i)
      return (r = this.errors) === null || r === void 0 || r.absenceOfDigitsInNumericCharacterReference(this.consumed), 0;
    if (e === c.SEMI)
      this.consumed += 1;
    else if (this.decodeMode === E.Strict)
      return 0;
    return this.emitCodePoint(A(this.result), this.consumed), this.errors && (e !== c.SEMI && this.errors.missingSemicolonAfterCharacterReference(), this.errors.validateNumericCharacterReference(this.result)), this.consumed;
  }
  /**
   * Parses a named entity.
   *
   * Equivalent to the `Named character reference state` in the HTML spec.
   *
   * @param str The string containing the entity (or a continuation of the entity).
   * @param offset The current offset.
   * @returns The number of characters that were consumed, or -1 if the entity is incomplete.
   */
  stateNamedEntity(e, i) {
    const { decodeTree: r } = this;
    let n = r[this.treeIndex], u = (n & N.VALUE_LENGTH) >> 14;
    for (; i < e.length; i++, this.excess++) {
      const m = e.charCodeAt(i);
      if (this.treeIndex = T(r, n, this.treeIndex + Math.max(1, u), m), this.treeIndex < 0)
        return this.result === 0 || // If we are parsing an attribute
        this.decodeMode === E.Attribute && // We shouldn't have consumed any characters after the entity,
        (u === 0 || // And there should be no invalid characters.
        U(m)) ? 0 : this.emitNotTerminatedNamedEntity();
      if (n = r[this.treeIndex], u = (n & N.VALUE_LENGTH) >> 14, u !== 0) {
        if (m === c.SEMI)
          return this.emitNamedEntityData(this.treeIndex, u, this.consumed + this.excess);
        this.decodeMode !== E.Strict && (this.result = this.treeIndex, this.consumed += this.excess, this.excess = 0);
      }
    }
    return -1;
  }
  /**
   * Emit a named entity that was not terminated with a semicolon.
   *
   * @returns The number of characters consumed.
   */
  emitNotTerminatedNamedEntity() {
    var e;
    const { result: i, decodeTree: r } = this, n = (r[i] & N.VALUE_LENGTH) >> 14;
    return this.emitNamedEntityData(i, n, this.consumed), (e = this.errors) === null || e === void 0 || e.missingSemicolonAfterCharacterReference(), this.consumed;
  }
  /**
   * Emit a named entity.
   *
   * @param result The index of the entity in the decode tree.
   * @param valueLength The number of bytes in the entity.
   * @param consumed The number of characters consumed.
   *
   * @returns The number of characters consumed.
   */
  emitNamedEntityData(e, i, r) {
    const { decodeTree: n } = this;
    return this.emitCodePoint(i === 1 ? n[e] & ~N.VALUE_LENGTH : n[e + 1], r), i === 3 && this.emitCodePoint(n[e + 2], r), r;
  }
  /**
   * Signal to the parser that the end of the input was reached.
   *
   * Remaining data will be emitted and relevant errors will be produced.
   *
   * @returns The number of characters consumed.
   */
  end() {
    var e;
    switch (this.state) {
      case s.NamedEntity:
        return this.result !== 0 && (this.decodeMode !== E.Attribute || this.result === this.treeIndex) ? this.emitNotTerminatedNamedEntity() : 0;
      // Otherwise, emit a numeric entity if we have one.
      case s.NumericDecimal:
        return this.emitNumericEntity(0, 2);
      case s.NumericHex:
        return this.emitNumericEntity(0, 3);
      case s.NumericStart:
        return (e = this.errors) === null || e === void 0 || e.absenceOfDigitsInNumericCharacterReference(this.consumed), 0;
      case s.EntityStart:
        return 0;
    }
  }
}
function o(t) {
  let e = "";
  const i = new y(t, (r) => e += _(r));
  return function(n, u) {
    let m = 0, h = 0;
    for (; (h = n.indexOf("&", h)) >= 0; ) {
      e += n.slice(m, h), i.startEntity(u);
      const l = i.write(
        n,
        // Skip the "&"
        h + 1
      );
      if (l < 0) {
        m = h + i.end();
        break;
      }
      m = h + l, h = l === 0 ? m + 1 : m;
    }
    const a = e + n.slice(m);
    return e = "", a;
  };
}
function T(t, e, i, r) {
  const n = (e & N.BRANCH_LENGTH) >> 7, u = e & N.JUMP_TABLE;
  if (n === 0)
    return u !== 0 && r === u ? i : -1;
  if (u) {
    const a = r - u;
    return a < 0 || a >= n ? -1 : t[i + a] - 1;
  }
  let m = i, h = m + n - 1;
  for (; m <= h; ) {
    const a = m + h >>> 1, l = t[a];
    if (l < r)
      m = a + 1;
    else if (l > r)
      h = a - 1;
    else
      return t[a + n];
  }
  return -1;
}
const H = o(L);
o(R);
function M(t, e = E.Legacy) {
  return H(t, e);
}
export {
  N as BinTrieFlags,
  E as DecodingMode,
  y as EntityDecoder,
  M as decodeHTML,
  T as determineBranch,
  _ as fromCodePoint,
  L as htmlDecodeTree,
  A as replaceCodePoint,
  R as xmlDecodeTree
};
