import { replaceCodePoint as R } from "./decode-codepoint.js";
var n;
(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";
})(n || (n = {}));
const _ = 32;
var a;
(function(t) {
  t[t.VALUE_LENGTH = 49152] = "VALUE_LENGTH", t[t.BRANCH_LENGTH = 16256] = "BRANCH_LENGTH", t[t.JUMP_TABLE = 127] = "JUMP_TABLE";
})(a || (a = {}));
function l(t) {
  return t >= n.ZERO && t <= n.NINE;
}
function L(t) {
  return t >= n.UPPER_A && t <= n.UPPER_F || t >= n.LOWER_A && t <= n.LOWER_F;
}
function A(t) {
  return t >= n.UPPER_A && t <= n.UPPER_Z || t >= n.LOWER_A && t <= n.LOWER_Z || l(t);
}
function o(t) {
  return t === n.EQUALS || A(t);
}
var c;
(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";
})(c || (c = {}));
var N;
(function(t) {
  t[t.Legacy = 0] = "Legacy", t[t.Strict = 1] = "Strict", t[t.Attribute = 2] = "Attribute";
})(N || (N = {}));
class U {
  constructor(e, i, r) {
    this.decodeTree = e, this.emitCodePoint = i, this.errors = r, this.state = c.EntityStart, this.consumed = 1, this.result = 0, this.treeIndex = 0, this.excess = 1, this.decodeMode = N.Strict;
  }
  /** Resets the instance to make it reusable. */
  startEntity(e) {
    this.decodeMode = e, this.state = c.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 input 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 c.EntityStart:
        return e.charCodeAt(i) === n.NUM ? (this.state = c.NumericStart, this.consumed += 1, this.stateNumericStart(e, i + 1)) : (this.state = c.NamedEntity, this.stateNamedEntity(e, i));
      case c.NumericStart:
        return this.stateNumericStart(e, i);
      case c.NumericDecimal:
        return this.stateNumericDecimal(e, i);
      case c.NumericHex:
        return this.stateNumericHex(e, i);
      case c.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 input 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) | _) === n.LOWER_X ? (this.state = c.NumericHex, this.consumed += 1, this.stateNumericHex(e, i + 1)) : (this.state = c.NumericDecimal, this.stateNumericDecimal(e, i));
  }
  addToNumericResult(e, i, r, s) {
    if (i !== r) {
      const u = r - i;
      this.result = this.result * Math.pow(s, u) + Number.parseInt(e.substr(i, u), s), this.consumed += u;
    }
  }
  /**
   * Parses a hexadecimal numeric entity.
   *
   * Equivalent to the `Hexademical character reference state` in the HTML spec.
   *
   * @param input 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 s = e.charCodeAt(i);
      if (l(s) || L(s))
        i += 1;
      else
        return this.addToNumericResult(e, r, i, 16), this.emitNumericEntity(s, 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 input 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 s = e.charCodeAt(i);
      if (l(s))
        i += 1;
      else
        return this.addToNumericResult(e, r, i, 10), this.emitNumericEntity(s, 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 === n.SEMI)
      this.consumed += 1;
    else if (this.decodeMode === N.Strict)
      return 0;
    return this.emitCodePoint(R(this.result), this.consumed), this.errors && (e !== n.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 input 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 s = r[this.treeIndex], u = (s & a.VALUE_LENGTH) >> 14;
    for (; i < e.length; i++, this.excess++) {
      const m = e.charCodeAt(i);
      if (this.treeIndex = P(r, s, this.treeIndex + Math.max(1, u), m), this.treeIndex < 0)
        return this.result === 0 || // If we are parsing an attribute
        this.decodeMode === N.Attribute && // We shouldn't have consumed any characters after the entity,
        (u === 0 || // And there should be no invalid characters.
        o(m)) ? 0 : this.emitNotTerminatedNamedEntity();
      if (s = r[this.treeIndex], u = (s & a.VALUE_LENGTH) >> 14, u !== 0) {
        if (m === n.SEMI)
          return this.emitNamedEntityData(this.treeIndex, u, this.consumed + this.excess);
        this.decodeMode !== N.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, s = (r[i] & a.VALUE_LENGTH) >> 14;
    return this.emitNamedEntityData(i, s, 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: s } = this;
    return this.emitCodePoint(i === 1 ? s[e] & ~a.VALUE_LENGTH : s[e + 1], r), i === 3 && this.emitCodePoint(s[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 c.NamedEntity:
        return this.result !== 0 && (this.decodeMode !== N.Attribute || this.result === this.treeIndex) ? this.emitNotTerminatedNamedEntity() : 0;
      // Otherwise, emit a numeric entity if we have one.
      case c.NumericDecimal:
        return this.emitNumericEntity(0, 2);
      case c.NumericHex:
        return this.emitNumericEntity(0, 3);
      case c.NumericStart:
        return (e = this.errors) === null || e === void 0 || e.absenceOfDigitsInNumericCharacterReference(this.consumed), 0;
      case c.EntityStart:
        return 0;
    }
  }
}
function P(t, e, i, r) {
  const s = (e & a.BRANCH_LENGTH) >> 7, u = e & a.JUMP_TABLE;
  if (s === 0)
    return u !== 0 && r === u ? i : -1;
  if (u) {
    const h = r - u;
    return h < 0 || h >= s ? -1 : t[i + h] - 1;
  }
  let m = i, E = m + s - 1;
  for (; m <= E; ) {
    const h = m + E >>> 1, d = t[h];
    if (d < r)
      m = h + 1;
    else if (d > r)
      E = h - 1;
    else
      return t[h + s];
  }
  return -1;
}
export {
  a as BinTrieFlags,
  N as DecodingMode,
  U as EntityDecoder,
  P as determineBranch,
  R as replaceCodePoint
};
//# sourceMappingURL=decode.js.map
