///|
/// Correct border junctions at span boundaries so merged cells display proper corners.
pub fn Table::correct_borders(self : Table) -> Table {
  let count_rows = self.rows.length()
  if count_rows == 0 {
    return self
  }
  let count_cols = self.rows[0].length()
  if count_cols == 0 {
    return self
  }
  let cfg = self.config
  let borders = cfg.borders()
  // Phase 1: Fix col span junctions
  for row in 0.. s
        None => continue
      }
      if span <= 1 {
        continue
      }
      for c = col; c < col + span && c < count_cols; c = c + 1 {
        if c == 0 {
          continue
        }
        let is_first = c == col
        let has_up = row > 0 &&
          cfg.has_left_border(row - 1, c, count_rows, count_cols)
        let has_down = row + 1 < count_rows &&
          cfg.has_left_border(row + 1, c, count_rows, count_cols)
        let border = cfg.get_cell_border(row, c, count_rows, count_cols)
        // Fix top-left corner
        let has_top_border = !(border.left_top_corner is None) &&
          !(border.top is None)
        let new_ltc = if has_top_border {
          if has_up && is_first {
            borders.intersection
          } else if has_up {
            borders.bottom_intersection
          } else if is_first {
            borders.top_intersection
          } else {
            border.top
          }
        } else {
          None
        }
        // Fix bottom-left corner
        let has_bottom_border = !(border.left_bottom_corner is None) &&
          !(border.bottom is None)
        let new_lbc = if has_bottom_border {
          if has_down && is_first {
            borders.intersection
          } else if has_down {
            borders.top_intersection
          } else if is_first {
            borders.bottom_intersection
          } else {
            border.bottom
          }
        } else {
          None
        }
        let cb : @papergrid.CellBorder = {
          top: None,
          bottom: None,
          left: None,
          right: None,
          left_top_corner: new_ltc,
          right_top_corner: None,
          left_bottom_corner: new_lbc,
          right_bottom_corner: None,
        }
        cfg.set_cell_border(row, c, cb)
      }
    }
  }
  // Phase 2: Fix row span junctions
  for row in 0.. s
        None => continue
      }
      if span <= 1 {
        continue
      }
      for r = row + 1; r < row + span && r < count_rows; r = r + 1 {
        let border = cfg.get_cell_border(r, col, count_rows, count_cols)
        // Fix left-top corner (left junction)
        let new_ltc = if !(border.left_top_corner is None) {
          let has_left = col > 0 &&
            cfg.has_top_border(r, col - 1, count_rows, count_cols)
          if has_left {
            borders.right_intersection
          } else {
            borders.vertical
          }
        } else {
          None
        }
        // Fix right-top corner (right junction)
        let new_rtc = if !(border.right_top_corner is None) {
          let has_right = col + 1 < count_cols &&
            cfg.has_top_border(r, col + 1, count_rows, count_cols)
          if has_right {
            borders.left_intersection
          } else {
            borders.vertical
          }
        } else {
          None
        }
        let cb : @papergrid.CellBorder = {
          top: None,
          bottom: None,
          left: None,
          right: None,
          left_top_corner: new_ltc,
          right_top_corner: new_rtc,
          left_bottom_corner: None,
          right_bottom_corner: None,
        }
        cfg.set_cell_border(r, col, cb)
      }
    }
  }
  // Phase 3: Fix cells covered by both row and col spans
  for row in 0.. Shadow {
  {
    size,
    offset: 1,
    fill: '▒',
    top: false,
    bottom: true,
    left: false,
    right: true,
  }
}

///|
pub enum HighlightTarget {
  HCell(Cell)
  HRows(Rows)
  HCols(Cols)
  HSegment(Segment)
}

///|
pub struct Highlight {
  target : HighlightTarget
  border : @papergrid.CellBorder
}

///|
pub fn Highlight::outline_cell(cell : Cell, ch : Char) -> Highlight {
  { target: HCell(cell), border: @papergrid.CellBorder::filled(ch) }
}

///|
pub fn Highlight::outline_rows(rows : Rows, ch : Char) -> Highlight {
  { target: HRows(rows), border: @papergrid.CellBorder::filled(ch) }
}

///|
pub fn Highlight::outline_cols(cols : Cols, ch : Char) -> Highlight {
  { target: HCols(cols), border: @papergrid.CellBorder::filled(ch) }
}

///|
pub fn Highlight::outline_segment(segment : Segment, ch : Char) -> Highlight {
  { target: HSegment(segment), border: @papergrid.CellBorder::filled(ch) }
}

