# Markus Maiwald (Architect) | Voxis Forge (AI)
# Rumpk Phase 23: The Sovereign Filesystem (SFS) v2
# Features: Multi-Sector Files (Linked List), Block Alloc Map (BAM)

proc kprintln(s: cstring) {.importc, cdecl.}
proc kprint(s: cstring) {.importc, cdecl.}
proc kprint_hex(n: uint64) {.importc, cdecl.}

# =========================================================
# SFS Configurations
# =========================================================

const SFS_MAGIC* = 0x31534653'u32
const SEC_SB = 0
const SEC_BAM = 1
const SEC_DIR = 2

# Linked List Payload: 508 bytes data + 4 bytes next_sector
const CHUNK_SIZE = 508
const NEXT_PTR_OFFSET = 508
const EOF_MARKER = 0xFFFFFFFF'u32

type
  Superblock* = object
    magic*: uint32
    disk_size*: uint32

  DirEntry* = object
    filename*: array[32, char]
    start_sector*: uint32
    size_bytes*: uint32
    reserved*: array[24, byte]

var sfs_mounted: bool = false
var io_buffer: array[512, byte]

proc virtio_blk_read(sector: uint64, buf: pointer) {.importc, cdecl.}
proc virtio_blk_write(sector: uint64, buf: pointer) {.importc, cdecl.}

# =========================================================
# Helpers
# =========================================================

proc sfs_set_bam(sector: uint32) =
  # Read BAM
  virtio_blk_read(SEC_BAM, addr io_buffer[0])
  let byteIndex = int(sector div 8)
  let bitIndex = int(sector mod 8)
  if byteIndex < 512:
    io_buffer[byteIndex] = io_buffer[byteIndex] or byte(1 shl bitIndex)
    virtio_blk_write(SEC_BAM, addr io_buffer[0])

proc sfs_alloc_sector(): uint32 =
  # Simple allocator: Scan BAM for first 0 bit
  virtio_blk_read(SEC_BAM, addr io_buffer[0])

  for i in 0..<512:
    if io_buffer[i] != 0xFF:
      # Found a byte with free space
      for b in 0..7:
        if (io_buffer[i] and byte(1 shl b)) == 0:
          # Found free bit
          let sec = uint32(i * 8 + b)
          # Mark applied in sfs_set_bam but for efficiency do it here/flush
          io_buffer[i] = io_buffer[i] or byte(1 shl b)
          virtio_blk_write(SEC_BAM, addr io_buffer[0])
          return sec
  return 0 # Error / Full

# =========================================================
# SFS API
# =========================================================

proc sfs_is_mounted*(): bool = sfs_mounted

proc sfs_mount*() =
  kprintln("[SFS] Mounting System v2...")

  # 1. Read Sector 0 (Superblock)
  virtio_blk_read(SEC_SB, addr io_buffer[0])

  # 2. Check Magic (SFS2)
  if io_buffer[0] == byte('S') and io_buffer[1] == byte('F') and
     io_buffer[2] == byte('S') and io_buffer[3] == byte('2'):
    kprintln("[SFS] Mount SUCCESS. Version 2 (Linked Chain).")
    sfs_mounted = true
  else:
    kprint("[SFS] Mount FAILED. Invalid Magic/Ver. Found: ")
    kprint_hex(cast[uint64](io_buffer[0]))
    kprintln("")

proc sfs_list*() =
  if not sfs_mounted: return

  virtio_blk_read(SEC_DIR, addr io_buffer[0])
  kprintln("[SFS] Files:")

  var offset = 0
  while offset < 512:
    if io_buffer[offset] != 0:
      var name: string = ""
      for i in 0..31:
        let c = char(io_buffer[offset+i])
        if c == '\0': break
        name.add(c)
      kprint("  - ")
      kprintln(cstring(name))
    offset += 64

proc sfs_get_files*(): string =
  var res = ""
  if not sfs_mounted: return res

  virtio_blk_read(SEC_DIR, addr io_buffer[0])
  for offset in countup(0, 511, 64):
    if io_buffer[offset] != 0:
      var name = ""
      for i in 0..31:
        let c = char(io_buffer[offset+i])
        if c == '\0': break
        name.add(c)
      res.add(name)
      res.add("\n")
  return res

