// The DEC VT500 byte parser (scalar path).

///|
const MAX_PARAMS : Int = 32

///|
const MAX_INTERMEDIATES : Int = 2

///|
const MAX_OSC_PARAMS : Int = 16

///| A byte-input DEC VT500 terminal parser. Dispatch decoded events to a

///|
/// `Perform` sink via `advance`; use `Recording` for the owned event API.
pub struct Parser {
  mut state : ParseState
  mut params : Array[Int]
  mut params_len : Int
  mut param : Int
  mut param_empty : Bool
  mut sep : Int
  mut intermediates : Array[Int]
  mut intermediates_len : Int
  mut ignore : Bool
  mut partial : Int
  mut partial_need : Int
  mut osc : Array[Int]
  mut osc_starts : Array[Int]
  mut simd : Bool
}

///|
/// Create a parser in the ground state (scalar decode).
pub fn Parser::new() -> Parser {
  {
    state: ParseState::Ground,
    params: [],
    params_len: 0,
    param: 0,
    param_empty: true,
    sep: 0,
    intermediates: [],
    intermediates_len: 0,
    ignore: false,
    partial: 0,
    partial_need: 0,
    osc: [],
    osc_starts: [],
    simd: false,
  }
}

///|
/// Create a parser with the SIMD-accelerated printable-run scan enabled.
pub fn Parser::with_simd() -> Parser {
  let p = Parser::new()
  p.simd = true
  p
}

///|
/// Parse `bytes`, dispatching each decoded event to the sink `p`.
pub fn[P : Perform] Parser::advance(
  self : Parser,
  p : P,
  bytes : ArrayView[Int],
) -> Unit {
  if bytes.length() == 0 {
    return
  }
  let mut i = 0
  // Complete a partially-decoded UTF-8 code point carried from the previous
  // call.
  if self.partial_need > 0 {
    let mut completed = false
    while self.partial_need > 0 && i < bytes.length() {
      let b = bytes[i]
      if b >= 0x80 && b <= 0xbf {
        self.partial = (self.partial << 6) | (b & 0x3f)
        self.partial_need = self.partial_need - 1
        i = i + 1
        completed = self.partial_need == 0
      } else {
        // Not a continuation byte — abort the partial.
        self.partial_need = 0
        p.print('\u{FFFD}')
        break
      }
    }
    if completed {
      dispatch_cp(p, self.partial)
      self.partial = 0
    }
  }
  while i < bytes.length() {
    if self.state == ParseState::Ground {
      i = self.advance_ground(p, bytes, i)
    } else {
      i = self.advance_non_ground(p, bytes, i)
    }
  }
}

///|
/// Ground-state dispatch: printable runs, C0/C1 controls, ESC, and UTF-8.
fn[P : Perform] Parser::advance_ground(
  self : Parser,
  p : P,
  bytes : ArrayView[Int],
  i : Int,
) -> Int {
  let b = bytes[i]
  if b >= 0x20 && b <= 0x7e {
    if self.simd {
      let n = first_non_printable(bytes[i:])
      p.print_bytes(bytes[i:i + n])
      return i + n
    }
    let mut j = i
    while j < bytes.length() && bytes[j] >= 0x20 && bytes[j] < 0x7f {
      j = j + 1
    }
    p.print_bytes(bytes[i:j])
    j
  } else if b == 0x1b {
    self.clear_seq_state()
    self.state = ParseState::Escape
    i + 1
  } else if b == 0x7f {
    // DEL is a C0 control (destructive backspace), not printable.
    p.execute(0x7f)
    i + 1
  } else if b < 0x20 {
    p.execute(b)
    i + 1
  } else if b <= 0x9f {
    // Raw 8-bit C1 control byte.
    let (action, next) = c1_transition(b)
    self.apply(p, action, b)
    self.state = next
    i + 1
  } else {
    // Multi-byte UTF-8 (lead byte >= 0xA0).
    self.decode_utf8(p, bytes, i)
  }
}

