///|
struct PanelConfig {
  index : Int
  text : String
  is_horizontal : Bool
}

///|
/// Insert a horizontal panel at the given row index.
/// The panel text spans all cols.
pub fn Panel::horizontal(row : Int, text : String) -> PanelConfig {
  { index: row, text, is_horizontal: true }
}

///|
/// Insert a header panel at row 0.
pub fn Panel::header(text : String) -> PanelConfig {
  { index: 0, text, is_horizontal: true }
}

///|
/// Insert a footer panel after the last row.
pub fn Panel::footer(text : String) -> PanelConfig {
  { index: -1, text, is_horizontal: true }
}

///|
/// Insert a vertical panel at the given col index.
/// The panel text spans all rows.
pub fn Panel::vertical(col : Int, text : String) -> PanelConfig {
  { index: col, text, is_horizontal: false }
}

///|
/// Apply a horizontal or vertical panel to the table.
pub fn Table::panel(self : Table, panel : PanelConfig) -> Table {
  if panel.is_horizontal {
    self.apply_horizontal_panel(panel.index, panel.text)
  } else {
    self.apply_vertical_panel(panel.index, panel.text)
  }
  self
}

///|
/// Split a string by character width, inserting newlines.
fn split_string_by_width(s : String, width : Int) -> String {
  if width <= 0 {
    return ""
  }
  let lines = @papergrid.apply_width(s, @papergrid.WidthMode::wrap(width))
  let buf = StringBuilder::new()
  for i, line in lines {
    if i > 0 {
      buf.write_char('\n')
    }
    buf.write_string(line)
  }
  buf.to_string()
}

///|
/// Insert a vertical panel with text split to fit a certain width.
pub fn Panel::vertical_with_width(
  col : Int,
  text : String,
  width : Int,
) -> PanelConfig {
  let split_text = split_string_by_width(text, width)
  { index: col, text: split_text, is_horizontal: false }
}

///|
fn Table::apply_horizontal_panel(
  self : Table,
  row : Int,
  text : String,
) -> Unit {
  let count_rows = self.rows.length()
  let count_cols = if count_rows > 0 { self.rows[0].length() } else { 1 }
  // Resolve special row values
  let target_row = if row < 0 { count_rows } else { row }
  // Bounds check
  if target_row > count_rows {
    return
  }
  // Check if any cell at target_row is covered by a row span
  for col in 0.. 1 {
    self.config.set_col_span(target_row, 0, count_cols)
  }
}

///|
fn Table::apply_vertical_panel(self : Table, col : Int, text : String) -> Unit {
  let count_rows = self.rows.length()
  let count_cols = if count_rows > 0 { self.rows[0].length() } else { 0 }
  // Bounds check
  if col > count_cols {
    return
  }
  // Check if any cell at target_col is covered by a col span
  for row = 0; row <= count_rows; row = row + 1 {
    if row < count_rows && self.config.is_cell_covered_by_col_span(row, col) {
      return
    }
  }
  // Insert an empty col at target_col in each row
  self.rows.each(row => row.insert(col, ""))
  // Shift span metadata
  self.config.shift_col_spans_right(col)
  // Set the panel text in the first row
  if count_rows > 0 {
    self.rows[0][col] = text
  }
  // Set row span to cover all rows
  if count_rows > 1 {
    self.config.set_row_span(0, col, count_rows)
  }
}