///|
/// Character-based framebuffer for TUI rendering

///|
/// Character-based framebuffer
pub struct CharBuffer {
  width : Int
  height : Int
  cells : Array[CharCell]
}

///|
pub fn CharBuffer::new(width : Int, height : Int) -> CharBuffer {
  let size = width * height
  let cells : Array[CharCell] = Array::make(size, CharCell::default())
  { width, height, cells }
}

///|
pub fn CharBuffer::get_cell(self : CharBuffer, x : Int, y : Int) -> CharCell {
  if x >= 0 && x < self.width && y >= 0 && y < self.height {
    self.cells[y * self.width + x]
  } else {
    CharCell::default()
  }
}

///|
pub fn CharBuffer::set_cell(
  self : CharBuffer,
  x : Int,
  y : Int,
  cell : CharCell,
) -> Unit {
  if x >= 0 && x < self.width && y >= 0 && y < self.height {
    self.cells[y * self.width + x] = cell
  }
}

///|
pub fn CharBuffer::fill_rect(
  self : CharBuffer,
  x : Int,
  y : Int,
  w : Int,
  h : Int,
  cell : CharCell,
) -> Unit {
  for row = y; row < y + h; row = row + 1 {
    for col = x; col < x + w; col = col + 1 {
      self.set_cell(col, row, cell)
    }
  }
}

///|
/// Fill background color and overwrite content with spaces
/// This allows overlays to properly cover underlying content
pub fn CharBuffer::fill_bg(
  self : CharBuffer,
  x : Int,
  y : Int,
  w : Int,
  h : Int,
  bg : @core.Color,
) -> Unit {
  for row = y; row < y + h; row = row + 1 {
    for col = x; col < x + w; col = col + 1 {
      if col >= 0 && col < self.width && row >= 0 && row < self.height {
        let existing = self.get_cell(col, row)
        // Preserve wide char placeholders (NUL) to avoid breaking CJK rendering
        if existing.char == '\u0000' {
          // Only update background, keep the placeholder
          self.set_cell(col, row, { ..existing, bg, })
        } else {
          // Overwrite cell completely with space to cover underlying content
          self.set_cell(col, row, { ..CharCell::default(), char: ' ', bg })
        }
      }
    }
  }
}

///|
pub fn CharBuffer::write_text(
  self : CharBuffer,
  x : Int,
  y : Int,
  text : String,
  style : TextStyle,
  max_width : Int,
) -> Int {
  let mut cur_x = x
  let end_x = x + max_width
  for c in text {
    if c == '\n' {
      break
    }
    if c.to_int() < 32 {
      continue
    }
    let width = @core.char_display_width(c)
    if cur_x + width > end_x {
      break
    }
    let bg = if style.bg.is_transparent() {
      self.get_cell(cur_x, y).bg
    } else {
      style.bg
    }
    let cell : CharCell = {
      char: c,
      fg: style.fg,
      bg,
      bold: style.bold,
      underline: style.underline,
      reverse: false,
    }
    self.set_cell(cur_x, y, cell)
    if width == 2 && cur_x + 1 < end_x {
      let next_bg = if style.bg.is_transparent() {
        self.get_cell(cur_x + 1, y).bg
      } else {
        style.bg
      }
      let placeholder : CharCell = {
        char: '\u0000',
        fg: style.fg,
        bg: next_bg,
        bold: style.bold,
        underline: style.underline,
        reverse: false,
      }
      self.set_cell(cur_x + 1, y, placeholder)
    }
    cur_x = cur_x + width
  }
  cur_x - x
}

///|
pub fn CharBuffer::write_text_wrapped(
  self : CharBuffer,
  x : Int,
  y : Int,
  text : String,
  style : TextStyle,
  max_width : Int,
  max_height : Int,
) -> Int {
  let mut cur_x = x
  let mut cur_y = y
  let end_x = x + max_width
  let end_y = y + max_height
  for c in text {
    if cur_y >= end_y {
      break
    }
    if c == '\n' {
      cur_x = x
      cur_y = cur_y + 1
      continue
    }
    if c.to_int() < 32 {
      continue
    }
    let width = @core.char_display_width(c)
    if cur_x + width > end_x {
      cur_x = x
      cur_y = cur_y + 1
      if cur_y >= end_y {
        break
      }
    }
    let bg = if style.bg.is_transparent() {
      self.get_cell(cur_x, cur_y).bg
    } else {
      style.bg
    }
    let cell : CharCell = {
      char: c,
      fg: style.fg,
      bg,
      bold: style.bold,
      underline: style.underline,
      reverse: false,
    }
    self.set_cell(cur_x, cur_y, cell)
    if width == 2 && cur_x + 1 < end_x {
      let next_bg = if style.bg.is_transparent() {
        self.get_cell(cur_x + 1, cur_y).bg
      } else {
        style.bg
      }
      let placeholder : CharCell = {
        char: '\u0000',
        fg: style.fg,
        bg: next_bg,
        bold: style.bold,
        underline: style.underline,
        reverse: false,
      }
      self.set_cell(cur_x + 1, cur_y, placeholder)
    }
    cur_x = cur_x + width
  }
  cur_y - y + 1
}

///|
pub fn CharBuffer::clear(self : CharBuffer) -> Unit {
  let default_cell = CharCell::default()
  for i = 0; i < self.cells.length(); i = i + 1 {
    self.cells[i] = default_cell
  }
}

///|
/// Clone the buffer
pub fn CharBuffer::clone(self : CharBuffer) -> CharBuffer {
  let new_cells = Array::make(self.cells.length(), CharCell::default())
  for i = 0; i < self.cells.length(); i = i + 1 {
    new_cells[i] = self.cells[i]
  }
  { width: self.width, height: self.height, cells: new_cells }
}