///|
/// Input parsing for TUI applications
/// Parses terminal escape sequences into structured events

///|
/// Parse a single byte as a character
fn is_printable(c : Char) -> Bool {
  let code = c.to_int()
  // ASCII printable or Unicode characters (above 127)
  (code >= 32 && code < 127) || code >= 128
}

///|
/// Parse escape sequence
pub fn parse_input(input : String) -> InputEvent {
  if input.length() == 0 {
    return InputEvent::Unknown("")
  }
  let chars = input.to_array()
  if chars.length() == 1 {
    let c = chars[0]
    let code = c.to_int()
    // Control characters
    if code == 27 {
      return InputEvent::Key(
        KeyEvent::Special(SpecialKey::Escape, KeyModifier::None),
      )
    }
    if code == 13 || code == 10 {
      return InputEvent::Key(
        KeyEvent::Special(SpecialKey::Enter, KeyModifier::None),
      )
    }
    if code == 9 {
      return InputEvent::Key(
        KeyEvent::Special(SpecialKey::Tab, KeyModifier::None),
      )
    }
    if code == 127 {
      return InputEvent::Key(
        KeyEvent::Special(SpecialKey::Backspace, KeyModifier::None),
      )
    }
    // Ctrl+letter (1-26 except 9, 10, 13)
    if code >= 1 && code <= 26 {
      let letter = (code + 96).unsafe_to_char() // 'a' = 97
      return InputEvent::Key(KeyEvent::Char(letter, KeyModifier::Ctrl))
    }
    // Printable characters
    if is_printable(c) {
      return InputEvent::Key(KeyEvent::Char(c, KeyModifier::None))
    }
    return InputEvent::Unknown(input)
  }

  // Escape sequences
  if chars[0] == '\u001b' {
    if chars.length() >= 2 && chars[1] == '[' {
      return parse_csi_sequence(chars)
    }
    if chars.length() >= 2 && chars[1] == 'O' {
      return parse_ss3_sequence(chars)
    }
    // Alt+key
    if chars.length() == 2 {
      let c = chars[1]
      if is_printable(c) {
        return InputEvent::Key(KeyEvent::Char(c, KeyModifier::Alt))
      }
    }
  }

  // Multi-character printable input (e.g., pasted text or unicode)
  // Return the first character as a key event
  if chars.length() > 0 && is_printable(chars[0]) {
    return InputEvent::Key(KeyEvent::Char(chars[0], KeyModifier::None))
  }
  InputEvent::Unknown(input)
}

///|
/// Parse CSI (Control Sequence Introducer) sequence
fn parse_csi_sequence(chars : Array[Char]) -> InputEvent {
  // Skip ESC[
  let len = chars.length()
  if len < 3 {
    return InputEvent::Unknown(String::from_array(chars))
  }
  let final_char = chars[len - 1]

  // Check for SGR mouse format first (ESC[<...M or ESC[<...m)
  let is_sgr = len >= 4 && chars[2] == '<'
  if is_sgr {
    if final_char == 'M' {
      return parse_mouse_sgr(chars, MouseEventType::Press)
    } else if final_char == 'm' {
      return parse_mouse_sgr(chars, MouseEventType::Release)
    }
  }

  // Simple arrow keys and navigation
  match final_char {
    'A' => InputEvent::Key(KeyEvent::Special(SpecialKey::Up, KeyModifier::None))
    'B' =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::Down, KeyModifier::None))
    'C' =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::Right, KeyModifier::None))
    'D' =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::Left, KeyModifier::None))
    'H' =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::Home, KeyModifier::None))
    'F' =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::End, KeyModifier::None))
    'Z' =>
      InputEvent::Key(
        KeyEvent::Special(SpecialKey::BackTab, KeyModifier::Shift),
      )
    '~' => parse_tilde_sequence(chars)
    'M' => parse_mouse_x10(chars)
    'm' => parse_mouse_sgr(chars, MouseEventType::Release)
    _ => InputEvent::Unknown(String::from_array(chars))
  }
}

