text editor inspired vim and yi
import ViE.Data.PieceTable
import Test.Utils

namespace Test.TreeStats

open ViE
open Test.Utils

def buildWorkload (iterations : Nat) : PieceTable := Id.run do
  let mut pt := PieceTable.fromString "Initial content\n"
  for i in [0:iterations] do
    pt := pt.insert pt.tree.length s!"line {i}\n" pt.tree.length
  return pt

mutual
  def countNodes (t : PieceTree) : Nat :=
    match t with
    | .empty => 0
    | .leaf _ _ _ => 1
    | .internal cs _ _ => 1 + countNodesList cs.toList

  def countNodesList (ts : List PieceTree) : Nat :=
    match ts with
    | [] => 0
    | t :: rest => countNodes t + countNodesList rest
end

mutual
  def countLeaves (t : PieceTree) : Nat :=
    match t with
    | .empty => 0
    | .leaf _ _ _ => 1
    | .internal cs _ _ => countLeavesList cs.toList

  def countLeavesList (ts : List PieceTree) : Nat :=
    match ts with
    | [] => 0
    | t :: rest => countLeaves t + countLeavesList rest
end

mutual
  def maxLeafPieces (t : PieceTree) : Nat :=
    match t with
    | .empty => 0
    | .leaf ps _ _ => ps.size
    | .internal cs _ _ => maxLeafPiecesList cs.toList

  def maxLeafPiecesList (ts : List PieceTree) : Nat :=
    match ts with
    | [] => 0
    | t :: rest => max (maxLeafPieces t) (maxLeafPiecesList rest)
end

mutual
  def maxInternalChildren (t : PieceTree) : Nat :=
    match t with
    | .empty => 0
    | .leaf _ _ _ => 0
    | .internal cs _ _ =>
        max cs.size (maxInternalChildrenList cs.toList)

  def maxInternalChildrenList (ts : List PieceTree) : Nat :=
    match ts with
    | [] => 0
    | t :: rest => max (maxInternalChildren t) (maxInternalChildrenList rest)
end

def parseIterations (args : List String) : Nat :=
  let args :=
    match args with
    | "--" :: rest => rest
    | _ => args
  match args with
  | a :: _ =>
      match a.toNat? with
      | some n => n
      | none => 5000
  | [] => 5000

def report (pt : PieceTable) (iterations : Nat) : IO Unit := do
  let tree := pt.tree
  let h := PieceTree.height tree
  let nodes := countNodes tree
  let leaves := countLeaves tree
  let maxLeaf := maxLeafPieces tree
  let maxChildren := maxInternalChildren tree
  IO.println s!"iterations={iterations}"
  IO.println s!"bytes={PieceTree.length tree} lines={PieceTree.lineBreaks tree} height={h}"
  IO.println s!"nodes={nodes} leaves={leaves} maxLeafPieces={maxLeaf} maxChildren={maxChildren}"

  match tree with
  | .empty => IO.println "root=empty"
  | .leaf ps _ _ => IO.println s!"root=leaf pieces={ps.size}"
  | .internal cs _ _ => IO.println s!"root=internal children={cs.size}"

  if iterations > 0 then
    let start := ← IO.monoMsNow
    let _ := pt.getLineRange (iterations - 1)
    let stop := ← IO.monoMsNow
    IO.println s!"getLineRange(last)={stop - start}ms"

def test : IO Unit := do
  IO.println "Starting TreeStats Test..."
  let iterations := 3000
  let pt := buildWorkload iterations
  let t := pt.tree
  let bytes := PieceTree.length t
  let textBytes := pt.toString.toUTF8.size
  let isNonEmpty :=
    match t with
    | .empty => false
    | _ => true

  assertEqual "tree length equals rendered bytes" textBytes bytes
  assert "tree is non-empty" isNonEmpty
  assert "tree height is positive for non-empty tree" (PieceTree.height t >= 1)
  assert "leaf piece count stays within node capacity" (maxLeafPieces t <= NodeCapacity)
  assert "internal children stay within node capacity" (maxInternalChildren t <= NodeCapacity)
  assert "line range for last inserted line exists" ((pt.getLineRange (iterations - 1)).isSome)
  IO.println "TreeStats Test passed!"

def main (args : List String) : IO Unit := do
  let iterations := parseIterations args
  let pt := buildWorkload iterations
  report pt iterations

end Test.TreeStats