///|
let reset_style_sequence : Bytes = @vt.sgr1(0)

///|
let reset_foreground_sequence : Bytes = @vt.sgr1(39)

///|
let reset_background_sequence : Bytes = @vt.sgr1(49)

///|
fn clamp_byte_value(value : Int) -> Int {
  if value < 0 {
    0
  } else if value > 255 {
    255
  } else {
    value
  }
}

///|
fn basic_foreground_code(color : @color.BasicColor) -> Int {
  match color {
    Black => 30
    Red => 31
    Green => 32
    Yellow => 33
    Blue => 34
    Magenta => 35
    Cyan => 36
    White => 37
    BrightBlack => 90
    BrightRed => 91
    BrightGreen => 92
    BrightYellow => 93
    BrightBlue => 94
    BrightMagenta => 95
    BrightCyan => 96
    BrightWhite => 97
  }
}

///|
fn basic_background_code(color : @color.BasicColor) -> Int {
  match color {
    Black => 40
    Red => 41
    Green => 42
    Yellow => 43
    Blue => 44
    Magenta => 45
    Cyan => 46
    White => 47
    BrightBlack => 100
    BrightRed => 101
    BrightGreen => 102
    BrightYellow => 103
    BrightBlue => 104
    BrightMagenta => 105
    BrightCyan => 106
    BrightWhite => 107
  }
}

///|
fn foreground_sequence(color : @color.Color) -> Bytes {
  match color {
    Default => reset_foreground_sequence
    Basic(color) => @vt.sgr1(basic_foreground_code(color))
    Indexed(index) => @vt.sgr3(38, 5, clamp_byte_value(index))
    Rgb(r, g, b) =>
      @vt.sgr5(
        38,
        2,
        clamp_byte_value(r),
        clamp_byte_value(g),
        clamp_byte_value(b),
      )
  }
}

///|
fn background_sequence(color : @color.Color) -> Bytes {
  match color {
    Default => reset_background_sequence
    Basic(color) => @vt.sgr1(basic_background_code(color))
    Indexed(index) => @vt.sgr3(48, 5, clamp_byte_value(index))
    Rgb(r, g, b) =>
      @vt.sgr5(
        48,
        2,
        clamp_byte_value(r),
        clamp_byte_value(g),
        clamp_byte_value(b),
      )
  }
}

///|
/// Enter the alternate screen buffer.
pub async fn Tty::enter_alt_screen(self : Self) -> Unit {
  self.write(@vt.enter_alt_screen)
}

///|
/// Leave the alternate screen buffer.
pub async fn Tty::leave_alt_screen(self : Self) -> Unit {
  self.write(@vt.leave_alt_screen)
}

///|
/// Run `f` with the output in the alternate screen buffer.
pub async fn[T] Tty::with_alt_screen(self : Self, f : async () -> T) -> T {
  self.enter_alt_screen()
  try f() catch {
    error => {
      self.leave_alt_screen()
      raise error
    }
  } noraise {
    value => {
      self.leave_alt_screen()
      value
    }
  }
}

///|
/// Enable bracketed paste mode.
pub async fn Tty::enable_bracketed_paste(self : Self) -> Unit {
  self.write(@vt.enable_bracketed_paste)
}

///|
/// Disable bracketed paste mode.
pub async fn Tty::disable_bracketed_paste(self : Self) -> Unit {
  self.write(@vt.disable_bracketed_paste)
}

///|
/// Run `f` with bracketed paste mode enabled.
pub async fn[T] Tty::with_bracketed_paste(self : Self, f : async () -> T) -> T {
  self.enable_bracketed_paste()
  try f() catch {
    error => {
      self.disable_bracketed_paste()
      raise error
    }
  } noraise {
    value => {
      self.disable_bracketed_paste()
      value
    }
  }
}

///|
/// Enable focus tracking.
pub async fn Tty::enable_focus_tracking(self : Self) -> Unit {
  self.write(@vt.enable_focus_tracking)
}

