// Port of rich/table.py.

///|
/// Defines a column within a `Table`.
pub(all) struct Column {
  /// Renderable for the header (typically a string).
  mut header : &Renderable
  /// Renderable for the footer (typically a string).
  mut footer : &Renderable
  /// The style of the header.
  mut header_style : StyleType
  /// The style of the footer.
  mut footer_style : StyleType
  /// The style of the column.
  mut style : StyleType
  /// How to justify text within the column.
  mut justify : JustifyMethod
  /// How to vertically align content.
  mut vertical : VerticalAlignMethod
  /// Overflow method.
  mut overflow : OverflowMethod
  /// Width of the column, or `None` to auto calculate width.
  mut width : Int?
  /// Minimum width of column.
  mut min_width : Int?
  /// Maximum width of column.
  mut max_width : Int?
  /// Ratio to use when calculating column width.
  mut ratio : Int?
  /// Prevent wrapping of text within the column.
  mut no_wrap : Bool
  /// Apply highlighter to column.
  mut highlight : Bool
  priv mut index : Int
  priv cells_ : Array[&Renderable]
}

///|
pub fn Column::new(
  header? : &Renderable = "",
  footer? : &Renderable = "",
  header_style? : &IntoStyle,
  footer_style? : &IntoStyle,
  style? : &IntoStyle,
  justify? : JustifyMethod = Left,
  vertical? : VerticalAlignMethod = Top,
  overflow? : OverflowMethod = Ellipsis,
  width? : Int,
  min_width? : Int,
  max_width? : Int,
  ratio? : Int,
  no_wrap? : Bool = false,
  highlight? : Bool = false,
) -> Column {
  {
    header,
    footer,
    header_style: style_or_empty(header_style),
    footer_style: style_or_empty(footer_style),
    style: style_or_empty(style),
    justify,
    vertical,
    overflow,
    width,
    min_width,
    max_width,
    ratio,
    no_wrap,
    highlight,
    index: 0,
    cells_: [],
  }
}

///|
/// A copy of the column without its cells.
pub fn Column::copy(self : Column) -> Column {
  { ..self, cells_: [], }
}

///|
/// The cells in the column.
pub fn Column::cells(self : Column) -> Array[&Renderable] {
  self.cells_.copy()
}

///|
/// Check if this column is flexible.
pub fn Column::flexible(self : Column) -> Bool {
  self.ratio is Some(_)
}

///|
/// Things accepted as table headers: strings and columns.
pub(open) trait IntoColumn {
  fn into_column(Self) -> Column
  /// Convert to a column of a table whose `highlight` setting is given.
  /// Upstream adds string headers with `add_column(header=...)`, so they
  /// inherit the table's `highlight`; `Column`s are used as they are.
  fn into_table_column(Self, highlight~ : Bool) -> Column = _
}

///|
impl IntoColumn with fn into_table_column(self, highlight~) {
  ignore(highlight)
  self.into_column()
}

///|
pub impl IntoColumn for String with fn into_column(self) {
  Column::new(header=self)
}

///|
pub impl IntoColumn for String with fn into_table_column(self, highlight~) {
  Column::new(header=self, highlight~)
}

///|
pub impl IntoColumn for Column with fn into_column(self) {
  self
}

///|
/// Information regarding a row.
pub(all) struct Row {
  /// Style to apply to row.
  mut style : StyleType?
  /// Indicated end of section, which will force a line beneath the row.
  mut end_section : Bool
}

///|
priv struct Cell {
  style : Style
  renderable : &Renderable
  vertical : VerticalAlignMethod
}

///|
/// A console renderable to draw a table.
pub(all) struct Table {
  columns : Array[Column]
  rows : Array[Row]
  mut title : TextType?
  mut caption : TextType?
  mut width : Int?
  mut min_width : Int?
  mut box_ : Box?
  mut safe_box : Bool?
  priv mut padding_ : (Int, Int, Int, Int)
  mut pad_edge : Bool
  priv mut expand_ : Bool
  mut show_header : Bool
  mut show_footer : Bool
  mut show_edge : Bool
  mut show_lines : Bool
  mut leading : Int
  mut collapse_padding : Bool
  mut style : StyleType
  mut header_style : StyleType
  mut footer_style : StyleType
  mut border_style : StyleType?
  mut title_style : StyleType?
  mut caption_style : StyleType?
  mut title_justify : JustifyMethod
  mut caption_justify : JustifyMethod
  mut highlight : Bool
  mut row_styles : Array[StyleType]
}