///| Decode one multi-byte UTF-8 code point starting at `bytes[i]` (a lead

///| byte `>= 0xA0`), following W3C "Substitution of Maximal Subparts".

///|
/// Returns the number of bytes consumed.
fn[P : Perform] Parser::decode_utf8(
  self : Parser,
  p : P,
  bytes : ArrayView[Int],
  i : Int,
) -> Int {
  let lead = bytes[i]
  let expected = utf8_expected(lead)
  if expected == 0 {
    // Invalid lead (lone continuation 0xA0..=0xBF, C0/C1, or F5..=FF).
    p.print('\u{FFFD}')
    i + 1
  } else {
    let mut have = 1
    let mut cp = utf8_lead_cp(lead)
    let mut j = i + 1
    let mut valid = true
    while have < expected && j < bytes.length() {
      let b = bytes[j]
      if b >= 0x80 && b <= 0xbf {
        if have == 1 && !utf8_second_byte_valid(lead, b) {
          valid = false
          break
        }
        cp = (cp << 6) | (b & 0x3f)
        have = have + 1
        j = j + 1
      } else {
        break
      }
    }
    if valid && have == expected {
      dispatch_cp(p, cp)
      j
    } else if valid && j >= bytes.length() {
      // Trailing partial: carry it to the next `advance` call.
      self.partial = cp
      self.partial_need = expected - have
      j
    } else {
      // Truncated / ill-formed maximal subpart → one U+FFFD.
      p.print('\u{FFFD}')
      i + have
    }
  }
}

///| Non-ground dispatch: the cold transition-table path plus param and

///|
/// intermediate accumulation.
fn[P : Perform] Parser::advance_non_ground(
  self : Parser,
  p : P,
  bytes : ArrayView[Int],
  i : Int,
) -> Int {
  let b = bytes[i]
  let state = self.state
  if b == 0x1b {
    // ESC aborts the current sequence and starts a new one fresh.
    self.clear_seq_state()
  }
  if b >= 0x80 && b <= 0x9f {
    // 8-bit C1: DEC's "anywhere" rule.
    self.clear_seq_state()
    let (action, next) = c1_in_sequence(state, b)
    self.apply(p, action, b)
    self.state = next
    return i + 1
  }
  // Parameter / intermediate accumulation.
  match state {
    ParseState::CsiEntry
    | ParseState::CsiParam
    | ParseState::CsiIntermediate
    | ParseState::DcsEntry
    | ParseState::DcsParam
    | ParseState::DcsIntermediate =>
      match b {
        0x30..=0x39 => {
          let v = self.param * 10 + (b - 0x30)
          self.param = if v > 65535 { 65535 } else { v }
          self.param_empty = false
        }
        0x3b => {
          self.push_param(self.param)
          self.param = 0
          self.param_empty = true
        }
        0x3a => {
          self.push_param(if self.param_empty { -1 } else { self.param })
          if self.params_len > 0 && self.params_len <= 16 {
            self.sep = self.sep | (1 << (self.params_len - 1))
          }
          self.param = 0
          self.param_empty = true
        }
        0x3c..=0x3f | 0x20..=0x2f => self.push_intermediate(b)
        _ => ()
      }
    ParseState::Escape | ParseState::EscapeIntermediate =>
      if b >= 0x20 && b <= 0x2f {
        self.push_intermediate(b)
      }
    _ => ()
  }
  let (action, next) = transition(state, b)
  self.apply(p, action, b)
  self.state = next
  i + 1
}

///| Apply a transition's actions to the sink, in DEC order (string