///|
pub fn Highlight::new_cell(cell : Cell) -> Highlight {
  { target: HCell(cell), border: @papergrid.CellBorder::empty() }
}

///|
pub fn Highlight::new_rows(rows : Rows) -> Highlight {
  { target: HRows(rows), border: @papergrid.CellBorder::empty() }
}

///|
pub fn Highlight::new_cols(cols : Cols) -> Highlight {
  { target: HCols(cols), border: @papergrid.CellBorder::empty() }
}

///|
pub fn Highlight::new_segment(segment : Segment) -> Highlight {
  { target: HSegment(segment), border: @papergrid.CellBorder::empty() }
}

///|
pub fn Highlight::with_border(
  self : Highlight,
  border : @papergrid.CellBorder,
) -> Highlight {
  { ..self, border, }
}

///|
fn resolve_target_to_cells(
  target : HighlightTarget,
  count_rows : Int,
  count_cols : Int,
) -> Array[(Int, Int)] {
  match target {
    HCell(cell) =>
      if cell.row >= 0 &&
        cell.row < count_rows &&
        cell.col >= 0 &&
        cell.col < count_cols {
        [(cell.row, cell.col)]
      } else {
        []
      }
    HRows(rows) => {
      let row_indexes = resolve_rows(rows, count_rows)
      let cells = []
      row_indexes.each(row => {
        for col in 0.. {
      let col_indexes = resolve_cols(cols, count_cols)
      let cells = []
      for row in 0.. cells.push((row, col)))
      }
      cells
    }
    HSegment(segment) => resolve_segment_cells(segment, count_rows, count_cols)
  }
}

///|
fn is_cell_connected(p1 : (Int, Int), p2 : (Int, Int)) -> Bool {
  if p1.0 == p2.0 {
    // same row: check left/right adjacency
    if p1.1 + 1 == p2.1 || p2.1 + 1 == p1.1 {
      return true
    }
  }
  if p1.1 == p2.1 {
    // same col: check up/down adjacency
    if p1.0 + 1 == p2.0 || p2.0 + 1 == p1.0 {
      return true
    }
  }
  false
}

///|
fn split_segments(cells : Array[(Int, Int)]) -> Array[Array[(Int, Int)]] {
  let segments : Array[Array[(Int, Int)]] = []
  cells.each(cell => {
    let mut found_index = -1
    for i in 0..= 0 {
        break
      }
    }
    if found_index >= 0 {
      segments[found_index].push(cell)
    } else {
      segments.push([cell])
    }
  })
  // Merge segments that are connected to each other
  let result : Array[Array[(Int, Int)]] = []
  while !segments.is_empty() {
    let segment = segments.remove(0)
    let mut i = 0
    while i < segments.length() {
      let mut connected = false
      for j in 0.. segment.push(c))
        ignore(segments.remove(i))
      } else {
        i = i + 1
      }
    }
    result.push(segment)
  }
  result
}

///|
fn sector_contains(sector : Array[(Int, Int)], p : (Int, Int)) -> Bool {
  sector.contains(p)
}