///|
/// Disable focus tracking.
pub async fn Tty::disable_focus_tracking(self : Self) -> Unit {
  self.write(@vt.disable_focus_tracking)
}

///|
/// Enable DEC auto wrap mode (DECAWM).
pub async fn Tty::enable_auto_wrap(self : Self) -> Unit {
  self.write(@vt.enable_auto_wrap)
}

///|
/// Disable DEC auto wrap mode (DECAWM).
pub async fn Tty::disable_auto_wrap(self : Self) -> Unit {
  self.write(@vt.disable_auto_wrap)
}

///|
/// Run `f` with focus tracking enabled.
pub async fn[T] Tty::with_focus_tracking(self : Self, f : async () -> T) -> T {
  self.enable_focus_tracking()
  try f() catch {
    error => {
      self.disable_focus_tracking()
      raise error
    }
  } noraise {
    value => {
      self.disable_focus_tracking()
      value
    }
  }
}

///|
const KeyboardDisambiguateEscapeCodes : Int = 1

///|
const KeyboardReportEventTypes : Int = 2

///|
const KeyboardReportAlternateKeys : Int = 4

///|
const KeyboardReportAllKeysAsEscapeCodes : Int = 8

///|
const KeyboardReportAssociatedText : Int = 16

///|
/// Kitty keyboard protocol progressive enhancement flags.
struct KeyboardEnhancementFlags(Int) derive(Eq)

///|
/// Construct kitty keyboard protocol progressive enhancement flags.
pub fn KeyboardEnhancementFlags::new(
  disambiguate_escape_codes? : Bool = false,
  report_event_types? : Bool = false,
  report_alternate_keys? : Bool = false,
  report_all_keys_as_escape_codes? : Bool = false,
  report_associated_text? : Bool = false,
) -> KeyboardEnhancementFlags {
  let mut bits = 0
  if disambiguate_escape_codes {
    bits = bits | KeyboardDisambiguateEscapeCodes
  }
  if report_event_types {
    bits = bits | KeyboardReportEventTypes
  }
  if report_alternate_keys {
    bits = bits | KeyboardReportAlternateKeys
  }
  if report_all_keys_as_escape_codes {
    bits = bits | KeyboardReportAllKeysAsEscapeCodes
  }
  if report_associated_text {
    bits = bits | KeyboardReportAssociatedText
  }
  KeyboardEnhancementFlags(bits)
}

///|
/// Enhancement flags for unambiguous key reports.
pub fn KeyboardEnhancementFlags::disambiguate() -> KeyboardEnhancementFlags {
  KeyboardEnhancementFlags::new(disambiguate_escape_codes=true)
}

///|
/// Enhancement flags for full kitty keyboard protocol reports.
pub fn KeyboardEnhancementFlags::full() -> KeyboardEnhancementFlags {
  KeyboardEnhancementFlags::new(
    disambiguate_escape_codes=true,
    report_event_types=true,
    report_alternate_keys=true,
    report_all_keys_as_escape_codes=true,
    report_associated_text=true,
  )
}

///|
/// Return the raw kitty protocol bitset.
pub fn KeyboardEnhancementFlags::bits(self : KeyboardEnhancementFlags) -> Int {
  self.0
}

///|
/// Return whether disambiguated escape-code reports are requested.
pub fn KeyboardEnhancementFlags::disambiguate_escape_codes(
  self : KeyboardEnhancementFlags,
) -> Bool {
  (self.0 & KeyboardDisambiguateEscapeCodes) != 0
}

///|
/// Return whether key repeat and release reports are requested.
pub fn KeyboardEnhancementFlags::report_event_types(
  self : KeyboardEnhancementFlags,
) -> Bool {
  (self.0 & KeyboardReportEventTypes) != 0
}