///|
/// Parse tilde sequences (Insert, Delete, PgUp, PgDn, F5-F12)
fn parse_tilde_sequence(chars : Array[Char]) -> InputEvent {
  let params = extract_csi_params(chars)
  if params.length() == 0 {
    return InputEvent::Unknown(String::from_array(chars))
  }
  let num = params[0]
  match num {
    1 => InputEvent::Key(KeyEvent::Special(SpecialKey::Home, KeyModifier::None))
    2 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::Insert, KeyModifier::None))
    3 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::Delete, KeyModifier::None))
    4 => InputEvent::Key(KeyEvent::Special(SpecialKey::End, KeyModifier::None))
    5 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::PageUp, KeyModifier::None))
    6 =>
      InputEvent::Key(
        KeyEvent::Special(SpecialKey::PageDown, KeyModifier::None),
      )
    7 => InputEvent::Key(KeyEvent::Special(SpecialKey::Home, KeyModifier::None))
    8 => InputEvent::Key(KeyEvent::Special(SpecialKey::End, KeyModifier::None))
    15 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(5), KeyModifier::None))
    17 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(6), KeyModifier::None))
    18 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(7), KeyModifier::None))
    19 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(8), KeyModifier::None))
    20 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(9), KeyModifier::None))
    21 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(10), KeyModifier::None))
    23 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(11), KeyModifier::None))
    24 =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(12), KeyModifier::None))
    _ => InputEvent::Unknown(String::from_array(chars))
  }
}

///|
/// Parse SS3 (Single Shift 3) sequence for F1-F4
fn parse_ss3_sequence(chars : Array[Char]) -> InputEvent {
  if chars.length() < 3 {
    return InputEvent::Unknown(String::from_array(chars))
  }
  match chars[2] {
    'P' =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(1), KeyModifier::None))
    'Q' =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(2), KeyModifier::None))
    'R' =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(3), KeyModifier::None))
    'S' =>
      InputEvent::Key(KeyEvent::Special(SpecialKey::F(4), KeyModifier::None))
    _ => InputEvent::Unknown(String::from_array(chars))
  }
}

///|
/// Extract numeric parameters from CSI sequence
fn extract_csi_params(chars : Array[Char]) -> Array[Int] {
  let params : Array[Int] = []
  let mut num = 0
  let mut has_num = false
  // Skip ESC[
  for i = 2; i < chars.length() - 1; i = i + 1 {
    let c = chars[i]
    let code = c.to_int()
    if code >= 48 && code <= 57 {
      // '0'-'9'
      num = num * 10 + (code - 48)
      has_num = true
    } else if c == ';' {
      if has_num {
        params.push(num)
      }
      num = 0
      has_num = false
    }
  }
  if has_num {
    params.push(num)
  }
  params
}

///|
/// Parse X10 mouse format (ESC[M followed by 3 bytes)
fn parse_mouse_x10(chars : Array[Char]) -> InputEvent {
  if chars.length() < 6 {
    return InputEvent::Unknown(String::from_array(chars))
  }
  let cb = chars[3].to_int() - 32
  let cx = chars[4].to_int() - 32
  let cy = chars[5].to_int() - 32
  let button_code = cb & 3
  let button = match button_code {
    0 => MouseButton::Left
    1 => MouseButton::Middle
    2 => MouseButton::Right
    3 => MouseButton::None
    _ => MouseButton::None
  }
  let has_bit_64 = (cb & 64) != 0
  let has_bit_32 = (cb & 32) != 0
  let event_type = if has_bit_64 {
    if button_code == 0 {
      MouseEventType::Move // Scroll up
    } else {
      MouseEventType::Move // Scroll down
    }
  } else if has_bit_32 {
    MouseEventType::Drag
  } else if button is MouseButton::None {
    MouseEventType::Release
  } else {
    MouseEventType::Press
  }
  let modifier = parse_mouse_modifier(cb)
  InputEvent::Mouse({ button, event_type, x: cx, y: cy, modifier })
}

