text editor inspired vim and yi
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka
--
-- SPDX-License-Identifier: BSD-3

import ViE.State
import ViE.Types
import ViE.Buffer.Content
import ViE.Buffer.LowIO
import ViE.Workspace

namespace ViE.Buffer

open ViE

def createNewBuffer (state : EditorState) (content : TextBuffer) (filename : Option String) : (Nat × EditorState) :=
  let s' := state.updateCurrentWorkspace fun ws =>
    let id := ws.nextBufferId
    let buildLeafBits := state.config.searchBloomBuildLeafBits
    let buf : FileBuffer := ViE.Buffer.fileBufferFromTextBufferWithConfig id filename content buildLeafBits
    { ws with buffers := buf :: ws.buffers, nextBufferId := id + 1 }
  let ws := s'.getCurrentWorkspace
  (ws.nextBufferId - 1, s')

def addBuffer (state : EditorState) (buf : FileBuffer) : (Nat × EditorState) :=
  let s' := state.updateCurrentWorkspace fun ws =>
    let id := ws.nextBufferId
    let newBuf := { buf with id := id }
    { ws with buffers := newBuf :: ws.buffers, nextBufferId := id + 1 }
  let ws := s'.getCurrentWorkspace
  (ws.nextBufferId - 1, s')

def removeBuffer (state : EditorState) (bufId : Nat) : EditorState :=
  state.updateCurrentWorkspace fun ws =>
    { ws with buffers := ws.buffers.filter (fun b => b.id != bufId) }

def ensureBufferLoadedById (state : EditorState) (bufId : Nat) : IO EditorState := do
  let ws := state.getCurrentWorkspace
  match ws.buffers.find? (fun b => b.id == bufId) with
  | none => pure state
  | some buf =>
      if buf.loaded || buf.filename.isNone then
        pure state
      else
        let fname := buf.filename.getD ""
        let loadedBuf ← ViE.loadBufferByteArrayWithConfig fname state.config
        let merged := {
          loadedBuf with
            id := buf.id
            dirty := buf.dirty
            loaded := true
            table := { loadedBuf.table with undoLimit := state.config.historyLimit }
        }
        let s' := state.updateCurrentWorkspace fun ws =>
          { ws with buffers := ws.buffers.map (fun b => if b.id == buf.id then merged else b) }
        pure { s' with message := s!"Loaded \"{fname}\"" }

def ensureActiveBufferLoaded (state : EditorState) : IO EditorState := do
  let ws := state.getCurrentWorkspace
  match ws.layout.findView ws.activeWindowId with
  | none => pure state
  | some v => ensureBufferLoadedById state v.bufferId

def findBufferByFilename (state : EditorState) (fname : String) : Option Nat :=
  let ws := state.getCurrentWorkspace
  ws.buffers.find? (fun b => b.filename == some fname) |>.map (fun b => b.id)

/-- Open a file buffer in the active window. Loads from disk if necessary. -/
def openBuffer (state : EditorState) (filename : String) : IO EditorState := do
  let resolvedPath := state.getCurrentWorkspace.resolvePath filename
  match findBufferByFilename state resolvedPath with
  | some bid =>
    -- Switch to existing buffer; load if it is a lazy placeholder.
    let s' := state.updateActiveView fun v => { v with bufferId := bid, cursor := {row:=0, col:=0}, scrollRow:=0, scrollCol:=0 }
    let s'' ← ensureBufferLoadedById s' bid
    return { s'' with message := s!"Switched to \"{filename}\"" }
  | none =>
    try
      let loadedBuf ← ViE.loadBufferByteArrayWithConfig resolvedPath state.config
      -- Assign new ID and add to workgroup
      let s' := state.updateCurrentWorkspace fun ws =>
        let id := ws.nextBufferId
        let buf := { loadedBuf with
          id := id
          table := { loadedBuf.table with undoLimit := state.config.historyLimit }
        }
        { ws with buffers := buf :: ws.buffers, nextBufferId := id + 1 }

      let ws := s'.getCurrentWorkspace
      let bid := ws.nextBufferId - 1

      let s'' := s'.updateActiveView fun v => { v with bufferId := bid, cursor := {row:=0, col:=0}, scrollRow:=0, scrollCol:=0 }
      return { s'' with message := s!"Loaded \"{filename}\"" }
    catch e =>
      return { state with message := s!"Error loading file: {e}" }


end ViE.Buffer