# SPDX-License-Identifier: LSL-1.0
# Copyright (c) 2026 Markus Maiwald
# Stewardship: Self Sovereign Society Foundation
#
# This file is part of the Nexus Sovereign Core.
# See legal/LICENSE_SOVEREIGN.md for license terms.

## Nexus Membrane: ION High-Level Client

# Copyright (c) 2026 Nexus Foundation
# Licensed under the Libertaria Unbound License (LUL-1.0)
# See legal/LICENSE_UNBOUND.md for details.
#
# core/rumpk/libs/membrane/ion_client.nim

# CRITICAL: Do NOT import ../../core/ion - it transitively imports ion/memory (2MB pool)
# Instead, locally declare only the types we need for userspace
import ../../core/ring

const SYS_TABLE_ADDR* = 0x83000000'u64

# Local type declarations (must match core/ion.nim definitions)
type
  IonPacket* = object
    data*: ptr UncheckedArray[byte]
    phys*: uint64
    len*: uint16
    id*: uint16

  CmdType* = enum
    CMD_SYS_EXIT = 1
    CMD_GPU_MATRIX = 2
    CMD_ION_STOP = 3
    CMD_ION_START = 4
    CMD_NET_TX = 5
    CMD_NET_RX = 6
    CMD_BLK_READ = 7
    CMD_BLK_WRITE = 8
    CMD_FS_WRITE = 9
    CMD_FS_READ = 10
    CMD_ION_FREE = 0x300

  CmdPacket* = object
    kind*: uint32
    pad*: uint32
    arg*: uint64
    id*: uint64

# Kernel functions (provided at link time, NOT from ion/memory module)
proc ion_alloc*(): IonPacket {.importc, cdecl.}
proc ion_free*(pkt: IonPacket) {.importc, cdecl.}

type
  HAL_Ring_Input* = object
    head*: uint32
    tail*: uint32
    mask*: uint32
    data*: array[256, IonPacket]

  SysTable* = object
    magic*: uint32
    reserved*: uint32
    s_rx*: pointer
    s_tx*: pointer
    s_event*: pointer
    s_cmd*: pointer
    s_input*: pointer
    fn_vfs_open*: proc(path: cstring, flags: int32): int32 {.cdecl.}
    fn_vfs_read*: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.}
    fn_vfs_list*: proc(buf: pointer, max_len: uint64): int64 {.cdecl.}
    fn_vfs_write*: proc(fd: int32, buf: pointer, count: uint64): int64 {.cdecl.}
    fn_vfs_close*: proc(fd: int32): int32 {.cdecl.}
    fn_log*: pointer
    fn_pledge*: proc(promises: uint64): int32 {.cdecl.}
    fb_addr*: uint64
    fb_width*: uint32
    fb_height*: uint32
    fb_stride*: uint32
    fb_bpp*: uint32
    fn_yield*: proc() {.cdecl.}
    fn_siphash*: proc(key: ptr array[16, byte], data: pointer, len: uint64, out_hash: ptr array[16, byte]) {.cdecl.}
    fn_ed25519_verify*: proc(sig: ptr array[64, byte], msg: pointer, len: uint64, pk: ptr array[32, byte]): bool {.cdecl.}
    # SPEC-503: Monolith Key Derivation
    fn_blake3*: proc(data: pointer, len: uint64, out_hash: ptr array[32, byte]) {.cdecl.}
    # Phase 36.2: Network Membrane
    s_net_rx*: pointer  # Kernel -> User (RX)
    s_net_tx*: pointer  # User -> Kernel (TX)

    # Phase 36.3: Shared ION (16 bytes)
    fn_ion_alloc*: proc(out_id: ptr uint16): uint64 {.cdecl.}
    fn_ion_free*: proc(id: uint16) {.cdecl.}
    
    # Phase 36.4: I/O Multiplexing (8 bytes)
    fn_wait_multi*: proc(mask: uint64): int32 {.cdecl.}
    
    # Phase 36.5: Network Hardware Info (8 bytes)
    net_mac*: array[6, byte]
    reserved_mac*: array[2, byte]

static:
  doAssert sizeof(SysTable) == 208