///|
/// Create a table. `headers` are column headers (strings or `Column`s).
/// `box_` is the box style; pass `no_box=true` for no box.
pub fn Table::new(
  headers? : Array[&IntoColumn] = [],
  title? : &IntoText,
  caption? : &IntoText,
  width? : Int,
  min_width? : Int,
  box_? : Box = box_heavy_head,
  no_box? : Bool = false,
  safe_box? : Bool,
  padding? : ArrayView[Int] = [0, 1],
  collapse_padding? : Bool = false,
  pad_edge? : Bool = true,
  expand? : Bool = false,
  show_header? : Bool = true,
  show_footer? : Bool = false,
  show_edge? : Bool = true,
  show_lines? : Bool = false,
  leading? : Int = 0,
  style? : &IntoStyle,
  row_styles? : Array[&IntoStyle] = [],
  header_style? : &IntoStyle,
  footer_style? : &IntoStyle,
  border_style? : &IntoStyle,
  title_style? : &IntoStyle,
  caption_style? : &IntoStyle,
  title_justify? : JustifyMethod = Center,
  caption_justify? : JustifyMethod = Center,
  highlight? : Bool = false,
) -> Table raise RichError {
  let table : Table = {
    columns: [],
    rows: [],
    title: title.map(t => t.into_text_type()),
    caption: caption.map(t => t.into_text_type()),
    width,
    min_width,
    box_: if no_box {
      None
    } else {
      Some(box_)
    },
    safe_box,
    padding_: unpack_padding(padding),
    pad_edge,
    expand_: expand,
    show_header,
    show_footer,
    show_edge,
    show_lines,
    leading,
    collapse_padding,
    style: match style {
      Some(s) => s.into_style_type()
      None => Def("none")
    },
    header_style: match header_style {
      Some(s) => s.into_style_type()
      None => Def("table.header")
    },
    footer_style: match footer_style {
      Some(s) => s.into_style_type()
      None => Def("table.footer")
    },
    border_style: style_opt(border_style),
    title_style: style_opt(title_style),
    caption_style: style_opt(caption_style),
    title_justify,
    caption_justify,
    highlight,
    row_styles: row_styles.map(s => s.into_style_type()),
  }
  for header in headers {
    let column = header.into_table_column(highlight~)
    column.index = table.columns.length()
    table.columns.push(column)
  }
  table
}

///|
/// Get a table with no lines, headers, or footer.
pub fn Table::grid(
  headers? : Array[&IntoColumn] = [],
  padding? : ArrayView[Int] = [0],
  collapse_padding? : Bool = true,
  pad_edge? : Bool = false,
  expand? : Bool = false,
) -> Table raise RichError {
  Table::new(
    headers~,
    no_box=true,
    padding~,
    collapse_padding~,
    show_header=false,
    show_footer=false,
    show_edge=false,
    pad_edge~,
    expand~,
  )
}

///|
/// Whether the table expands to the available space.
pub fn Table::expand(self : Table) -> Bool {
  self.expand_ || self.width is Some(_)
}

///|
pub fn Table::set_expand(self : Table, expand : Bool) -> Unit {
  self.expand_ = expand
}

///|
/// Cell padding as `(top, right, bottom, left)`.
pub fn Table::padding(self : Table) -> (Int, Int, Int, Int) {
  self.padding_
}

///|
/// Set cell padding (1, 2 or 4 values).
pub fn Table::set_padding(
  self : Table,
  padding : ArrayView[Int],
) -> Unit raise RichError {
  self.padding_ = unpack_padding(padding)
}

///|
fn Table::extra_width(self : Table) -> Int {
  let mut width = 0
  if self.box_ is Some(_) && self.show_edge {
    width += 2
  }
  if self.box_ is Some(_) {
    width += self.columns.length() - 1
  }
  width
}

///|
/// The number of rows in the table.
pub fn Table::row_count(self : Table) -> Int {
  self.rows.length()
}

///|
/// Get the current row style.
pub fn Table::get_row_style(
  self : Table,
  console : Console,
  index : Int,
) -> Style raise {
  let mut style = Style::null()
  if !self.row_styles.is_empty() {
    style = style +
      console.get_style(
        self.row_styles[py_mod(index, self.row_styles.length())],
      )
  }
  if self.rows[index].style is Some(row_style) {
    style = style + console.get_style(row_style)
  }
  style
}

