# 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: Software Network Switch

# core/rumpk/core/netswitch.nim
# Phase 36.2: The Traffic Cop (Network Membrane Layer 2 Switch)
# 
# Responsibilities:
# - Poll VirtIO-Net hardware for incoming packets
# - Route RX packets to s_net_rx ring (Kernel -> Userland)
# - Drain s_net_tx ring and transmit via VirtIO-Net (Userland -> Kernel)
# - Never yield during active traffic (War Mode latency optimization)

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

import ion

# Forward declare fiber_yield to avoid circular import
proc fiber_yield*() {.importc, cdecl.}
proc fiber_sleep*(ms: int) {.importc, cdecl.}

# HAL Imports
proc virtio_net_poll() {.importc, cdecl.}
proc virtio_net_send(data: pointer, len: uint32) {.importc, cdecl.}

# Logging
proc kprintln(s: cstring) {.importc, cdecl.}
proc kprint(s: cstring) {.importc, cdecl.}
proc kprint_hex(v: uint64) {.importc, cdecl.}
proc get_now_ns(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.}

# Membrane Infrastructure (LwIP Glue)
proc membrane_init*() {.importc, cdecl.}
proc pump_membrane_stack*() {.importc, cdecl.}

var netswitch_initialized: bool = false

proc netswitch_init*() =
  ## Initialize network channels and populate SysTable
  ## MUST be called before userland starts!
  ion_init_network()
  kprintln("[NetSwitch] Network Rings Initialized")
  netswitch_initialized = true

proc netswitch_attach_systable*(sys: ptr SysTable) =
  ## Attach network ring pointers to SysTable for userland access
  sys.s_net_rx = chan_net_rx.ring
  sys.s_net_tx = chan_net_tx.ring
  kprintln("[NetSwitch] SysTable Rings Attached")

type
  EthHeader* {.packed.} = object
    dst*: array[6, byte]
    src*: array[6, byte]
    ethertype*: uint16 # Big Endian

proc swap16(v: uint16): uint16 =
  ((v and 0xFF) shl 8) or (v shr 8)

proc netswitch_process_packet(pkt: IonPacket): bool =
  ## Layer 2 Demultiplexer
  if pkt.len < 14:
    ion_free(pkt)
    return false

  let eth = cast[ptr EthHeader](pkt.data)
  let etype = swap16(eth.ethertype)

  case etype:
  of 0x0800, 0x0806, 0x86DD: # IPv4, ARP, IPv6
    # Route to Legacy/LwIP Membrane
    if not chan_net_rx.send(pkt):
      # Ring full (Backpressure)
      ion_free(pkt)
      return false
    return true
    
  of 0x88B5: # Sovereign UTCP (SPEC-700)
    # TODO: Route to dedicated UTCP channel
    # kprintln("[NetSwitch] UTCP Sovereign Packet Identified")
    ion_free(pkt)
    return true # Handled (dropped)
    
  else:
    # Drop unknown EtherTypes (Security/Sovereignty)
    ion_free(pkt)
    return false

proc fiber_netswitch_entry*() {.cdecl.} =
  membrane_init()
  kprintln("[NetSwitch] Fiber Entry - The Traffic Cop is ON DUTY")
  
  var rx_activity: bool = false
  var tx_activity: bool = false
  var rx_count: uint64 = 0
  var tx_count: uint64 = 0
  var last_stat_print: uint64 = 0
  
  while true:
    rx_activity = false
    tx_activity = false
    
    # 1. Drive the hardware poll (fills chan_netswitch_rx)
    virtio_net_poll()
    
    # 2. Drive the LwIP Stack (Timers/RX)
    pump_membrane_stack()
    
    # 2. Consume from the Driver -> Switch internal ring
    var raw_pkt: IonPacket
    while chan_netswitch_rx.recv(raw_pkt):
      if netswitch_process_packet(raw_pkt):
        rx_activity = true
        inc rx_count
    
    # 3. TX PATH: Userland -> Hardware
    var tx_pkt: IonPacket
    while chan_net_tx.recv(tx_pkt):
      if tx_pkt.data != nil and tx_pkt.len > 0:
        virtio_net_send(cast[pointer](tx_pkt.data), uint32(tx_pkt.len))
        inc tx_count
      ion_free(tx_pkt)
      tx_activity = true
    
    # 4. Periodically print stats
    let now = get_now_ns()
    if now - last_stat_print > 5000000000'u64: # 5 seconds
      kprint("[NetSwitch] STATS - RX: ")
      kprint_hex(rx_count)
      kprint(" TX: ")
      kprint_hex(tx_count)
      kprintln("")
      last_stat_print = now

    # 5. Yield Strategy
    if rx_activity or tx_activity:
      # War Mode: High priority processing
      continue
    else:
      fiber_sleep(10)

{.pop.}
