# 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.

## Rumpk Layer 1: Fibers (The Sovereign Thread)

# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# Rumpk Phase 10: Multitasking & Context Switching
# 
# Responsibilities:
# - Define the Fiber abstraction (Hardware Context + Stack)
# - Abstract the ISA-specific context switch mechanism
# - Provide a high-level API for yielding and scheduling

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

# Architecture Configuration
# =========================================================

when defined(riscv64):
  const ARCH_NAME* = "riscv64"
  const CONTEXT_SIZE* = 128
  const RET_ADDR_INDEX* = 0 # Offset in stack for RA
elif defined(amd64) or defined(x86_64):
  const ARCH_NAME* = "amd64"
  const CONTEXT_SIZE* = 64
  const RET_ADDR_INDEX* = 0
else:
  {.error: "Unsupported Architecture".}

# --- FIBER DEFINITION ---

type
  Spectrum* {.pure.} = enum
    Void = 0,    # Default/Uninitialized
    Photon = 1,  # Real-time (0-1ms latency)
    Matter = 2,  # Interactive (1-10ms latency)
    Gravity = 3, # Batch/Idle (100ms+ latency)

  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]
    phys_offset*: uint64 # Cellular Memory Offset
    stack_size*: int
    sleep_until*: uint64 # NS timestamp
    promises*: uint64    # [63:62]=Spectrum, [61:0]=Pledge bits
    pledge_budget*: uint64 # Microseconds allowed per frame (legacy)
    avg_burst*: uint64     # Moving average for telemetry (The Ratchet)
    user_entry*: pointer # Phase 29: User function pointer for workers
    user_arg*: uint64    # Phase 29: Argument for user function
    satp_value*: uint64  # Phase 31: Page table root (0 = use kernel map)
    wants_yield*: bool   # Phase 37: Deferred yield flag
    # SPEC-102: The Ratchet
    budget_ns*: uint64   # "I promise to run for X ns max"
    last_burst_ns*: uint64 # Actual execution time of last run
    violations*: uint32  # Strike counter (3 strikes = demotion)
    pty_id*: int         # Phase 40: Assigned PTY ID (-1 if none)
    user_sp_init*: uint64 # Initial SP for userland entry
    # Ground Zero Phase 1: Capability Space (SPEC-051)
    cspace_id*: uint64   # Index into global CSpace table
    # Ground Zero Phase 3: Typed Channels & I/O Multiplexing
    blocked_on_mask*: uint64 # Bitmask of capability slots fiber is waiting on
    is_blocked*: bool        # True if fiber is waiting for I/O

# Spectrum Accessors
proc getSpectrum*(f: Fiber): Spectrum =
  if f == nil: return Spectrum.Void
  Spectrum((f.promises shr 62) and 0x3)

proc setSpectrum*(f: Fiber, s: Spectrum) =
  if f == nil: return
  f.promises = (f.promises and 0x3FFFFFFFFFFFFFFF'u64) or (uint64(s) shl 62)

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.}

# Phase 31: Page Table Activation
proc mm_activate_satp(satp_val: uint64) {.importc, cdecl.}
proc mm_get_kernel_satp(): uint64 {.importc, cdecl.}

proc debug(s: string) =
  proc console_write(p: pointer, len: int) {.importc, cdecl.}
  if s.len > 0:
    console_write(unsafeAddr s[0], 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* {.global.}: Fiber = addr main_fiber

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

proc fiber_trampoline() {.cdecl, exportc, noreturn.} =
  var msg = "[FIBER] Trampoline Entry!\n"
  # We can't use kprintln here if it's not imported or we use emit
  proc console_write(p: pointer, len: int) {.importc, cdecl.}
  console_write(addr msg[0], msg.len)
  let f = current_fiber

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

  # If the fiber returns, halt
  when defined(riscv64):
    while true:
      {.emit: "asm volatile(\"wfi\");".}
  else:
    while true: discard

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

proc init_fiber*(f: Fiber, entry: proc() {.cdecl.}, stack_base: pointer, size: int) =
  f.state.entry = entry
  f.stack = cast[ptr UncheckedArray[uint8]](stack_base)
  f.stack_size = size
  f.sleep_until = 0
  f.pty_id = -1
  f.user_sp_init = 0
  f.cspace_id = f.id  # Ground Zero: CSpace ID matches Fiber ID
  f.blocked_on_mask = 0
  f.is_blocked = false

  # 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

  # Swap address space if necessary (Phase 31: Sv39 Memory Isolation)
  if next.satp_value != 0:
    mm_activate_satp(next.satp_value)
  else:
    # If fiber has no specific satp (0), restore kernel identity map
    let k_satp = mm_get_kernel_satp()
    if k_satp != 0:
      mm_activate_satp(k_satp)

  cpu_switch_to(addr prev.state.sp, next.state.sp)

{.pop.}
