///|
pub struct ReportTable {
  columns : Array[String]
  rows : Array[Array[String]]
} derive(Debug, Eq)

///|
pub fn table(
  columns : Array[String],
  rows : Array[Array[String]],
) -> ReportTable {
  { columns, rows }
}

///|
pub fn ReportTable::validate(self : ReportTable) -> Array[String] {
  let errors : Array[String] = []
  if self.columns.length() == 0 {
    errors.push("table needs at least one column")
  }
  for _, row in self.rows {
    if row.length() != self.columns.length() {
      errors.push(
        "row has {row.length()} cells, expected {self.columns.length()}",
      )
    }
  }
  errors
}

///|
fn escape_table(text : String) -> String {
  escape_html(text).replace(old="|", new="\\|")
}

///|
pub fn ReportTable::to_markdown(self : ReportTable) -> String {
  let lines : Array[String] = []
  lines.push("| " + self.columns.map(escape_table).join(" | ") + " |")
  lines.push("| " + self.columns.map(fn(_) { "---" }).join(" | ") + " |")
  for row in self.rows {
    lines.push("| " + row.map(escape_table).join(" | ") + " |")
  }
  lines.join("\n")
}

///|
fn csv_cell(text : String) -> String {
  "\"" + text.replace(old="\"", new="\"\"") + "\""
}

///|
pub fn ReportTable::to_csv(self : ReportTable) -> String {
  let lines : Array[String] = [self.columns.map(csv_cell).join(",")]
  for row in self.rows {
    lines.push(row.map(csv_cell).join(","))
  }
  lines.join("\n")
}

///|
pub fn ReportTable::to_html(self : ReportTable) -> String {
  let header = "" +
    self.columns.map(fn(item) { "" + escape_html(item) + "" }).join("") +
    ""
  let body : Array[String] = []
  for row in self.rows {
    body.push(
      "" +
      row.map(fn(item) { "" + escape_html(item) + "" }).join("") +
      "",
    )
  }
  "" +
  header +
  "" +
  body.join("") +
  "
" } ///| pub fn ReportTable::row_count(self : ReportTable) -> Int { self.rows.length() } ///| pub fn ReportTable::column_count(self : ReportTable) -> Int { self.columns.length() } ///| pub fn ReportTable::is_rectangular(self : ReportTable) -> Bool { self.validate().length() == 0 }