///|
fn build_highlight_cell_border(
  sector : Array[(Int, Int)],
  p : (Int, Int),
  border : @papergrid.CellBorder,
) -> @papergrid.CellBorder {
  let has_top = p.0 > 0 && sector_contains(sector, (p.0 - 1, p.1))
  let has_bottom = sector_contains(sector, (p.0 + 1, p.1))
  let has_left = p.1 > 0 && sector_contains(sector, (p.0, p.1 - 1))
  let has_right = sector_contains(sector, (p.0, p.1 + 1))
  let has_lt = p.0 > 0 && p.1 > 0 && sector_contains(sector, (p.0 - 1, p.1 - 1))
  let has_rt = p.0 > 0 && sector_contains(sector, (p.0 - 1, p.1 + 1))
  let has_lb = p.1 > 0 && sector_contains(sector, (p.0 + 1, p.1 - 1))
  let has_rb = sector_contains(sector, (p.0 + 1, p.1 + 1))
  let mut b = @papergrid.CellBorder::empty()
  // Top side
  if !has_top {
    b = { ..b, top: border.top }
    if has_right && !has_rt {
      b = { ..b, right_top_corner: border.top }
    }
  }
  // Bottom side
  if !has_bottom {
    b = { ..b, bottom: border.bottom }
    if has_right && !has_rb {
      b = { ..b, right_bottom_corner: border.bottom }
    }
  }
  // Left side
  if !has_left {
    b = { ..b, left: border.left }
    if has_bottom && !has_lb {
      b = { ..b, left_bottom_corner: border.left }
    }
  }
  // Right side
  if !has_right {
    b = { ..b, right: border.right }
    if has_bottom && !has_rb {
      b = { ..b, right_bottom_corner: border.right }
    }
  }
  // Corners (only when no neighbor in both directions)
  if !has_left && !has_top {
    b = { ..b, left_top_corner: border.left_top_corner }
  }
  if !has_left && !has_bottom {
    b = { ..b, left_bottom_corner: border.left_bottom_corner }
  }
  if !has_right && !has_top {
    b = { ..b, right_top_corner: border.right_top_corner }
  }
  if !has_right && !has_bottom {
    b = { ..b, right_bottom_corner: border.right_bottom_corner }
  }
  // Diagonal rules for L-shaped segments
  if !has_bottom {
    if !has_left && has_lt {
      b = { ..b, left_top_corner: border.right_top_corner }
    }
    if has_left && has_lb {
      b = { ..b, left_bottom_corner: border.left_top_corner }
    }
    if !has_right && has_rt {
      b = { ..b, right_top_corner: border.left_top_corner }
    }
    if has_right && has_rb {
      b = { ..b, right_bottom_corner: border.right_top_corner }
    }
  }
  if !has_top {
    if !has_left && has_lb {
      b = { ..b, left_bottom_corner: border.right_bottom_corner }
    }
    if has_left && has_lt {
      b = { ..b, left_top_corner: border.left_bottom_corner }
    }
    if !has_right && has_rb {
      b = { ..b, right_bottom_corner: border.left_bottom_corner }
    }
    if has_right && has_rt {
      b = { ..b, right_top_corner: border.right_bottom_corner }
    }
  }
  b
}

///|
pub fn Table::highlight(self : Table, highlight : Highlight) -> Table {
  let count_rows = self.rows.length()
  if count_rows == 0 {
    return self
  }
  let count_cols = self.rows[0].length()
  if count_cols == 0 {
    return self
  }
  let cells = resolve_target_to_cells(highlight.target, count_rows, count_cols)
  if cells.is_empty() {
    return self
  }
  let segments = split_segments(cells)
  segments.each(segment => {
    segment.each(p => {
      let cell_border = build_highlight_cell_border(
        segment,
        p,
        highlight.border,
      )
      self.config.set_cell_border(p.0, p.1, cell_border)
    })
  })
  self
}

///|
pub struct Shadow {
  size : Int
  offset : Int
  fill : Char
  top : Bool
  bottom : Bool
  left : Bool
  right : Bool
}

///|
pub fn Shadow::set_fill(self : Shadow, c : Char) -> Shadow {
  { ..self, fill: c }
}

///|
pub fn Shadow::set_offset(self : Shadow, offset : Int) -> Shadow {
  { ..self, offset, }
}

///|
pub fn Shadow::set_top(self : Shadow) -> Shadow {
  { ..self, top: true, bottom: false }
}

///|
pub fn Shadow::set_bottom(self : Shadow) -> Shadow {
  { ..self, top: false, bottom: true }
}

///|
pub fn Shadow::set_left(self : Shadow) -> Shadow {
  { ..self, left: true, right: false }
}

///|
pub fn Shadow::set_right(self : Shadow) -> Shadow {
  { ..self, left: false, right: true }
}

///|
pub fn Table::shadow(self : Table, shadow : Shadow) -> Table {
  let fill = shadow.fill
  let top_size = if shadow.top { shadow.size } else { 0 }
  let bottom_size = if shadow.bottom { shadow.size } else { 0 }
  let left_size = if shadow.left { shadow.size } else { 0 }
  let right_size = if shadow.right { shadow.size } else { 0 }
  self.config.set_margin(
    top_size, bottom_size, left_size, right_size, fill, fill, fill, fill,
  )
  // Set offsets: the offset determines where fill starts on perpendicular sides
  let h_offset = shadow.offset
  let v_offset = shadow.offset
  // For right+bottom (default): start offset on both
  // For left: end offset on vertical, start offset on horizontal
  // For top: end offset on horizontal, start offset on vertical
  let top_offset = if shadow.top { h_offset } else { 0 }
  let bottom_offset = if shadow.bottom { h_offset } else { 0 }
  let left_offset = if shadow.left { v_offset } else { 0 }
  let right_offset = if shadow.right { v_offset } else { 0 }
  self.config.set_margin_offset(
    top_offset, bottom_offset, left_offset, right_offset,
  )
  self
}