///|
/// Parse SGR mouse format (ESC[ InputEvent {
  // Find '<' to confirm SGR format
  let mut start = 0
  for i = 0; i < chars.length(); i = i + 1 {
    if chars[i] == '<' {
      start = i + 1
      break
    }
  }
  if start == 0 {
    return InputEvent::Unknown(String::from_array(chars))
  }
  let params = extract_sgr_params(chars, start)
  if params.length() < 3 {
    return InputEvent::Unknown(String::from_array(chars))
  }
  let cb = params[0]
  let cx = params[1]
  let cy = params[2]
  let has_bit_64 = (cb & 64) != 0
  let has_bit_32 = (cb & 32) != 0
  let button_code = cb & 3
  let button = if has_bit_64 {
    if (cb & 1) != 0 {
      MouseButton::ScrollDown
    } else {
      MouseButton::ScrollUp
    }
  } else {
    match button_code {
      0 => MouseButton::Left
      1 => MouseButton::Middle
      2 => MouseButton::Right
      _ => MouseButton::None
    }
  }
  let actual_event_type = if has_bit_32 {
    MouseEventType::Drag
  } else {
    event_type
  }
  let modifier = parse_mouse_modifier(cb)
  InputEvent::Mouse({
    button,
    event_type: actual_event_type,
    x: cx,
    y: cy,
    modifier,
  })
}

///|
/// Extract SGR parameters
fn extract_sgr_params(chars : Array[Char], start : Int) -> Array[Int] {
  let params : Array[Int] = []
  let mut num = 0
  let mut has_num = false
  for i = start; i < chars.length() - 1; i = i + 1 {
    let c = chars[i]
    let code = c.to_int()
    if code >= 48 && code <= 57 {
      num = num * 10 + (code - 48)
      has_num = true
    } else if c == ';' {
      if has_num {
        params.push(num)
      }
      num = 0
      has_num = false
    }
  }
  if has_num {
    params.push(num)
  }
  params
}

///|
/// Parse mouse modifier from button code
fn parse_mouse_modifier(cb : Int) -> KeyModifier {
  let shift = (cb & 4) != 0
  let alt = (cb & 8) != 0
  let ctrl = (cb & 16) != 0
  if ctrl && shift && alt {
    KeyModifier::CtrlShiftAlt
  } else if ctrl && shift {
    KeyModifier::CtrlShift
  } else if ctrl && alt {
    KeyModifier::CtrlAlt
  } else if shift && alt {
    KeyModifier::ShiftAlt
  } else if ctrl {
    KeyModifier::Ctrl
  } else if shift {
    KeyModifier::Shift
  } else if alt {
    KeyModifier::Alt
  } else {
    KeyModifier::None
  }
}

///|
/// Check if key matches a specific character with optional modifier
pub fn KeyEvent::is_char(self : KeyEvent, c : Char) -> Bool {
  match self {
    KeyEvent::Char(ch, KeyModifier::None) => ch == c
    _ => false
  }
}

///|
/// Check if key matches a special key
pub fn KeyEvent::is_special(self : KeyEvent, special : SpecialKey) -> Bool {
  match self {
    KeyEvent::Special(s, KeyModifier::None) =>
      match (s, special) {
        (SpecialKey::Up, SpecialKey::Up) => true
        (SpecialKey::Down, SpecialKey::Down) => true
        (SpecialKey::Left, SpecialKey::Left) => true
        (SpecialKey::Right, SpecialKey::Right) => true
        (SpecialKey::Home, SpecialKey::Home) => true
        (SpecialKey::End, SpecialKey::End) => true
        (SpecialKey::PageUp, SpecialKey::PageUp) => true
        (SpecialKey::PageDown, SpecialKey::PageDown) => true
        (SpecialKey::Insert, SpecialKey::Insert) => true
        (SpecialKey::Delete, SpecialKey::Delete) => true
        (SpecialKey::Tab, SpecialKey::Tab) => true
        (SpecialKey::BackTab, SpecialKey::BackTab) => true
        (SpecialKey::Backspace, SpecialKey::Backspace) => true
        (SpecialKey::Enter, SpecialKey::Enter) => true
        (SpecialKey::Escape, SpecialKey::Escape) => true
        (SpecialKey::F(n1), SpecialKey::F(n2)) => n1 == n2
        _ => false
      }
    _ => false
  }
}

///|
/// Check if key is Ctrl+C
pub fn KeyEvent::is_ctrl_c(self : KeyEvent) -> Bool {
  match self {
    KeyEvent::Char('c', KeyModifier::Ctrl) => true
    _ => false
  }
}

///|
/// Check if key is Ctrl+Q (common quit)
pub fn KeyEvent::is_ctrl_q(self : KeyEvent) -> Bool {
  match self {
    KeyEvent::Char('q', KeyModifier::Ctrl) => true
    _ => false
  }
}