///|
/// Return whether alternate key-code reports are requested.
pub fn KeyboardEnhancementFlags::report_alternate_keys(
  self : KeyboardEnhancementFlags,
) -> Bool {
  (self.0 & KeyboardReportAlternateKeys) != 0
}

///|
/// Return whether all keys should be reported as escape codes.
pub fn KeyboardEnhancementFlags::report_all_keys_as_escape_codes(
  self : KeyboardEnhancementFlags,
) -> Bool {
  (self.0 & KeyboardReportAllKeysAsEscapeCodes) != 0
}

///|
/// Return whether associated text reports are requested.
pub fn KeyboardEnhancementFlags::report_associated_text(
  self : KeyboardEnhancementFlags,
) -> Bool {
  (self.0 & KeyboardReportAssociatedText) != 0
}

///|
/// Push kitty keyboard protocol progressive enhancement flags.
pub async fn Tty::push_keyboard_enhancement_flags(
  self : Self,
  flags : KeyboardEnhancementFlags,
) -> Unit {
  self.write(@vt.push_keyboard_enhancement_flags(flags.bits()))
}

///|
/// Pop one kitty keyboard protocol progressive enhancement flag stack entry.
pub async fn Tty::pop_keyboard_enhancement_flags(self : Self) -> Unit {
  self.write(@vt.pop_keyboard_enhancement_flags)
}

///|
/// Run `f` with kitty keyboard protocol enhancements enabled.
pub async fn[T] Tty::with_keyboard_enhancements(
  self : Self,
  flags : KeyboardEnhancementFlags,
  f : async () -> T,
) -> T {
  self.push_keyboard_enhancement_flags(flags)
  try f() catch {
    error => {
      self.pop_keyboard_enhancement_flags()
      raise error
    }
  } noraise {
    value => {
      self.pop_keyboard_enhancement_flags()
      value
    }
  }
}

///|
/// Run `f` with full kitty keyboard protocol reports enabled.
pub async fn[T] Tty::with_kitty_keyboard(self : Self, f : async () -> T) -> T {
  self.with_keyboard_enhancements(KeyboardEnhancementFlags::full(), f)
}

///|
/// Mouse tracking scope for SGR mouse reports.
pub(all) enum MouseTrackingMode {
  Click
  Drag
  Motion
} derive(Eq)

///|
/// Enable SGR mouse tracking.
pub async fn Tty::enable_mouse(self : Self, mode : MouseTrackingMode) -> Unit {
  self.write(@vt.disable_mouse_motion)
  self.write(@vt.disable_mouse_drag)
  self.write(@vt.disable_mouse_click)
  self.write(@vt.enable_sgr_mouse)
  match mode {
    Click => self.write(@vt.enable_mouse_click)
    Drag => self.write(@vt.enable_mouse_drag)
    Motion => self.write(@vt.enable_mouse_motion)
  }
}

///|
/// Disable SGR mouse tracking.
pub async fn Tty::disable_mouse(self : Self) -> Unit {
  self.write(@vt.disable_mouse_motion)
  self.write(@vt.disable_mouse_drag)
  self.write(@vt.disable_mouse_click)
  self.write(@vt.disable_sgr_mouse)
}

///|
/// Run `f` with SGR mouse tracking enabled.
pub async fn[T] Tty::with_mouse(
  self : Self,
  mode : MouseTrackingMode,
  f : async () -> T,
) -> T {
  self.enable_mouse(mode)
  try f() catch {
    error => {
      self.disable_mouse()
      raise error
    }
  } noraise {
    value => {
      self.disable_mouse()
      value
    }
  }
}

///|
/// Hide the terminal cursor.
pub async fn Tty::hide_cursor(self : Self) -> Unit {
  self.write(@vt.hide_cursor)
}

///|
/// Show the terminal cursor.
pub async fn Tty::show_cursor(self : Self) -> Unit {
  self.write(@vt.show_cursor)
}

