///|
/// The dimension being measured. This replaces Rust's trait type parameter.
pub(all) enum Attribute {
  Width
  Height
} derive(Eq, Debug)

///|
/// Compute a width or height from the table's records and configuration.
/// MoonBit traits do not accept type parameters, so Attribute selects the axis.
///
/// ```mbt check
/// test {
///   let table = @tabular.Table::from_rows([["abcd", "x"]])
///   assert_eq(
///     @tabular.Measurement::measure(
///       @tabular.Percent::new(50),
///       table,
///       @tabular.Attribute::Width,
///     ),
///     6,
///   )
///   assert_eq(
///     @tabular.Measurement::measure(
///       @tabular.Max::new(),
///       table,
///       @tabular.Attribute::Width,
///     ),
///     4,
///   )
/// }
/// ```
pub(open) trait Measurement {
  fn measure(Self, Table, Attribute) -> Int
}

///|
pub impl Measurement for Int with fn measure(self, _table, _attribute) {
  Int::max(0, self)
}

///|
pub(all) struct Max {} derive(Eq, Debug)

///|
pub fn Max::new() -> Max {
  Max::{ }
}

///|
pub fn Max::default() -> Max {
  Max::new()
}

///|
pub(all) struct Min {} derive(Eq, Debug)

///|
pub fn Min::new() -> Min {
  Min::{ }
}

///|
pub fn Min::default() -> Min {
  Min::new()
}

///|
pub(all) struct Percent(Int) derive(Eq, Debug)

///|
pub fn Percent::new(value : Int) -> Percent {
  Percent(Int::max(0, value))
}

///|
fn measure_max_width(table : Table) -> Int {
  let mut width = 0
  for row in table.rows {
    for cell in row {
      width = Int::max(width, @papergrid.get_text_width(cell))
    }
  }
  width
}

///|
fn measure_min_width(table : Table) -> Int {
  let columns = table.count_cols()
  let mut width = 0
  for row in table.rows {
    let mut smallest = if columns == 0 { 0 } else { 0x7fffffff }
    for col in 0.. Int {
  let mut height = 0
  for col in 0.. measure_max_width(table)
    Height =>
      table.rows.fold(init=0, (height, row) => {
        Int::max(height, row_height(row, columns))
      })
  }
}

///|
pub impl Measurement for Min with fn measure(_self, table, attribute) {
  let columns = table.count_cols()
  match attribute {
    Width => measure_min_width(table)
    Height =>
      if table.rows.is_empty() {
        0
      } else {
        table.rows.fold(init=0x7fffffff, (height, row) => {
          Int::min(height, row_height(row, columns))
        })
      }
  }
}

///|
fn percent_of(total : Int, percent : Int) -> Int {
  let result = total.to_int64() * Int::max(0, percent).to_int64() / 100L
  if result > 0x7fffffffL {
    0x7fffffff
  } else {
    result.to_int()
  }
}

///|
pub impl Measurement for Percent with fn measure(self, table, attribute) {
  let records = @papergrid.IterRecords::new(table.rows)
  let total = match attribute {
    Width => @papergrid.IterGridDimension::total_width(records, table.config)
    Height => @papergrid.IterGridDimension::total_height(records, table.config)
  }
  percent_of(total, self.0)
}

///|
pub extend Attribute with Eq::{not_equal, equal}

///|
pub extend Attribute with @debug.Debug::{to_repr}

///|
pub extend Max with Eq::{not_equal, equal}

///|
pub extend Max with @debug.Debug::{to_repr}

///|
pub extend Min with Eq::{not_equal, equal}

///|
pub extend Min with @debug.Debug::{to_repr}

///|
pub extend Percent with Eq::{not_equal, equal}

///|
pub extend Percent with @debug.Debug::{to_repr}

///|
pub extend Max with Measurement::{measure}

///|
pub extend Min with Measurement::{measure}

///|
pub extend Percent with Measurement::{measure}