///|
pub impl Renderable for Table with fn rich_measure(self, console, options) {
  let mut max_width = options.max_width
  if self.width is Some(w) {
    max_width = w
  }
  if max_width < 0 {
    return Some({ minimum: 0, maximum: 0, })
  }
  let extra_width = self.extra_width()
  let mut max_width_sum = 0
  for
    w in self.calculate_column_widths(
      console,
      options.update_width(max_width - extra_width),
    ) {
    max_width_sum += w
  }
  let max_width = max_width_sum
  let mut minimum_width = 0
  let mut maximum_width = 0
  for column in self.columns {
    let m = self.measure_column(
      console,
      options.update_width(max_width),
      column,
    )
    minimum_width += m.minimum
    maximum_width += m.maximum
  }
  let measurement = {
    minimum: minimum_width + extra_width,
    maximum: match self.width {
      None => maximum_width + extra_width
      Some(w) => w
    },
  }
  Some(measurement.clamp(min_width?=self.min_width))
}

///|
/// Add a column to the table.
pub fn Table::add_column(
  self : Table,
  header? : &Renderable = "",
  footer? : &Renderable = "",
  header_style? : &IntoStyle,
  highlight? : Bool,
  footer_style? : &IntoStyle,
  style? : &IntoStyle,
  justify? : JustifyMethod = Left,
  vertical? : VerticalAlignMethod = Top,
  overflow? : OverflowMethod = Ellipsis,
  width? : Int,
  min_width? : Int,
  max_width? : Int,
  ratio? : Int,
  no_wrap? : Bool = false,
) -> Unit {
  let column = Column::new(
    header~,
    footer~,
    header_style?,
    highlight=highlight.unwrap_or(self.highlight),
    footer_style?,
    style?,
    justify~,
    vertical~,
    overflow~,
    width?,
    min_width?,
    max_width?,
    ratio?,
    no_wrap~,
  )
  column.index = self.columns.length()
  self.columns.push(column)
}

///|
/// Add a row of renderables.
pub fn Table::add_row(
  self : Table,
  renderables : ArrayView[&Renderable],
  style? : &IntoStyle,
  end_section? : Bool = false,
) -> Unit {
  let cells : Array[&Renderable] = renderables.to_owned()
  while cells.length() < self.columns.length() {
    cells.push("")
  }
  for index, renderable in cells {
    let column = if index == self.columns.length() {
      let column = Column::new(highlight=self.highlight)
      column.index = index
      for _ in self.rows {
        column.cells_.push(Text::new())
      }
      self.columns.push(column)
      column
    } else {
      self.columns[index]
    }
    column.cells_.push(renderable)
  }
  self.rows.push({ style: style_opt(style), end_section, })
}

///|
/// Add a new section (draw a line after current row).
pub fn Table::add_section(self : Table) -> Unit {
  if !self.rows.is_empty() {
    self.rows[self.rows.length() - 1].end_section = true
  }
}

///|
pub impl Renderable for Table with fn rich_console(self, console, options) {
  if self.columns.is_empty() {
    return [Seg(Segment::new("\n"))]
  }
  let mut max_width = options.max_width
  if self.width is Some(w) {
    max_width = w
  }
  let extra_width = self.extra_width()
  let widths = self.calculate_column_widths(
    console,
    options.update_width(max_width - extra_width),
  )
  let mut table_width = extra_width
  for w in widths {
    table_width += w
  }
  let render_options = options.update(
    width=table_width,
    highlight=Some(self.highlight),
    height=None,
  )
  let out = []
  fn render_annotation(
    text : TextType,
    style : StyleType,
    justify : JustifyMethod,
  ) -> Unit raise {
    let render_text = match text {
      Str(s) => console.render_str(s, style~, highlight=Some(false))
      Text(t) => t
    }
    for
      s in console.render(
        render_text,
        options=render_options.update(justify=Some(justify)),
      ) {
      out.push(Seg(s))
    }
  }

  if self.title is Some(title) && title.to_bool() {
    render_annotation(
      title,
      self.title_style.unwrap_or(Def("table.title")),
      self.title_justify,
    )
  }
  for s in self.render_table(console, render_options, widths) {
    out.push(Seg(s))
  }
  if self.caption is Some(caption) && caption.to_bool() {
    render_annotation(
      caption,
      self.caption_style.unwrap_or(Def("table.caption")),
      self.caption_justify,
    )
  }
  out
}