///|
/// Move the terminal cursor to a 1-based row and column.
pub async fn Tty::set_cursor_position(
  self : Self,
  row : Int,
  col : Int,
) -> Unit {
  self.write(@vt.cursor_position(row, col))
}

///|
/// Cursor Up (CUU), ECMA-48.
pub async fn Tty::cursor_up(self : Self, n : Int) -> Unit {
  self.write(@vt.cursor_up(n))
}

///|
/// Cursor Forward (CUF), ECMA-48.
pub async fn Tty::cursor_forward(self : Self, n : Int) -> Unit {
  self.write(@vt.cursor_forward(n))
}

///|
/// Cursor Back (CUB), ECMA-48.
pub async fn Tty::cursor_back(self : Self, n : Int) -> Unit {
  self.write(@vt.cursor_back(n))
}

///|
/// Erase the entire current line (EL 2), ECMA-48.
pub async fn Tty::erase_line_all(self : Self) -> Unit {
  self.write(@vt.erase_line_all)
}

///|
/// Erase the visible display (ED 2), ECMA-48.
pub async fn Tty::erase_display(self : Self) -> Unit {
  self.write(@vt.erase_display)
}

///|
/// Erase saved lines in the terminal scrollback (ED 3), xterm extension.
pub async fn Tty::erase_scrollback(self : Self) -> Unit {
  self.write(@vt.erase_scrollback)
}

///|
/// Set Top and Bottom Margins (DECSTBM).
pub async fn Tty::set_top_bottom_margins(
  self : Self,
  top : Int,
  bottom : Int,
) -> Unit {
  self.write(@vt.set_top_bottom_margins(top, bottom))
}

///|
/// Reset Top and Bottom Margins (DECSTBM).
pub async fn Tty::reset_top_bottom_margins(self : Self) -> Unit {
  self.write(@vt.reset_top_bottom_margins)
}

///|
/// Reverse Index (RI), ECMA-48.
pub async fn Tty::reverse_index(self : Self) -> Unit {
  self.write(@vt.reverse_index)
}

///|
/// Set the foreground color.
pub async fn Tty::set_foreground(self : Self, color : @color.Color) -> Unit {
  self.write(foreground_sequence(color))
}

///|
/// Set the background color.
pub async fn Tty::set_background(self : Self, color : @color.Color) -> Unit {
  self.write(background_sequence(color))
}

///|
/// Reset the foreground color to the terminal default.
pub async fn Tty::reset_foreground(self : Self) -> Unit {
  self.write(reset_foreground_sequence)
}

///|
/// Reset the background color to the terminal default.
pub async fn Tty::reset_background(self : Self) -> Unit {
  self.write(reset_background_sequence)
}

///|
/// Set bold or increased intensity.
pub async fn Tty::bold(self : Self) -> Unit {
  self.write(@vt.bold)
}

///|
/// Reset bold by restoring normal intensity.
pub async fn Tty::reset_bold(self : Self) -> Unit {
  self.write(@vt.reset_bold)
}

///|
/// Set italic text.
pub async fn Tty::italic(self : Self) -> Unit {
  self.write(@vt.italic)
}

///|
/// Reset italic text.
pub async fn Tty::reset_italic(self : Self) -> Unit {
  self.write(@vt.reset_italic)
}

///|
/// Set underlined text.
pub async fn Tty::underline(self : Self) -> Unit {
  self.write(@vt.underline)
}

///|
/// Reset underlined text.
pub async fn Tty::reset_underline(self : Self) -> Unit {
  self.write(@vt.reset_underline)
}

///|
/// Set reverse video.
pub async fn Tty::reverse(self : Self) -> Unit {
  self.write(@vt.reverse)
}

///|
/// Reset reverse video.
pub async fn Tty::reset_reverse(self : Self) -> Unit {
  self.write(@vt.reset_reverse)
}

///|
/// Reset all SGR style attributes.
pub async fn Tty::reset_style(self : Self) -> Unit {
  self.write(reset_style_sequence)
}