var membrane_rx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
var membrane_tx_ring_ptr*: ptr RingBuffer[IonPacket, 256]
var membrane_cmd_ring_ptr*: ptr RingBuffer[CmdPacket, 256]
var membrane_input_ring_ptr*: ptr HAL_Ring_Input

# Phase 36.2: Network Ring Pointers
var membrane_net_rx_ptr*: ptr HAL_Ring_Input
var membrane_net_tx_ptr*: ptr HAL_Ring_Input

proc get_sys_table*(): ptr SysTable =
  return cast[ptr SysTable](SYS_TABLE_ADDR)

proc ion_user_init*() {.exportc.} =
  let sys = get_sys_table()
  discard sys
  # Use raw C write to avoid Nim string issues before init
  proc console_write(p: pointer, len: uint) {.importc, cdecl.}
  var msg = "[ION-Client] Initializing...\n"
  console_write(addr msg[0], uint(msg.len))

  when defined(is_membrane) or defined(RUMPK_KERNEL):
    if sys.magic != 0x4E585553:
      var err = "[ION-Client] ERROR: Invalid SysTable Magic!\n"
      console_write(addr err[0], uint(err.len))
      return
      
    membrane_rx_ring_ptr = cast[ptr RingBuffer[IonPacket, 256]](sys.s_rx)
    membrane_tx_ring_ptr = cast[ptr RingBuffer[IonPacket, 256]](sys.s_tx)
    membrane_cmd_ring_ptr = cast[ptr RingBuffer[CmdPacket, 256]](sys.s_cmd)
    membrane_input_ring_ptr = cast[ptr HAL_Ring_Input](sys.s_input)
    # Phase 36.2: Network rings
    membrane_net_rx_ptr = cast[ptr HAL_Ring_Input](sys.s_net_rx)
    membrane_net_tx_ptr = cast[ptr HAL_Ring_Input](sys.s_net_tx)
    
    var ok = "[ION-Client] Rings Mapped.\n"
    console_write(addr ok[0], uint(ok.len))
  else:
    var err = "[ION-Client] ERROR: is_membrane NOT DEFINED!\n"
    console_write(addr err[0], uint(err.len))

# --- ION CLIENT LOGIC ---
# Pure shared-memory slab allocator - NO kernel function calls!

const
  USER_SLAB_BASE = 0x83010000'u64   # Start of user packet slab in SysTable region
  USER_SLAB_COUNT = 512             # Number of packet slots
  USER_PKT_SIZE = 2048              # Size of each packet buffer
  USER_BITMAP_ADDR = 0x83000100'u64 # Bitmap stored in SysTable region (after SysTable struct)

# Get pointer to shared bitmap (512 bits = 64 bytes for 512 slots)
proc get_user_bitmap(): ptr array[64, byte] =
  return cast[ptr array[64, byte]](USER_BITMAP_ADDR)

proc ion_user_alloc*(out_pkt: ptr IonPacket): bool {.exportc.} =
  ## Allocate packet from shared slab - pure userland, no kernel call
  let bitmap = get_user_bitmap()
  
  # Find first free slot
  for byteIdx in 0 ..< 64:
    if bitmap[byteIdx] != 0xFF:  # At least one bit free
      for bitIdx in 0 ..< 8:
        let slotIdx = byteIdx * 8 + bitIdx
        if slotIdx >= USER_SLAB_COUNT:
          return false
        let mask = byte(1 shl bitIdx)
        if (bitmap[byteIdx] and mask) == 0:
          # Found free slot - mark as used
          bitmap[byteIdx] = bitmap[byteIdx] or mask
          
          let addr_val = USER_SLAB_BASE + uint64(slotIdx) * USER_PKT_SIZE
          out_pkt.id = uint16(slotIdx) or 0x8000
          out_pkt.phys = addr_val
          out_pkt.len = 0
          out_pkt.data = cast[ptr UncheckedArray[byte]](addr_val)
          return true
  return false

