text editor inspired vim and yi
import Lean
import ViE.Data.PieceTable

namespace ViE

/-- Editor Mode -/
inductive Mode where
  | normal
  | insert
  | command
  | searchForward
  | searchBackward
  | visual
  | visualBlock
  deriving Repr, BEq, Inhabited

instance : ToString Mode where
  toString
    | .normal => "NORMAL"
    | .insert => "INSERT"
    | .command => "COMMAND"
    | .searchForward => "SEARCH"
    | .searchBackward => "SEARCH"
    | .visual => "VISUAL"
    | .visualBlock => "VISUAL BLOCK"

/-- Row index (0-based) with dimensional safety -/
structure Row where
  val : Nat
  deriving Repr, BEq, Ord, Hashable

/-- Column index (0-based) with dimensional safety -/
structure Col where
  val : Nat
  deriving Repr, BEq, Ord

/-- Cursor position with dimensional type safety -/
structure Point where
  row : Row
  col : Col
  deriving Repr, BEq

-- Row helper functions
namespace Row
  def zero : Row := ⟨0⟩
  def succ (r : Row) : Row := ⟨r.val + 1⟩
  def pred (r : Row) : Row := ⟨r.val.pred⟩
  instance : ToString Row := ⟨fun r => toString r.val⟩
end Row

instance : OfNat Row n := ⟨⟨n⟩⟩
instance : LT Row := ⟨fun a b => a.val < b.val⟩
instance : LE Row := ⟨fun a b => a.val ≤ b.val⟩
instance (a b : Row) : Decidable (a < b) := inferInstanceAs (Decidable (a.val < b.val))
instance (a b : Row) : Decidable (a ≤ b) := inferInstanceAs (Decidable (a.val ≤ b.val))
instance : Min Row := ⟨fun a b => if a.val ≤ b.val then a else b⟩
instance : Max Row := ⟨fun a b => if a.val ≥ b.val then a else b⟩
instance : Add Row := ⟨fun a b => ⟨a.val + b.val⟩⟩
instance : Sub Row := ⟨fun a b => ⟨a.val - b.val⟩⟩
instance : HAdd Row Nat Row := ⟨fun r n => ⟨r.val + n⟩⟩
instance : HSub Row Nat Row := ⟨fun r n => ⟨r.val - n⟩⟩
instance : Coe Row Nat := ⟨(·.val)⟩

instance : GetElem (List α) Row α (fun l r => r.val < l.length) where
  getElem l r h := l[r.val]

instance : GetElem (Array α) Row α (fun a r => r.val < a.size) where
  getElem a r h := a[r.val]

-- Col helper functions
namespace Col
  def zero : Col := ⟨0⟩
  def succ (c : Col) : Col := ⟨c.val + 1⟩
  def pred (c : Col) : Col := ⟨c.val.pred⟩
  instance : ToString Col := ⟨fun c => toString c.val⟩
end Col

instance : OfNat Col n := ⟨⟨n⟩⟩
instance : LT Col := ⟨fun a b => a.val < b.val⟩
instance : LE Col := ⟨fun a b => a.val ≤ b.val⟩
instance (a b : Col) : Decidable (a < b) := inferInstanceAs (Decidable (a.val < b.val))
instance (a b : Col) : Decidable (a ≤ b) := inferInstanceAs (Decidable (a.val ≤ b.val))
instance : Min Col := ⟨fun a b => if a.val ≤ b.val then a else b⟩
instance : Max Col := ⟨fun a b => if a.val ≥ b.val then a else b⟩
instance : Add Col := ⟨fun a b => ⟨a.val + b.val⟩⟩
instance : Sub Col := ⟨fun a b => ⟨a.val - b.val⟩⟩
instance : HAdd Col Nat Col := ⟨fun c n => ⟨c.val + n⟩⟩
instance : HSub Col Nat Col := ⟨fun c n => ⟨c.val - n⟩⟩
instance : Coe Col Nat := ⟨(·.val)⟩

instance : GetElem (List α) Col α (fun l c => c.val < l.length) where
  getElem l c h := l[c.val]

instance : GetElem (Array α) Col α (fun a c => c.val < a.size) where
  getElem a c h := a[c.val]

-- Point helper functions
namespace Point
  def zero : Point := ⟨Row.zero, Col.zero⟩
  def make (row : Row) (col : Col) : Point := ⟨row, col⟩
  def fromNat (r c : Nat) : Point := ⟨⟨r⟩, ⟨c⟩⟩
