///|
/// 表格。列定义与行数据分离:列说"怎么显示",行说"显示什么"。
///
/// 构建器就地累积(`push` 直接改内部行表并返回自身),链式调用不会复制中间
/// 结果。字段都不带 `mut`:内容靠引用原地修改。
pub struct Table {
  cols : Array[Column]
  rows : Array[Array[Cell]]
  chars : Chars
  pad_l : Int32
  pad_r : Int32
  head_style : @clicolor.Style
  border_style : @clicolor.Style
  total_width : Int32?
  row_height : Int32?
  word_wrap : Bool
  wrap_on_word_boundary : Bool
  truncate : String
} derive(Debug)

///|
pub extend Table with Debug::{to_repr}

///|
/// 建一张表。列定义必填,其余都有默认值,用 `with_*` 逐个叠加。
pub fn Table::new(cols : Array[Column]) -> Table {
  {
    cols,
    rows: [],
    chars: Chars::single(),
    pad_l: 1,
    pad_r: 1,
    head_style: @clicolor.default,
    border_style: @clicolor.default,
    total_width: None,
    row_height: None,
    word_wrap: false,
    wrap_on_word_boundary: true,
    truncate: "…",
  }
}

///|
/// 追加一行。数组元素经 `ToCell` 转成单元格,所以 `["a", "b"]` 与 `[1, 2]` 都行。
///
/// 跨行跨列靠单元格自己声明:`Cell::text("x").span(2, rows=3)`。空出来的位置
/// 不必补占位符,布局阶段会自己填。
pub fn[T : ToCell] Table::push(self : Table, cells : Array[T]) -> Table {
  self.rows.push(cells.map(fn(c) { c.to_cell() }))
  self
}

///|
/// 批量追加。元素实现 `ToRow` 即可:`Array[T]` 是一行,业务类型可以自己实现。
pub fn[R : ToRow] Table::push_rows(self : Table, rows : Array[R]) -> Table {
  for row in rows {
    self.rows.push(row.to_row())
  }
  self
}

///|
/// 换一套边框字符。
pub fn Table::with_chars(self : Table, chars : Chars) -> Table {
  { ..self, chars, }
}

///|
/// 换内边距。左右都按显示列数给。
pub fn Table::with_padding(self : Table, left : Int32, right : Int32) -> Table {
  { ..self, pad_l: left, pad_r: right, }
}

///|
/// 约束表格总宽。超出时按均摊削列宽,内容窄时不会被拉伸。
pub fn Table::with_width(self : Table, width : Int32) -> Table {
  { ..self, total_width: Some(width), }
}

///|
/// 固定每一行的行数。内容超出时在末行收成截断符,空出的行补空行。
pub fn Table::with_row_height(self : Table, height : Int32) -> Table {
  { ..self, row_height: Some(height), }
}

///|
/// 打开自动换行。配合列宽约束使用:窄列会把长内容折行而不是截断。
pub fn Table::with_word_wrap(self : Table) -> Table {
  { ..self, word_wrap: true, }
}

///|
/// 换行时是否优先在词边界断开。
pub fn Table::with_word_boundary(self : Table, on : Bool) -> Table {
  { ..self, wrap_on_word_boundary: on, }
}

///|
/// 表头与边框的样式。传一个 Style 值,空 Style 原样返回。
pub fn Table::with_head_style(self : Table, style : @clicolor.Style) -> Table {
  { ..self, head_style: style, }
}

///|
pub fn Table::with_border_style(self : Table, style : @clicolor.Style) -> Table {
  { ..self, border_style: style, }
}

///|
/// 无表头表格:列数从第一行推导。
pub fn Table::grid(rows : Array[Array[String]]) -> Table {
  let ncols = if rows.length() > 0 { rows[0].length() } else { 0 }
  let cols : Array[Column] = []
  for _ in 0.. Table {
  let t = Table::new([Column::new(""), Column::new("")])
  for pair in pairs {
    let (key, value) = pair
    t.rows.push([Cell::text(key), Cell::text(value)])
  }
  t
}

///|
/// 是否有表头:任一列有标题就画表头行。
fn has_head(cols : Array[Column]) -> Bool {
  for col in cols {
    if col.title != "" {
      return true
    }
  }
  false
}

///|
/// 组装参与布局的行:有表头时把列标题作为第一行,其余行原样跟上。
fn build_rows(table : Table) -> Array[Array[Cell]] {
  let rows : Array[Array[Cell]] = []
  if has_head(table.cols) {
    rows.push(table.cols.map(fn(c) { Cell::text(c.title) }))
  }
  for row in table.rows {
    rows.push(row)
  }
  rows
}

///|
/// 渲染成字符串。
///
/// 流程与 cli-table3 一致:铺布局网格 → 算列宽 → 算行高 → 解出跨行偏移 →
/// 逐行画顶线、内容行与底线。行内单元格数与列表数不一致时
/// `raise TableError::Arity`,不做静默补齐。
pub fn Table::render(self : Table) -> String raise TableError {
  let ncols = self.cols.length()
  if ncols == 0 {
    if self.rows.length() == 0 {
      return ""
    }
    raise TableError::Arity(0, self.rows[0].length())
  }
  // 总宽连边框和最小列宽都放不下时直接报错,而不是画出一张挤碎的表。
  match self.total_width {
    Some(budget) => {
      let min_col = self.pad_l + self.pad_r + 1
      let needed = ncols + 1 + ncols * min_col
      if budget < needed {
        raise TableError::TooNarrow(budget)
      }
    }
    None => ()
  }
  let rows = build_rows(self)
  if rows.length() == 0 {
    return ""
  }
  let (grid, occupied) = layout_rows(rows)
  // 行宽校验:行内跨列数之和,加上被上方跨行格子占用的列数,必须刚好等于列数。
  // 写多写少都报错,不静默补齐——跨行表格里"少写"是正常的,占用的列会补回来。
  for i in 0..
      for i in 0.. ()
  }
  resolve_row_span_offsets(grid, row_heights)
  let ctx : DrawCtx = {
    chars: self.chars,
    pad_l: self.pad_l,
    pad_r: self.pad_r,
    col_widths,
    row_heights,
    cols: self.cols,
    head_style: self.head_style,
    border_style: self.border_style,
    truncate: self.truncate,
    wrap: self.word_wrap,
    on_word_boundary: self.wrap_on_word_boundary,
    has_head: has_head(self.cols),
    grid,
  }
  let out : Array[String] = []
  for y in 0.. "" })
}

///|
/// 让 `table.to_string()` 与 `Show::to_string(table)` 两种写法都可用。
pub extend Table with Show::{to_string, output}