proc ion_user_free*(pkt: IonPacket) {.exportc.} =
  ## Free packet back to shared slab - pure userland, no kernel call
  if pkt.data == nil:
    return
  let slotIdx = pkt.id and 0x7FFF
  if slotIdx >= USER_SLAB_COUNT:
    return
  let bitmap = get_user_bitmap()
  let byteIdx = int(slotIdx) div 8
  let bitIdx = int(slotIdx) mod 8
  let mask = byte(1 shl bitIdx)
  bitmap[byteIdx] = bitmap[byteIdx] and (not mask)

proc ion_user_return*(id: uint16) {.exportc.} =
  if membrane_cmd_ring_ptr == nil: return
  var cmd: CmdPacket
  cmd.kind = uint32(CmdType.CMD_ION_FREE)
  cmd.arg = uint64(id)
  discard membrane_cmd_ring_ptr[].push(cmd)

proc ion_user_tx*(pkt: IonPacket): bool {.exportc.} =
  if membrane_tx_ring_ptr == nil: return false
  return membrane_tx_ring_ptr[].push(pkt)

proc ion_user_rx*(out_pkt: ptr IonPacket): bool {.exportc.} =
  if membrane_rx_ring_ptr == nil: return false
  if membrane_rx_ring_ptr[].isEmpty: return false
  let (ok, pkt) = membrane_rx_ring_ptr[].pop()
  if not ok: return false
  out_pkt[] = pkt
  return true

proc ion_user_input*(out_pkt: ptr IonPacket): bool {.exportc.} =
  if membrane_input_ring_ptr == nil: return false
  let head = membrane_input_ring_ptr.head
  let tail = membrane_input_ring_ptr.tail
  let mask = membrane_input_ring_ptr.mask
  if head == tail: return false
  let idx = tail and mask
  let pkt = membrane_input_ring_ptr.data[idx]
  out_pkt[] = pkt
  membrane_input_ring_ptr.tail = tail + 1
  return true

# --- Phase 36.2: Network Ring Access ---
proc ion_net_rx*(out_pkt: ptr IonPacket): bool {.exportc.} =
  if membrane_net_rx_ptr == nil: return false
  let head = membrane_net_rx_ptr.head
  let tail = membrane_net_rx_ptr.tail
  let mask = membrane_net_rx_ptr.mask
  if head == tail: return false
  let idx = tail and mask
  let pkt = membrane_net_rx_ptr.data[idx]
  out_pkt[] = pkt
  membrane_net_rx_ptr.tail = tail + 1
  return true

proc ion_net_tx*(pkt: IonPacket): bool {.exportc.} =
  if membrane_net_tx_ptr == nil: return false
  let head = membrane_net_tx_ptr.head
  let tail = membrane_net_tx_ptr.tail
  let mask = membrane_net_tx_ptr.mask
  let next_head = (head + 1) and mask
  if next_head == tail: return false  # Ring full
  membrane_net_tx_ptr.data[head and mask] = pkt
  membrane_net_tx_ptr.head = next_head
  return true

proc ion_net_available*(): bool {.exportc.} =
  ## Check if network rings are initialized and ready
  return membrane_net_rx_ptr != nil and membrane_net_tx_ptr != nil

proc ion_user_wait_multi*(mask: uint64): int32 {.exportc.} =
  let sys = get_sys_table()
  if sys.fn_wait_multi != nil:
    return sys.fn_wait_multi(mask)
  return -1

# --- Crypto Wrappers ---
proc crypto_siphash*(key: array[16, byte], data: pointer, len: uint64): array[16, byte] =
  let sys = get_sys_table()
  if sys.fn_siphash != nil:
    var k = key
    sys.fn_siphash(addr k, data, len, addr result)

proc crypto_verify*(sig: array[64, byte], msg: pointer, len: uint64, pk: array[32, byte]): bool =
  let sys = get_sys_table()
  if sys.fn_ed25519_verify != nil:
    var s = sig
    var p = pk
    return sys.fn_ed25519_verify(addr s, msg, len, addr p)
  return false

proc crypto_blake3*(data: pointer, len: uint64): array[32, byte] =
  let sys = get_sys_table()
  if sys.fn_blake3 != nil:
    sys.fn_blake3(data, len, addr result)

proc ion_get_mac*(): array[6, byte] =
  let sys = get_sys_table()
  return sys.net_mac