end Point

-- Inhabited instances
instance : Inhabited Row := ⟨Row.zero⟩
instance : Inhabited Col := ⟨Col.zero⟩
instance : Inhabited Point := ⟨Point.zero⟩

/-- A line is an array of characters for O(1) indexing -/
abbrev Line := Array Char

/-- A text buffer is an array of lines (2D character array) -/
abbrev TextBuffer := Array Line

structure EditorConfig where
  showLineNumbers : Bool
  vSplitStr : String
  hSplitStr : String
  emptyLineMarker : String
  statusBarStyle : String
  resetStyle : String
  fileIcon : String
  dirIcon : String
  searchHighlightStyle : String
  searchHighlightCursorStyle : String
  tabStop : Nat := 4
  searchBloomCacheMax : Nat
  searchBloomBuildLeafBits : Bool
  historyLimit : Nat := 100
  deriving Inhabited

inductive SearchDirection where
  | forward
  | backward
  deriving Repr, Inhabited, BEq

structure SearchState where
  pattern : String
  direction : SearchDirection
  useBloom : Bool
  lastMatch : Option Nat
  lastSearchOffset : Nat
  cache : Array Nat
  cacheMax : Nat
  lineCacheBufferId : Option Nat := none
  lineMatches : Lean.RBMap Row (String × Array (Nat × Nat)) compare
  lineOrder : Array Row
  lineCacheMax : Nat
  bloomCache : Lean.RBMap Nat ByteArray compare
  bloomOrder : Array Nat
  bloomCacheMax : Nat
  deriving Inhabited

inductive RegisterKind where
  | charwise
  | linewise
  | blockwise
  deriving Repr, Inhabited, BEq

structure Register where
  kind : RegisterKind
  text : String
  blockLines : List String
  blockWidth : Nat
  deriving Repr, Inhabited

inductive Key where
  | char (c : Char)
  | ctrl (c : Char)
  | alt (c : Char)
  | esc
  | backspace
  | enter
  | left | right | up | down
  | unknown (c : Char)
  deriving Repr, BEq, Inhabited

structure RenderCache where
  /-- Visual string for a line, cached to avoid re-calculating widths/tabs/etc. -/
  -- Cache key includes scroll column and available width to avoid stale renders.
  lineMap : Lean.RBMap Row (String × Nat × Nat) compare
  /-- Raw line string cache (no display transform). -/
  rawLineMap : Lean.RBMap Row String compare
  /-- Display-column to byte-offset index cache for a line. -/
  lineIndexMap : Lean.RBMap Row (Array (Nat × Nat)) compare
  deriving Inhabited

namespace RenderCache
  def find (c : RenderCache) (r : Row) : Option (String × Nat × Nat) :=
    c.lineMap.find? r

  def findRaw (c : RenderCache) (r : Row) : Option String :=
    c.rawLineMap.find? r

  def findIndex (c : RenderCache) (r : Row) : Option (Array (Nat × Nat)) :=
    c.lineIndexMap.find? r

  def update (c : RenderCache) (r : Row) (s : String) (scrollCol : Nat) (availableWidth : Nat) : RenderCache :=
    { c with lineMap := c.lineMap.insert r (s, scrollCol, availableWidth) }

  def updateRaw (c : RenderCache) (r : Row) (s : String) : RenderCache :=
    { c with rawLineMap := c.rawLineMap.insert r s }

  def updateIndex (c : RenderCache) (r : Row) (idx : Array (Nat × Nat)) : RenderCache :=
    { c with lineIndexMap := c.lineIndexMap.insert r idx }
end RenderCache

structure FileBuffer where
  id : Nat
  filename : Option String
  dirty : Bool
  table : PieceTable
  missingEol : Bool
  cache : RenderCache
  deriving Inhabited

-- Manual Repr instance
instance : Repr FileBuffer where
  reprPrec buf _ :=
    s!"FileBuffer(id={buf.id}, file={buf.filename}, dirty={buf.dirty}, lines={buf.table.lineCount})"

def initialFileBuffer : FileBuffer := {
  id := 0
  filename := none
  dirty := false
  table := ViE.PieceTable.fromString ""
  missingEol := false
  cache := { lineMap := Lean.RBMap.empty, rawLineMap := Lean.RBMap.empty, lineIndexMap := Lean.RBMap.empty }
}