///|
/// terminations before the execute).
fn[P : Perform] Parser::apply(
  self : Parser,
  p : P,
  action : Int,
  byte : Int,
) -> Unit {
  if action == ACTION_NONE {
    return
  }
  if action_has(action, ACTION_PRINT) {
    p.print(cp_to_char(byte))
  }
  if action_has(action, ACTION_CSI_DISPATCH) {
    if !self.param_empty || self.params_len != 0 {
      self.push_param(if self.param_empty { -1 } else { self.param })
    }
    p.csi_dispatch(
      self.params[:self.params_len],
      self.sep,
      self.intermediates[:self.intermediates_len],
      byte,
    )
    self.clear_seq_state()
  }
  if action_has(action, ACTION_ESC_DISPATCH) {
    p.esc_dispatch(self.intermediates[:self.intermediates_len], byte)
    self.clear_seq_state()
  }
  if action_has(action, ACTION_HOOK) {
    if !self.param_empty || self.params_len != 0 {
      self.push_param(self.param)
    }
    p.hook(
      self.params[:self.params_len],
      self.intermediates[:self.intermediates_len],
      byte,
    )
    self.clear_seq_state()
  }
  if action_has(action, ACTION_PUT) {
    p.put(byte)
  }
  if action_has(action, ACTION_UNHOOK) {
    p.unhook()
  }
  if action_has(action, ACTION_OSC_START) {
    self.osc_reset()
  }
  if action_has(action, ACTION_OSC_PUT) {
    self.osc_push(byte)
  }
  if action_has(action, ACTION_OSC_END) {
    self.osc_end(p, byte == 0x07)
  }
  // EXECUTE last: a string terminator (CAN/SUB) also executes.
  if action_has(action, ACTION_EXECUTE) {
    p.execute(byte)
  }
}

///|
/// Reset CSI/DCS/ESC accumulation state.
fn Parser::clear_seq_state(self : Parser) -> Unit {
  self.params = []
  self.params_len = 0
  self.sep = 0
  self.param = 0
  self.param_empty = true
  self.intermediates = []
  self.intermediates_len = 0
  self.ignore = false
}

///|
/// Push an intermediate byte (private marker or 0x20..=0x2f), capping at two.
fn Parser::push_intermediate(self : Parser, b : Int) -> Unit {
  if self.intermediates_len < MAX_INTERMEDIATES {
    self.intermediates.push(b)
    self.intermediates_len = self.intermediates_len + 1
  } else {
    self.ignore = true
  }
}

///|
/// Push a CSI/DCS parameter, capping at `MAX_PARAMS`.
fn Parser::push_param(self : Parser, value : Int) -> Unit {
  if self.params_len < MAX_PARAMS {
    self.params.push(value)
    self.params_len = self.params_len + 1
  }
}

///|
/// Reset the OSC accumulation state.
fn Parser::osc_reset(self : Parser) -> Unit {
  self.osc = []
  self.osc_starts = []
}

///| Append an OSC payload byte, tracking `;` param boundaries (the separator

///|
/// itself is not buffered).
fn Parser::osc_push(self : Parser, b : Int) -> Unit {
  if b == 0x3b {
    if self.osc_starts.length() < MAX_OSC_PARAMS {
      self.osc_starts.push(self.osc.length())
    }
  } else {
    self.osc.push(b)
  }
}

///|
/// Dispatch the accumulated OSC params and reset.
fn[P : Perform] Parser::osc_end(self : Parser, p : P, bell : Bool) -> Unit {
  let n = self.osc_starts.length() + 1
  let params : Array[Array[Int]] = []
  for k = 0; k < n; k = k + 1 {
    let start = if k == 0 { 0 } else { self.osc_starts[k - 1] }
    let end = if k < self.osc_starts.length() {
      self.osc_starts[k]
    } else {
      self.osc.length()
    }
    params.push(self.osc[start:end].to_owned())
  }
  p.osc_dispatch(params, bell)
  self.osc_reset()
}

///|
/// Emit a decoded code point: C1 range keeps `execute` semantics.
fn[P : Perform] dispatch_cp(p : P, cp : Int) -> Unit {
  if cp >= 0x80 && cp <= 0x9f {
    p.execute(cp)
  } else {
    p.print(cp_to_char(cp))
  }
}