///|
fn sum(xs : ArrayView[Int]) -> Int {
  let mut total = 0
  for x in xs {
    total += x
  }
  total
}

///|
/// Calculate the widths of each column, including padding, not including
/// borders.
fn Table::calculate_column_widths(
  self : Table,
  console : Console,
  options : ConsoleOptions,
) -> Array[Int] raise {
  let max_width = options.max_width
  let columns = self.columns
  let width_ranges = columns.map(c => self.measure_column(console, options, c))
  let mut widths = width_ranges.map(r => {
    if r.maximum == 0 {
      1
    } else {
      r.maximum
    }
  })
  let extra_width = self.extra_width()
  if self.expand() {
    let ratios = []
    for col in columns {
      if col.flexible() {
        ratios.push(col.ratio.unwrap_or(0))
      }
    }
    if ratios.iter().any(r => r != 0) {
      let fixed_widths = columns.mapi((i, column) => {
        if column.flexible() {
          0
        } else {
          width_ranges[i].maximum
        }
      })
      let flex_minimum = []
      for column in columns {
        if column.flexible() {
          flex_minimum.push(
            (match column.width {
              Some(w) if w != 0 => w
              _ => 1
            }) +
            self.get_padding_width(column.index),
          )
        }
      }
      let flexible_width = max_width - sum(fixed_widths)
      let flex_widths = ratio_distribute(
        flexible_width,
        ratios,
        minimums=flex_minimum,
      )
      let mut flex_index = 0
      for index, column in columns {
        if column.flexible() {
          widths[index] = fixed_widths[index] + flex_widths[flex_index]
          flex_index += 1
        }
      }
    }
  }
  let mut table_width = sum(widths)
  if table_width > max_width {
    widths = Table::collapse_widths(
      widths,
      columns.map(column => column.width is None && !column.no_wrap),
      max_width,
    )
    table_width = sum(widths)
    if table_width > max_width {
      let excess_width = table_width - max_width
      widths = ratio_reduce(
        excess_width,
        Array::make(widths.length(), 1),
        widths,
        widths,
      )
      table_width = sum(widths)
    }
    let width_ranges = []
    for i, column in columns {
      width_ranges.push(
        self.measure_column(console, options.update_width(widths[i]), column),
      )
    }
    widths = width_ranges.map(r => r.maximum)
  }
  if (table_width < max_width && self.expand()) ||
    (self.min_width is Some(mw) && table_width < mw - extra_width) {
    let max_width = match self.min_width {
      None => max_width
      Some(mw) => @cmp.minimum(mw - extra_width, max_width)
    }
    let pad_widths = ratio_distribute(max_width - table_width, widths)
    widths = widths.mapi((i, w) => w + pad_widths[i])
  }
  widths
}

///|
/// Reduce widths so that the total is under max_width.
fn Table::collapse_widths(
  widths : Array[Int],
  wrapable : Array[Bool],
  max_width : Int,
) -> Array[Int] {
  let mut widths = widths
  let mut total_width = sum(widths)
  let mut excess_width = total_width - max_width
  if wrapable.iter().any(w => w) {
    while total_width != 0 && excess_width > 0 {
      let mut max_column = 0
      let mut first = true
      for i, width in widths {
        if wrapable[i] && (first || width > max_column) {
          max_column = width
          first = false
        }
      }
      let mut second_max_column = 0
      for i, width in widths {
        let v = if wrapable[i] && width != max_column { width } else { 0 }
        second_max_column = @cmp.maximum(second_max_column, v)
      }
      let column_difference = max_column - second_max_column
      let ratios = widths.mapi((i, width) => {
        if width == max_column && wrapable[i] {
          1
        } else {
          0
        }
      })
      if !ratios.iter().any(r => r != 0) || column_difference == 0 {
        break
      }
      let max_reduce = Array::make(
        widths.length(),
        @cmp.minimum(excess_width, column_difference),
      )
      widths = ratio_reduce(excess_width, ratios, max_reduce, widths)
      total_width = sum(widths)
      excess_width = total_width - max_width
    }
  }
  widths
}