namespace FileBuffer
  def lineCount (buf : FileBuffer) : Nat :=
    buf.table.lineCount

  def recomputeMissingEol (buf : FileBuffer) : FileBuffer :=
    let len := buf.table.length
    let missing := if len == 0 then false else !buf.table.endsWithNewline
    { buf with missingEol := missing }

  def clearCache (buf : FileBuffer) : FileBuffer :=
    { buf with cache := { lineMap := Lean.RBMap.empty, rawLineMap := Lean.RBMap.empty, lineIndexMap := Lean.RBMap.empty } }
end FileBuffer

structure ViewState where
  bufferId : Nat
  cursor : Point
  scrollRow : Row
  scrollCol : Col
  deriving Repr, Inhabited

/-- File entry for explorer -/
structure FileEntry where
  name : String
  path : String
  isDirectory : Bool
  deriving Repr, Inhabited

/-- Explorer state for file navigation -/
inductive ExplorerMode where
  | files
  | workgroups
  | workspaces
  | buffers
  deriving Repr, Inhabited, BEq

structure ExplorerState where
  currentPath : String
  entries : List FileEntry
  selectedIndex : Nat
  mode : ExplorerMode
  previewWindowId : Option Nat
  previewBufferId : Option Nat
  targetWindowId : Option Nat := none
  deriving Repr, Inhabited

structure FloatingOverlay where
  title : String
  lines : Array String
  maxWidth : Nat := 0
  cursorRow : Nat := 0
  cursorCol : Nat := 0
  deriving Repr, Inhabited

inductive Layout where
  | window (id : Nat) (view : ViewState)
  | hsplit (left right : Layout) (ratio : Float)
  | vsplit (top bottom : Layout) (ratio : Float)
  deriving Repr, Inhabited

/-- Temporary input state that doesn't need to persist -/
structure InputState where
  previousKey : Option Char
  countBuffer : String
  commandBuffer : String
  pendingKeys : String
  lastFindChar : Option Char := none
  lastFindForward : Bool := true
  lastFindTill : Bool := false
  lastInputTime : Nat
  lastSearchInputTime : Nat
  lastSearchRunTime : Nat
  pendingSearch : Bool
  deriving Repr, Inhabited


/-- State for a single workspace (project unit). -/
structure WorkspaceState where
  name : String
  rootPath : Option String
  buffers : List FileBuffer
  nextBufferId : Nat
  layout : Layout
  activeWindowId : Nat
  nextWindowId : Nat
  floatingWindows : List Nat := []
  floatingWindowOffsets : List (Nat × Int × Int) := []
  deriving Repr, Inhabited

/-- State for a single workgroup (collection of workspaces). -/
structure WorkgroupState where
  name : String
  workspaces : Array WorkspaceState
  currentWorkspace : Nat
  deriving Repr, Inhabited

structure EditorState where
  mode : Mode
  -- Workgroup management
  workgroups : Array WorkgroupState
  currentGroup : Nat
  -- Global State
  message : String
  shouldQuit : Bool
  config : EditorConfig
  clipboard : Option Register -- Yank buffer
  selectionStart : Option Point -- Visual mode anchor
  explorers : List (Nat × ExplorerState) -- BufferId × Explorer state
  searchState : Option SearchState
  floatingOverlay : Option FloatingOverlay
  floatingInputCommand : Option String := none
  jumpBack : List Point := []
  jumpForward : List Point := []
  -- Temporary input state
  inputState : InputState
  -- UI dimensions (updated frequently)
  windowHeight : Nat
  windowWidth : Nat
  -- Rendering optimization
  dirty : Bool

/-- Key mapping function type. -/
structure KeyMap where
  normal : EditorState → Key → IO EditorState
  insert : EditorState → Key → IO EditorState
  command : EditorState → Key → IO EditorState
  visual : EditorState → Key → IO EditorState
  visualBlock : EditorState → Key → IO EditorState

/-- Command action type: arguments -> state -> IO new state -/
abbrev CommandAction := List String → EditorState → IO EditorState

/-- Map of command names to actions -/
abbrev CommandMap := List (String × CommandAction)

/-- The complete user configuration. -/
structure Config where
  settings : EditorConfig
  bindings : KeyMap
  commands : CommandMap

inductive Direction where
  | left
  | right
  | up
  | down
  deriving Repr, BEq, Inhabited

structure SessionState where
  files : List String
  activeFileIndex : Nat
  cursors : List (Row × Col)
  deriving Repr, Inhabited

end ViE