///|
/// 终端屏幕模型:可视区 + 回看缓冲 + 光标 + 滚动区域 + 脏行增量。
/// 只有单元格内容变化才标脏;光标移动、SGR 属性与滚动区域设置不算画面变化,
/// 因此呈现器按 `take_damage` 输出即可覆盖残影,又不会重绘未变的行。
pub struct TermScreen {
  priv mut cols : Int
  priv mut rows : Int
  priv screen : Array[TermRow]
  priv mut scrollback : Array[TermRow]
  priv mut scrollback_limit : Int
  priv mut cursor_row : Int
  priv mut cursor_col : Int
  priv mut scroll_top : Int
  priv mut scroll_bottom : Int
  priv mut saved_cursor_row : Int
  priv mut saved_cursor_col : Int
  priv mut wrap_pending : Bool
  priv mut auto_wrap : Bool
  priv mut cursor_visible : Bool
  priv mut title : String
  priv mut attrs : CellAttrs
  priv mut dirty_rows : Array[Bool]
  priv mut dirty_all : Bool
  priv mut revision : Int
}

///|
fn dirty_mask(rows : Int) -> Array[Bool] {
  let mask : Array[Bool] = []
  for _ in 0.. TermScreen {
  let attrs = CellAttrs::default()
  let screen : Array[TermRow] = []
  for _ in 0.. Unit {
  if self.dirty_all || y < 0 || y >= self.dirty_rows.length() {
    return
  }
  if !self.dirty_rows[y] {
    self.dirty_rows[y] = true
    self.revision = self.revision + 1
  }
}

///|
/// 标脏 [from, to) 行。
fn TermScreen::mark_rows(self : TermScreen, from : Int, to : Int) -> Unit {
  for y in from.. Unit {
  if !self.dirty_all {
    self.dirty_all = true
    self.revision = self.revision + 1
    self.dirty_rows = dirty_mask(self.rows)
  }
}

///|
/// 写入单个格:内容与属性都没变时不标脏。
/// 投影器每帧重画整棵语义树,若同内容写入也算变化,damage 通路就永远非空,
/// 增量呈现省下的重绘就不存在了。
fn TermScreen::write_cell(
  self : TermScreen,
  y : Int,
  x : Int,
  cell : Cell,
) -> Unit {
  let row = self.screen[y]
  if row.cells[x] == cell {
    return
  }
  self.mark_row(y)
  row.cells[x] = cell
}

///|
/// 缓冲区代数:任何内容变化都会递增,呈现器用它跳过无变化的帧。
pub fn TermScreen::revision(self : TermScreen) -> Int {
  self.revision
}

///|
/// 取出并清零脏区:整屏重建,或升序去重的脏行。
pub fn TermScreen::take_damage(self : TermScreen) -> Damage {
  let rows : Array[Int] = []
  if !self.dirty_all {
    for y, dirty in self.dirty_rows {
      if dirty {
        rows.push(y)
      }
    }
  }
  let damage : Damage = { full: self.dirty_all, rows, }
  self.dirty_all = false
  self.dirty_rows = dirty_mask(self.rows)
  damage
}

///|
pub fn TermScreen::cols(self : TermScreen) -> Int {
  self.cols
}

///|
pub fn TermScreen::rows(self : TermScreen) -> Int {
  self.rows
}

///|
pub fn TermScreen::cursor_row(self : TermScreen) -> Int {
  self.cursor_row
}

///|
pub fn TermScreen::cursor_col(self : TermScreen) -> Int {
  self.cursor_col
}

///|
pub fn TermScreen::title(self : TermScreen) -> String {
  self.title
}

///|
pub fn TermScreen::current_attrs(self : TermScreen) -> CellAttrs {
  self.attrs
}

///|
pub fn TermScreen::cursor_visible(self : TermScreen) -> Bool {
  self.cursor_visible
}

///|
pub fn TermScreen::set_cursor_visible(self : TermScreen, v : Bool) -> Unit {
  self.cursor_visible = v
}

///|
/// DECAWM:关闭自动换行后,超宽字符停在最后一列而不是滚到新行。
pub fn TermScreen::set_auto_wrap(self : TermScreen, v : Bool) -> Unit {
  self.auto_wrap = v
}

///|
/// 回看行数 + 可视行数。
pub fn TermScreen::total_lines(self : TermScreen) -> Int {
  self.scrollback.length() + self.rows
}

///|
/// 取第 idx 行(0 = 最早的回看行;total_lines - rows 起为可视区)。
pub fn TermScreen::line_at(self : TermScreen, idx : Int) -> TermRow {
  let sb = self.scrollback.length()
  if idx < sb {
    self.scrollback[idx]
  } else {
    self.screen[idx - sb]
  }
}

///|
pub fn TermScreen::set_scrollback_limit(self : TermScreen, limit : Int) -> Unit {
  self.scrollback_limit = limit
  while self.scrollback.length() > limit {
    ignore(self.scrollback.pop())
  }
}

///|
pub fn TermScreen::set_current_attrs(
  self : TermScreen,
  attrs : CellAttrs,
) -> Unit {
  self.attrs = attrs
}

///|
/// 重置到初始状态(保留尺寸)。
pub fn TermScreen::reset(self : TermScreen) -> Unit {
  self.clear_all()
  self.cursor_row = 0
  self.cursor_col = 0
  self.scroll_top = 0
  self.scroll_bottom = self.rows - 1
  self.saved_cursor_row = 0
  self.saved_cursor_col = 0
  self.wrap_pending = false
  self.auto_wrap = true
  self.cursor_visible = true
  self.attrs = CellAttrs::default()
  self.title = ""
}

///|
/// 清空屏幕与回看缓冲。
pub fn TermScreen::clear_all(self : TermScreen) -> Unit {
  self.mark_all()
  for i in 0.. TermRow {
  TermRow::new(self.cols, self.attrs)
}

///|
fn TermScreen::push_scrollback(self : TermScreen, row : TermRow) -> Unit {
  self.scrollback.push(row)
  while self.scrollback.length() > self.scrollback_limit {
    ignore(self.scrollback.remove(0))
  }
}

///|
fn TermScreen::prepend_scrollback(self : TermScreen, row : TermRow) -> Unit {
  self.scrollback.insert(0, row)
  while self.scrollback.length() > self.scrollback_limit {
    ignore(self.scrollback.pop())
  }
}

///|
/// 光标处写入字符;处理自动换行与宽字符(右半格填 continuation)。
pub fn TermScreen::print_char(
  self : TermScreen,
  ch : String,
  width : Int,
) -> Unit {
  if self.wrap_pending && self.auto_wrap {
    self.carriage_return()
    self.linefeed()
  }
  self.wrap_pending = false
  if width == 2 && self.cursor_col >= self.cols - 1 {
    // 宽字符放不下最后一格:丢弃(与 xterm 一致)
    return
  }
  self.write_cell(self.cursor_row, self.cursor_col, {
    ch,
    width,
    attrs: self.attrs,
  })
  if width == 2 {
    self.write_cell(
      self.cursor_row,
      self.cursor_col + 1,
      continuation_cell(self.attrs),
    )
  }
  if self.cursor_col + width >= self.cols {
    if self.auto_wrap {
      self.wrap_pending = true
    }
  } else {
    self.cursor_col = self.cursor_col + width
  }
}

///|
/// 直接写入第 y 行第 x 列,不移动光标 —— 投影器按量化盒定位单元格。
/// 越界或宽字符放不下时返回 false,调用方据此记降级诊断。
pub fn TermScreen::put(
  self : TermScreen,
  y : Int,
  x : Int,
  ch : String,
  width : Int,
  attrs : CellAttrs,
) -> Bool {
  if y < 0 || y >= self.rows || x < 0 || x >= self.cols {
    return false
  }
  if width == 2 && x >= self.cols - 1 {
    return false
  }
  self.write_cell(y, x, { ch, width, attrs, })
  if width == 2 {
    self.write_cell(y, x + 1, continuation_cell(attrs))
  }
  true
}

///|
/// 把第 y 行的 [from, to) 列擦成空白(越界裁剪),保持 attrs 的底色。
pub fn TermScreen::erase_cells(
  self : TermScreen,
  y : Int,
  from : Int,
  to : Int,
  attrs : CellAttrs,
) -> Unit {
  if y < 0 || y >= self.rows {
    return
  }
  let start = from.clamp(min=0, max=self.cols)
  let end = to.clamp(min=0, max=self.cols)
  if end <= start {
    return
  }
  for i in start.. Unit {
  self.wrap_pending = false
  if self.cursor_row == self.scroll_bottom {
    self.scroll_up(1)
  } else if self.cursor_row < self.rows - 1 {
    self.cursor_row = self.cursor_row + 1
  }
}

///|
/// RI:光标上移,在滚动区域顶部时向下滚动。
pub fn TermScreen::reverse_index(self : TermScreen) -> Unit {
  self.wrap_pending = false
  if self.cursor_row == self.scroll_top {
    self.scroll_down(1)
  } else if self.cursor_row > 0 {
    self.cursor_row = self.cursor_row - 1
  }
}

///|
pub fn TermScreen::carriage_return(self : TermScreen) -> Unit {
  self.cursor_col = 0
  self.wrap_pending = false
}

///|
pub fn TermScreen::backspace(self : TermScreen) -> Unit {
  self.wrap_pending = false
  if self.cursor_col > 0 {
    self.cursor_col = self.cursor_col - 1
  }
}

///|
pub fn TermScreen::tab(self : TermScreen) -> Unit {
  self.wrap_pending = false
  let next = (self.cursor_col / 8 + 1) * 8
  self.cursor_col = if next < self.cols { next } else { self.cols - 1 }
}

///|
fn TermScreen::clamp_cursor(self : TermScreen) -> Unit {
  if self.cursor_col >= self.cols {
    self.cursor_col = self.cols - 1
  }
  if self.cursor_row >= self.rows {
    self.cursor_row = self.rows - 1
  }
  if self.cursor_col < 0 {
    self.cursor_col = 0
  }
  if self.cursor_row < 0 {
    self.cursor_row = 0
  }
}

///|
/// 把计数参数限制在 [0, limit](防溢出负值与超大值)。
fn clamp_count(n : Int, limit : Int) -> Int {
  if n <= 0 {
    0
  } else if n > limit {
    limit
  } else {
    n
  }
}

///|
/// 光标上移 n 行(不越过滚动区域顶部)。
pub fn TermScreen::cursor_up(self : TermScreen, n : Int) -> Unit {
  self.wrap_pending = false
  let n = clamp_count(n, self.rows)
  let target = self.cursor_row - n
  self.cursor_row = if target < self.scroll_top {
    self.scroll_top
  } else {
    target
  }
}

///|
/// 光标下移 n 行(不越过滚动区域底部)。
pub fn TermScreen::cursor_down(self : TermScreen, n : Int) -> Unit {
  self.wrap_pending = false
  let n = clamp_count(n, self.rows)
  let target = self.cursor_row + n
  self.cursor_row = if target > self.scroll_bottom {
    self.scroll_bottom
  } else {
    target
  }
}

///|
pub fn TermScreen::cursor_right(self : TermScreen, n : Int) -> Unit {
  self.wrap_pending = false
  let n = clamp_count(n, self.cols)
  let target = self.cursor_col + n
  self.cursor_col = if target >= self.cols { self.cols - 1 } else { target }
}

///|
pub fn TermScreen::cursor_left(self : TermScreen, n : Int) -> Unit {
  self.wrap_pending = false
  let n = clamp_count(n, self.cols)
  let target = self.cursor_col - n
  self.cursor_col = if target < 0 { 0 } else { target }
}

///|
/// 光标定位(row/col 均为 1-based)。
pub fn TermScreen::cursor_position(
  self : TermScreen,
  row : Int,
  col : Int,
) -> Unit {
  self.wrap_pending = false
  let r = if row < 1 { 1 } else { row }
  let c = if col < 1 { 1 } else { col }
  self.cursor_row = if r > self.rows { self.rows - 1 } else { r - 1 }
  self.cursor_col = if c > self.cols { self.cols - 1 } else { c - 1 }
}

///|
/// 光标行定位(1-based)。
pub fn TermScreen::cursor_line(self : TermScreen, row : Int) -> Unit {
  self.cursor_position(row, self.cursor_col + 1)
}

///|
/// 光标列定位(1-based)。
pub fn TermScreen::cursor_column(self : TermScreen, col : Int) -> Unit {
  self.cursor_position(self.cursor_row + 1, col)
}

///|
/// 清除显示:0 = 光标到末尾,1 = 开头到光标,2 = 整屏,3 = 整屏 + 回看。
pub fn TermScreen::erase_in_display(self : TermScreen, mode : Int) -> Unit {
  self.wrap_pending = false
  match mode {
    0 => {
      self.erase_cells(self.cursor_row, self.cursor_col, self.cols, self.attrs)
      for r in (self.cursor_row + 1).. {
      self.erase_cells(self.cursor_row, 0, self.cursor_col + 1, self.attrs)
      for r in 0.. {
      self.mark_all()
      for r in 0.. self.clear_all()
  }
}

///|
/// 清除行:0 = 光标到行尾,1 = 行首到光标,2 = 整行。
pub fn TermScreen::erase_in_line(self : TermScreen, mode : Int) -> Unit {
  self.wrap_pending = false
  match mode {
    0 =>
      self.erase_cells(self.cursor_row, self.cursor_col, self.cols, self.attrs)
    1 => self.erase_cells(self.cursor_row, 0, self.cursor_col + 1, self.attrs)
    _ => self.erase_cells(self.cursor_row, 0, self.cols, self.attrs)
  }
}

///|
/// 光标行插入 n 个空行(区域内),底部行进入回看缓冲。
pub fn TermScreen::insert_lines(self : TermScreen, n : Int) -> Unit {
  self.wrap_pending = false
  if self.cursor_row < self.scroll_top || self.cursor_row > self.scroll_bottom {
    return
  }
  let n = clamp_count(n, self.scroll_bottom - self.scroll_top + 1)
  self.mark_rows(self.cursor_row, self.scroll_bottom + 1)
  for _ in 0.. self.cursor_row {
      self.screen[i] = self.screen[i - 1]
      i = i - 1
    }
    self.screen[self.cursor_row] = self.blank_row()
  }
}

///|
/// 删除光标行起的 n 行(区域内),顶部行进入回看缓冲。
pub fn TermScreen::delete_lines(self : TermScreen, n : Int) -> Unit {
  self.wrap_pending = false
  if self.cursor_row < self.scroll_top || self.cursor_row > self.scroll_bottom {
    return
  }
  let n = clamp_count(n, self.scroll_bottom - self.scroll_top + 1)
  self.mark_rows(self.cursor_row, self.scroll_bottom + 1)
  for _ in 0.. Unit {
  self.wrap_pending = false
  let row = self.screen[self.cursor_row]
  let count = clamp_count(n, self.cols - self.cursor_col)
  if count == 0 {
    return
  }
  self.mark_row(self.cursor_row)
  let mut i = self.cols - 1
  while i >= self.cursor_col + count {
    row.cells[i] = row.cells[i - count]
    i = i - 1
  }
  for i in self.cursor_col..<(self.cursor_col + count) {
    row.cells[i] = blank_cell(self.attrs)
  }
}

///|
/// 删除光标处起 n 个字符(行内左移)。
pub fn TermScreen::delete_chars(self : TermScreen, n : Int) -> Unit {
  self.wrap_pending = false
  let row = self.screen[self.cursor_row]
  let count = clamp_count(n, self.cols - self.cursor_col)
  if count == 0 {
    return
  }
  self.mark_row(self.cursor_row)
  for i in self.cursor_col..<(self.cols - count) {
    row.cells[i] = row.cells[i + count]
  }
  for i in (self.cols - count).. Unit {
  self.wrap_pending = false
  let count = clamp_count(n, self.cols - self.cursor_col)
  self.erase_cells(
    self.cursor_row,
    self.cursor_col,
    self.cursor_col + count,
    self.attrs,
  )
}

///|
/// 滚动区域内向上滚动 n 行,顶部行进入回看缓冲。
pub fn TermScreen::scroll_up(self : TermScreen, n : Int) -> Unit {
  let n = clamp_count(n, self.scroll_bottom - self.scroll_top + 1)
  if n == 0 {
    return
  }
  self.mark_rows(self.scroll_top, self.scroll_bottom + 1)
  for _ in 0.. Unit {
  let n = clamp_count(n, self.scroll_bottom - self.scroll_top + 1)
  if n == 0 {
    return
  }
  self.mark_rows(self.scroll_top, self.scroll_bottom + 1)
  for _ in 0.. self.scroll_top {
      self.screen[i] = self.screen[i - 1]
      i = i - 1
    }
    self.screen[self.scroll_top] = self.blank_row()
  }
}

///|
/// 设置滚动区域(top/bottom 为 1-based)。
pub fn TermScreen::set_scroll_region(
  self : TermScreen,
  top : Int,
  bottom : Int,
) -> Unit {
  self.wrap_pending = false
  let t = if top < 1 { 1 } else { top }
  let b = if bottom > self.rows { self.rows } else { bottom }
  // 0-based 索引必须落在 [0, rows-1]
  self.scroll_top = if t > self.rows { self.rows - 1 } else { t - 1 }
  self.scroll_bottom = if b < 1 { 1 } else { b - 1 }
  if self.scroll_bottom >= self.rows {
    self.scroll_bottom = self.rows - 1
  }
  if self.scroll_bottom < self.scroll_top {
    self.scroll_bottom = self.scroll_top
  }
  self.cursor_position(1, 1)
}

///|
pub fn TermScreen::save_cursor(self : TermScreen) -> Unit {
  self.saved_cursor_row = self.cursor_row
  self.saved_cursor_col = self.cursor_col
}

///|
pub fn TermScreen::restore_cursor(self : TermScreen) -> Unit {
  self.cursor_row = self.saved_cursor_row
  self.cursor_col = self.saved_cursor_col
  self.wrap_pending = false
  self.clamp_cursor()
}

///|
pub fn TermScreen::set_title(self : TermScreen, title : String) -> Unit {
  self.title = title
}

///|
/// 调整尺寸;行/列变化尽量保留内容,缩小后的行进入回看缓冲。
/// 尺寸变化会让所有行的列位置失效,因此整屏标脏。
pub fn TermScreen::resize(
  self : TermScreen,
  new_cols : Int,
  new_rows : Int,
) -> Unit {
  let cols = if new_cols < 1 { 1 } else { new_cols }
  let rows = if new_rows < 1 { 1 } else { new_rows }
  let size_changed = cols != self.cols || rows != self.rows
  // 列调整
  if cols != self.cols {
    for row in self.screen {
      let cells = row.cells
      if cols > self.cols {
        while cells.length() < cols {
          cells.push(blank_cell(self.attrs))
        }
      } else if cols < self.cols {
        while cells.length() > cols {
          ignore(cells.pop())
        }
      }
    }
    self.cols = cols
    if self.cursor_col >= cols {
      self.cursor_col = cols - 1
    }
    if self.saved_cursor_col >= cols {
      self.saved_cursor_col = cols - 1
    }
  }
  // 行调整
  if rows != self.rows {
    if rows > self.rows {
      while self.screen.length() < rows {
        self.screen.push(self.blank_row())
      }
    } else if rows < self.rows {
      while self.screen.length() > rows {
        self.push_scrollback(self.screen[0])
        ignore(self.screen.remove(0))
      }
    }
    self.rows = rows
    if self.cursor_row >= rows {
      self.cursor_row = rows - 1
    }
    if self.saved_cursor_row >= rows {
      self.saved_cursor_row = rows - 1
    }
    self.scroll_top = 0
    self.scroll_bottom = rows - 1
  }
  if size_changed {
    self.dirty_rows = dirty_mask(rows)
    self.mark_all()
  } else if self.dirty_rows.length() != rows {
    self.dirty_rows = dirty_mask(rows)
  }
}