///|
fn Table::get_cells(
  self : Table,
  console : Console,
  column_index : Int,
  column : Column,
) -> Array[Cell] raise {
  let collapse_padding = self.collapse_padding
  let pad_edge = self.pad_edge
  let padding = self.padding_
  let any_padding = padding.0 != 0 ||
    padding.1 != 0 ||
    padding.2 != 0 ||
    padding.3 != 0
  let first_column = column_index == 0
  let last_column = column_index == self.columns.length() - 1
  fn get_padding(first_row : Bool, last_row : Bool) -> Array[Int] {
    let (t, r, b, l) = padding
    let mut top = t
    let mut right = r
    let mut bottom = b
    let mut left = l
    if collapse_padding {
      if !first_column {
        left = @cmp.maximum(0, left - right)
      }
      if !last_row {
        bottom = @cmp.maximum(0, top - bottom)
      }
    }
    if !pad_edge {
      if first_column {
        left = 0
      }
      if last_column {
        right = 0
      }
      if first_row {
        top = 0
      }
      if last_row {
        bottom = 0
      }
    }
    [top, right, bottom, left]
  }

  let raw_cells : Array[(Style, &Renderable)] = []
  if self.show_header {
    let header_style = console.get_style(self.header_style) +
      console.get_style(column.header_style)
    raw_cells.push((header_style, column.header))
  }
  let cell_style = console.get_style(column.style)
  for cell in column.cells_ {
    raw_cells.push((cell_style, cell))
  }
  if self.show_footer {
    let footer_style = console.get_style(self.footer_style) +
      console.get_style(column.footer_style)
    raw_cells.push((footer_style, column.footer))
  }
  let cells = []
  let n = raw_cells.length()
  for i, raw in raw_cells {
    let (style, renderable) = raw
    let vertical = renderable.vertical_align().unwrap_or(column.vertical)
    if any_padding {
      let p = get_padding(i == 0, i == n - 1)
      cells.push({
        style,
        renderable: Padding::new(renderable, pad=p),
        vertical,
      })
    } else {
      cells.push({ style, renderable, vertical, })
    }
  }
  cells
}

///|
fn Table::get_padding_width(self : Table, column_index : Int) -> Int {
  let (_, pad_right, _, pad_left) = self.padding_
  let mut pad_left = pad_left
  let mut pad_right = pad_right
  if self.collapse_padding {
    pad_left = 0
    pad_right = (pad_left - pad_right).abs()
  }
  if !self.pad_edge {
    if column_index == 0 {
      pad_left = 0
    }
    if column_index == self.columns.length() - 1 {
      pad_right = 0
    }
  }
  pad_left + pad_right
}

///|
fn Table::measure_column(
  self : Table,
  console : Console,
  options : ConsoleOptions,
  column : Column,
) -> Measurement raise {
  let max_width = options.max_width
  if max_width < 1 {
    return { minimum: 0, maximum: 0, }
  }
  let padding_width = self.get_padding_width(column.index)
  if column.width is Some(w) {
    return { minimum: w + padding_width, maximum: w + padding_width, }.with_maximum(
      max_width,
    )
  }
  let min_widths = []
  let max_widths = []
  for cell in self.get_cells(console, column.index, column) {
    let m = Measurement::get(console, options, cell.renderable)
    min_widths.push(m.minimum)
    max_widths.push(m.maximum)
  }
  fn max_of(xs : Array[Int], default : Int) -> Int {
    if xs.is_empty() {
      return default
    }
    let mut m = xs[0]
    for x in xs {
      m = @cmp.maximum(m, x)
    }
    m
  }

  let measurement = {
    minimum: max_of(min_widths, 1),
    maximum: max_of(max_widths, max_width),
  }.with_maximum(max_width)
  measurement.clamp(
    min_width?=column.min_width.map(w => w + padding_width),
    max_width?=column.max_width.map(w => w + padding_width),
  )
}

