namespace Bliku.Tui.Cursor

/-- Scroll/selection state for list-like UIs. -/
structure State where
  selected : Nat := 0
  scroll : Nat := 0
  totalRows : Nat := 0
  viewportRows : Nat := 20
  margin : Nat := 3
  deriving Inhabited, Repr

def autoScrollRow
    (selectedRow scrollRow totalRows : Nat)
    (viewportRows : Nat := 20)
    (margin : Nat := 3) : Nat :=
  if totalRows == 0 || viewportRows == 0 then
    0
  else
    let maxScroll := totalRows - 1
    let lowBound := scrollRow + margin
    let highBound := scrollRow + (viewportRows - 1 - min margin (viewportRows - 1))
    if selectedRow < lowBound then
      min (selectedRow - margin) maxScroll
    else if selectedRow > highBound then
      let wanted := selectedRow - (viewportRows - 1 - min margin (viewportRows - 1))
      min wanted maxScroll
    else
      min scrollRow maxScroll

def State.clamp (s : State) : State :=
  let maxIdx := if s.totalRows == 0 then 0 else s.totalRows - 1
  let selected := min s.selected maxIdx
  let scroll := autoScrollRow selected s.scroll s.totalRows s.viewportRows s.margin
  { s with selected := selected, scroll := scroll }

def State.moveDown (s : State) : State :=
  let maxIdx := if s.totalRows == 0 then 0 else s.totalRows - 1
  { s with selected := min (s.selected + 1) maxIdx } |>.clamp

def State.moveUp (s : State) : State :=
  { s with selected := if s.selected > 0 then s.selected - 1 else 0 } |>.clamp

def State.moveTop (s : State) : State :=
  { s with selected := 0 } |>.clamp

def State.moveBottom (s : State) : State :=
  let maxIdx := if s.totalRows == 0 then 0 else s.totalRows - 1
  { s with selected := maxIdx } |>.clamp

end Bliku.Tui.Cursor