proc sfs_write_file*(name: cstring, data: cstring, data_len: int) {.exportc, cdecl.} =
  if not sfs_mounted: return

  virtio_blk_read(SEC_DIR, addr io_buffer[0])

  var dir_offset = -1
  var start_sector = 0'u32
  var file_exists = false

  # 1. Find File or Free Slot
  for offset in countup(0, 511, 64):
    if io_buffer[offset] != 0:
      var entry_name = ""
      for i in 0..31:
        if io_buffer[offset+i] == 0: break
        entry_name.add(char(io_buffer[offset+i]))

      if entry_name == $name:
        dir_offset = offset
        file_exists = true
        # For existing files, efficient rewrite logic is complex (reuse chain vs new).
        # V2 Simplification: Just create NEW chain, orphan old one (leak) for now.
        # Future: Walk old chain and free in BAM.
        break
    elif dir_offset == -1:
      dir_offset = offset

  if dir_offset == -1:
    kprintln("[SFS] Error: Directory Full.")
    return

  # 2. Chunk and Write Data
  var remaining = data_len
  var data_ptr = 0
  var first_sector = 0'u32
  var prev_sector = 0'u32
  var current_sector = 0'u32

  # For the first chunk
  current_sector = sfs_alloc_sector()
  if current_sector == 0:
    kprintln("[SFS] Error: Disk Full.")
    return
  first_sector = current_sector

  while remaining > 0:
    var sector_buf: array[512, byte]

    # Fill Data
    let chunk_size = if remaining > CHUNK_SIZE: CHUNK_SIZE else: remaining
    for i in 0..<chunk_size:
      sector_buf[i] = byte(data[data_ptr + i])

    remaining -= chunk_size
    data_ptr += chunk_size

    # Determine Next Sector
    var next_sector = EOF_MARKER
    if remaining > 0:
      next_sector = sfs_alloc_sector()
      if next_sector == 0:
        next_sector = EOF_MARKER # Disk full, truncated
        remaining = 0

    # Write Next Pointer
    sector_buf[508] = byte(next_sector and 0xFF)
    sector_buf[509] = byte((next_sector shr 8) and 0xFF)
    sector_buf[510] = byte((next_sector shr 16) and 0xFF)
    sector_buf[511] = byte((next_sector shr 24) and 0xFF)

    # Flush Sector
    virtio_blk_write(uint64(current_sector), addr sector_buf[0])

    current_sector = next_sector
    if current_sector == EOF_MARKER: break

  # 3. Update Directory Entry
  # Need to read Dir again as buffer was used for BAM/Data
  virtio_blk_read(SEC_DIR, addr io_buffer[0])

  let n_str = $name
  for i in 0..31:
    if i < n_str.len: io_buffer[dir_offset+i] = byte(n_str[i])
    else: io_buffer[dir_offset+i] = 0

  io_buffer[dir_offset+32] = byte(first_sector and 0xFF)
  io_buffer[dir_offset+33] = byte((first_sector shr 8) and 0xFF)
  io_buffer[dir_offset+34] = byte((first_sector shr 16) and 0xFF)
  io_buffer[dir_offset+35] = byte((first_sector shr 24) and 0xFF)

  let sz = uint32(data_len)
  io_buffer[dir_offset+36] = byte(sz and 0xFF)
  io_buffer[dir_offset+37] = byte((sz shr 8) and 0xFF)
  io_buffer[dir_offset+38] = byte((sz shr 16) and 0xFF)
  io_buffer[dir_offset+39] = byte((sz shr 24) and 0xFF)

  virtio_blk_write(SEC_DIR, addr io_buffer[0])
  kprintln("[SFS] Multi-Sector Write Complete.")

proc sfs_read_file*(name: cstring, dest: pointer, max_len: int): int {.exportc, cdecl.} =
  if not sfs_mounted: return -1

  virtio_blk_read(SEC_DIR, addr io_buffer[0])

  var start_sector = 0'u32
  var file_size = 0'u32
  var found = false

  for offset in countup(0, 511, 64):
    if io_buffer[offset] != 0:
      var entry_name = ""
      for i in 0..31:
        if io_buffer[offset+i] == 0: break
        entry_name.add(char(io_buffer[offset+i]))

      if entry_name == $name:
        start_sector = uint32(io_buffer[offset+32]) or
                       (uint32(io_buffer[offset+33]) shl 8) or
                       (uint32(io_buffer[offset+34]) shl 16) or
                       (uint32(io_buffer[offset+35]) shl 24)
        file_size = uint32(io_buffer[offset+36]) or
                    (uint32(io_buffer[offset+37]) shl 8) or
                    (uint32(io_buffer[offset+38]) shl 16) or
                    (uint32(io_buffer[offset+39]) shl 24)
        found = true
        break

  if not found: return -1

  # Read Chain
  var current_sector = start_sector
  var dest_addr = cast[int](dest)
  var remaining = int(file_size)
  if remaining > max_len: remaining = max_len

  var total_read = 0

  while remaining > 0 and current_sector != EOF_MARKER and current_sector != 0:
    var sector_buf: array[512, byte]
    virtio_blk_read(uint64(current_sector), addr sector_buf[0])

    # Extract Payload
    let payload_size = min(remaining, CHUNK_SIZE)
    # Be careful not to overflow dest buffer if payload_size > remaining (handled by min)

    copyMem(cast[pointer](dest_addr), addr sector_buf[0], payload_size)

    dest_addr += payload_size
    remaining -= payload_size
    total_read += payload_size

    # Next Sector
    current_sector = uint32(sector_buf[508]) or
                     (uint32(sector_buf[509]) shl 8) or
                     (uint32(sector_buf[510]) shl 16) or
                     (uint32(sector_buf[511]) shl 24)

  return total_read

proc vfs_register_sfs(name: string, size: uint64) {.importc, cdecl.}

proc sfs_sync_vfs*() =
  if not sfs_mounted: return

  virtio_blk_read(SEC_DIR, addr io_buffer[0])
  for offset in countup(0, 511, 64):
    if io_buffer[offset] != 0:
      var name = ""
      for i in 0..31:
        let c = char(io_buffer[offset+i])
        if c == '\0': break
        name.add(c)

      let f_size = uint32(io_buffer[offset+36]) or
                   (uint32(io_buffer[offset+37]) shl 8) or
                   (uint32(io_buffer[offset+38]) shl 16) or
                   (uint32(io_buffer[offset+39]) shl 24)

      vfs_register_sfs(name, uint64(f_size))