///|
fn Table::render_table(
  self : Table,
  console : Console,
  options : ConsoleOptions,
  widths : Array[Int],
) -> Array[Segment] raise {
  let out = []
  let table_style = console.get_style(self.style)
  let border_style = table_style +
    console.get_style(self.border_style.unwrap_or(Def("")))
  let column_cells = []
  for column_index, column in self.columns {
    column_cells.push(self.get_cells(console, column_index, column))
  }
  // zip(*column_cells): truncated to the shortest column
  let mut row_count = column_cells[0].length()
  for cells in column_cells {
    row_count = @cmp.minimum(row_count, cells.length())
  }
  let row_cells = []
  for r in 0.. cells[r]))
  }
  let box_ = match self.box_ {
    Some(b) => {
      let b = b.substitute(
        options,
        safe=pick_bool([self.safe_box, Some(console.safe_box)]),
      )
      if !self.show_header {
        Some(b.get_plain_headed_box())
      } else {
        Some(b)
      }
    }
    None => None
  }
  let new_line = Segment::line()
  let columns = self.columns
  let show_header = self.show_header
  let show_footer = self.show_footer
  let show_edge = self.show_edge
  let show_lines = self.show_lines
  let leading = self.leading
  let box_segments = match box_ {
    Some(b) => {
      if show_edge {
        out.push(Segment::new(b.get_top(widths), style=border_style))
        out.push(new_line)
      }
      [
        (
          Segment::new(b.head_left, style=border_style),
          Segment::new(b.head_right, style=border_style),
          Segment::new(b.head_vertical, style=border_style),
        ),
        (
          Segment::new(b.mid_left, style=border_style),
          Segment::new(b.mid_right, style=border_style),
          Segment::new(b.mid_vertical, style=border_style),
        ),
        (
          Segment::new(b.foot_left, style=border_style),
          Segment::new(b.foot_right, style=border_style),
          Segment::new(b.foot_vertical, style=border_style),
        ),
      ]
    }
    None => []
  }
  let header_offset = if show_header { 1 } else { 0 }
  for index, row_cell in row_cells {
    let first = index == 0
    let last = index == row_cells.length() - 1
    let header_row = first && show_header
    let footer_row = last && show_footer
    let row = if !header_row && !footer_row {
      Some(self.rows[index - header_offset])
    } else {
      None
    }
    let mut max_height = 1
    let cells : Array[Array[Array[Segment]]] = []
    let row_style = if header_row || footer_row {
      Style::null()
    } else {
      self.get_row_style(console, if show_header { index - 1 } else { index })
    }
    for i, cell in row_cell {
      let width = widths[i]
      let column = columns[i]
      let render_options = options.update(
        width~,
        justify=Some(column.justify),
        no_wrap=Some(column.no_wrap),
        overflow=Some(column.overflow),
        height=None,
        highlight=Some(column.highlight),
      )
      let lines = console.render_lines(
        cell.renderable,
        options=render_options,
        style=cell.style + row_style,
      )
      max_height = @cmp.maximum(max_height, lines.length())
      cells.push(lines)
    }
    let mut row_height = 0
    for cell in cells {
      row_height = @cmp.maximum(row_height, cell.length())
    }
    fn align_cell(
      cell : Array[Array[Segment]],
      vertical : VerticalAlignMethod,
      width : Int,
      style : Style,
    ) -> Array[Array[Segment]] {
      let vertical = if header_row {
        Bottom
      } else if footer_row {
        Top
      } else {
        vertical
      }
      match vertical {
        Top => Segment::align_top(cell, width, row_height, Some(style))
        Middle => Segment::align_middle(cell, width, row_height, Some(style))
        Bottom => Segment::align_bottom(cell, width, row_height, Some(style))
      }
    }

    let aligned = []
    for i, cell in cells {
      let width = widths[i]
      let c = row_cell[i]
      aligned.push(
        Segment::set_shape(
          align_cell(cell, c.vertical, width, c.style + row_style),
          width,
          height=max_height,
        ),
      )
    }
    match box_ {
      Some(b) => {
        if last && show_footer {
          out.push(
            Segment::new(
              b.get_row(widths, level=Foot, edge=show_edge),
              style=border_style,
            ),
          )
          out.push(new_line)
        }
        let (left, right, divider_) = box_segments[if first {
            0
          } else if last {
            2
          } else {
            1
          }]
        let divider = if py_strip(divider_.text) != "" {
          divider_
        } else {
          Segment::new(
            divider_.text,
            style=row_style.background_style().add_opt(divider_.style),
          )
        }
        for line_no in 0..
        for line_no in 0.. r.end_section
      None => false
    }
    if box_ is Some(b) && (show_lines || leading != 0 || end_section) {
      if !last &&
        !(show_footer && index >= row_cells.length() - 2) &&
        !(show_header && header_row) {
        if leading != 0 {
          out.push(
            Segment::new(
              b.get_row(widths, level=Mid, edge=show_edge).repeat(leading),
              style=border_style,
            ),
          )
        } else {
          out.push(
            Segment::new(
              b.get_row(widths, level=Row, edge=show_edge),
              style=border_style,
            ),
          )
        }
        out.push(new_line)
      }
    }
  }
  if box_ is Some(b) && show_edge {
    out.push(Segment::new(b.get_bottom(widths), style=border_style))
    out.push(new_line)
  }
  out
}