# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# RUMPK CORE // FIBER (Unified Multi-Arch)
# The atom of execution.
#
# Supported Architectures:
# - aarch64 (ARM64): VisionFive 2, RPi, AWS Graviton
# - amd64 (x86_64): Standard servers, trading systems
# - riscv64 (RISC-V): Sovereign compute, satellites

{.push stackTrace: off, lineTrace: off.}

# =========================================================
# Architecture-Specific Constants
# =========================================================

when defined(amd64) or defined(x86_64):
  const CONTEXT_SIZE = 56
  const RET_ADDR_INDEX = 6 # RIP at [sp + 48]
  const ARCH_NAME = "x86_64"

elif defined(arm64) or defined(aarch64):
  const CONTEXT_SIZE = 96
  const RET_ADDR_INDEX = 11 # x30 (LR) at [sp + 88]
  const ARCH_NAME = "aarch64"

elif defined(riscv64):
  const CONTEXT_SIZE = 128
  const RET_ADDR_INDEX = 0 # ra at [sp + 0]
  const ARCH_NAME = "riscv64"

else:
  {.error: "Unsupported architecture for Rumpk fibers".}

# =========================================================
# Types
# =========================================================

type
  FiberState* = object
    sp*: uint64              # The Stack Pointer (Must be first field!)
    entry*: proc() {.cdecl.} # Entry point for this fiber

  Fiber* = ptr FiberObject
  FiberObject* = object
    id*: uint64
    name*: cstring
    state*: FiberState
    stack*: ptr UncheckedArray[uint8]
    stack_size*: int
    sleep_until*: uint64 # NS timestamp

proc fiber_yield*() {.importc, cdecl.}
# Imports
# =========================================================

# Import the Assembly Magic (same symbol name, different implementation per arch)
proc cpu_switch_to(prev_sp_ptr: ptr uint64, next_sp: uint64) {.importc, cdecl.}

# Import console for debugging
proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}

proc debug*(s: string) =
  if s.len > 0:
    console_write(unsafeAddr s[0], csize_t(s.len))

proc print_arch_info*() =
  debug("[Rumpk] Architecture Context: " & ARCH_NAME & "\n")

# =========================================================
# Constants
# =========================================================

const STACK_SIZE* = 4096

# =========================================================
# Fiber State
# =========================================================

var main_fiber: FiberObject
var current_fiber*: Fiber = addr main_fiber

# =========================================================
# Trampoline (Entry point for new fibers)
# =========================================================

proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
  var msg = "[FIBER] Trampoline Entry!\n"
  console_write(addr msg[0], csize_t(msg.len))
  let f = current_fiber

  if f.state.entry != nil:
    f.state.entry()

  # If the fiber returns, halt
  when defined(amd64) or defined(x86_64):
    while true:
      {.emit: "asm volatile(\"hlt\");".}
  elif defined(arm64) or defined(aarch64):
    while true:
      {.emit: "asm volatile(\"wfi\");".}
  elif defined(riscv64):
    while true:
      {.emit: "asm volatile(\"wfi\");".}

# =========================================================
# Fiber Initialization (Arch-Specific)
# =========================================================

proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer, size: int) =
  f.state.entry = entry

  # Start at top of stack (using actual size)
  var sp = cast[uint64](stack_base) + cast[uint64](size)

  # 1. Align to 16 bytes (Universal requirement)
  sp = sp and not 15'u64

  # 2. Reserve space for the context frame
  sp = sp - CONTEXT_SIZE

  # 3. Setup the Context (zero all, set return address)
  var stack_ptr = cast[ptr UncheckedArray[uint64]](sp)

  # Zero out the frame
  let num_regs = CONTEXT_SIZE div 8
  for i in 0..<num_regs:
    stack_ptr[i] = 0

  # Set return address to trampoline
  stack_ptr[RET_ADDR_INDEX] = cast[uint64](fiber_trampoline)

  f.state.sp = sp

# =========================================================
# Context Switch
# =========================================================

proc switch*(next: Fiber) =
  let prev = current_fiber
  current_fiber = next
  cpu_switch_to(addr prev.state.sp, next.state.sp)

{.pop.}