///|
// InputEvent helpers - work directly with parsed events

///|
/// Check if the input event is a key matching a character (no modifier)
pub fn InputEvent::is_char(self : InputEvent, c : Char) -> Bool {
  match self {
    InputEvent::Key(KeyEvent::Char(ch, KeyModifier::None)) => ch == c
    _ => false
  }
}

///|
/// Check if the input event is a key with Ctrl modifier
pub fn InputEvent::is_ctrl(self : InputEvent, c : Char) -> Bool {
  match self {
    InputEvent::Key(KeyEvent::Char(ch, KeyModifier::Ctrl)) => ch == c
    _ => false
  }
}

///|
/// Check if the input event is Ctrl+C (quit)
pub fn InputEvent::is_ctrl_c(self : InputEvent) -> Bool {
  self.is_ctrl('c')
}

///|
/// Check if the input event is Escape
pub fn InputEvent::is_escape(self : InputEvent) -> Bool {
  match self {
    InputEvent::Key(KeyEvent::Special(SpecialKey::Escape, _)) => true
    _ => false
  }
}

///|
/// Check if the input event is Tab
pub fn InputEvent::is_tab(self : InputEvent) -> Bool {
  match self {
    InputEvent::Key(KeyEvent::Special(SpecialKey::Tab, KeyModifier::None)) =>
      true
    _ => false
  }
}

///|
/// Check if the input event is Shift+Tab or BackTab
pub fn InputEvent::is_backtab(self : InputEvent) -> Bool {
  match self {
    InputEvent::Key(
      KeyEvent::Special(
        SpecialKey::Tab
        | SpecialKey::BackTab,
        KeyModifier::Shift
      )
    ) => true
    InputEvent::Key(KeyEvent::Special(SpecialKey::BackTab, KeyModifier::None)) =>
      true
    _ => false
  }
}

///|
/// Check if the input event is Enter
pub fn InputEvent::is_enter(self : InputEvent) -> Bool {
  match self {
    InputEvent::Key(KeyEvent::Special(SpecialKey::Enter, _)) => true
    _ => false
  }
}

///|
/// Check if the input event is an arrow key, returns the direction
pub fn InputEvent::is_arrow(self : InputEvent) -> SpecialKey? {
  match self {
    InputEvent::Key(KeyEvent::Special(SpecialKey::Up as k, _)) => Some(k)
    InputEvent::Key(KeyEvent::Special(SpecialKey::Down as k, _)) => Some(k)
    InputEvent::Key(KeyEvent::Special(SpecialKey::Left as k, _)) => Some(k)
    InputEvent::Key(KeyEvent::Special(SpecialKey::Right as k, _)) => Some(k)
    _ => None
  }
}

///|
/// Check if the input event is a common quit key (q, Ctrl+C, Escape)
pub fn InputEvent::is_quit(self : InputEvent) -> Bool {
  self.is_char('q') || self.is_ctrl_c() || self.is_escape()
}

///|
// MouseEvent helpers

///|
/// Check if the input event is a left mouse button press
pub fn InputEvent::is_left_click(self : InputEvent) -> MouseEvent? {
  match self {
    InputEvent::Mouse(
      { button: MouseButton::Left, event_type: MouseEventType::Press, .. } as m
    ) => Some(m)
    _ => None
  }
}

///|
/// Check if the input event is a right mouse button press
pub fn InputEvent::is_right_click(self : InputEvent) -> MouseEvent? {
  match self {
    InputEvent::Mouse(
      { button: MouseButton::Right, event_type: MouseEventType::Press, .. } as m
    ) => Some(m)
    _ => None
  }
}

///|
/// Check if the input event is a scroll up
pub fn InputEvent::is_scroll_up(self : InputEvent) -> Bool {
  match self {
    InputEvent::Mouse({ button: MouseButton::ScrollUp, .. }) => true
    _ => false
  }
}

///|
/// Check if the input event is a scroll down
pub fn InputEvent::is_scroll_down(self : InputEvent) -> Bool {
  match self {
    InputEvent::Mouse({ button: MouseButton::ScrollDown, .. }) => true
    _ => false
  }
}