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

# SPEC-093: UTCP Protocol Implementation
# Sovereign Transport for Intra-Cluster Communication

# Import C decls for kernel logging
proc kprint(s: cstring) {.importc, cdecl.}
proc kprintln(s: cstring) {.importc, cdecl.}
proc kprint_hex(n: uint64) {.importc, cdecl.}

# --- Protocol Constants ---

const
  ETHERTYPE_UTCP* = 0x88B5'u16
  
  # Flags
  UTCP_FLAG_SYN*  = 0x01'u8
  UTCP_FLAG_ACK*  = 0x02'u8
  UTCP_FLAG_NACK* = 0x04'u8
  UTCP_FLAG_FIN*  = 0x08'u8
  UTCP_FLAG_DATA* = 0x10'u8

# --- Types ---

type
  CellID* = object
    ## 128-bit SipHash result representing a Node Identity
    lo*: uint64
    hi*: uint64

  UtcpHeader* {.packed.} = object
    ## 32-Byte Fixed Header (SPEC-093)
    eth_type*: uint16     # 0x88B5 (Big Endian)
    flags*: uint8         
    reserved*: uint8
    target_id*: CellID    # 16 bytes
    sender_id*: CellID    # 16 bytes
    seq_num*: uint64      # 8 bytes (Big Endian)
    payload_len*: uint16  # 2 bytes (Big Endian)
  # Total = 46 bytes.
    # 46 bytes + 14 byte Eth header = 60 bytes minimum frame size.

  UtcpState* = enum
    CLOSED, LISTEN, SYN_SENT, SYN_RCVD, ESTABLISHED, FIN_WAIT

  UtcpControlBlock* = object
    state*: UtcpState
    local_id*: CellID
    remote_id*: CellID
    local_seq*: uint64
    remote_seq*: uint64
    last_ack*: uint64

const MAX_CONNECTIONS = 16
var utcp_pcb_table: array[MAX_CONNECTIONS, UtcpControlBlock]

# --- Helper Functions ---

proc ntohs(n: uint16): uint16 {.inline.} =
  return (n shr 8) or (n shl 8)

proc ntohll(n: uint64): uint64 {.inline.} =
  var b = cast[array[8, byte]](n)
  # Reverse bytes
  return (uint64(b[0]) shl 56) or (uint64(b[1]) shl 48) or
         (uint64(b[2]) shl 40) or (uint64(b[3]) shl 32) or
         (uint64(b[4]) shl 24) or (uint64(b[5]) shl 16) or
         (uint64(b[6]) shl 8)  or uint64(b[7])

proc htonll(n: uint64): uint64 {.inline.} =
  return ntohll(n) # Symmetric

proc cellid_eq(a, b: CellID): bool =
  return a.lo == b.lo and a.hi == b.hi

proc utcp_find_pcb(remote_id: CellID): ptr UtcpControlBlock =
  for i in 0 ..< MAX_CONNECTIONS:
    if utcp_pcb_table[i].state != CLOSED and cellid_eq(utcp_pcb_table[i].remote_id, remote_id):
      return addr utcp_pcb_table[i]
  return nil

proc utcp_alloc_pcb(): ptr UtcpControlBlock =
  for i in 0 ..< MAX_CONNECTIONS:
    if utcp_pcb_table[i].state == CLOSED:
      return addr utcp_pcb_table[i]
  return nil

# --- Logic ---

proc utcp_handle_packet*(data: ptr UncheckedArray[byte], len: uint16) {.exportc, cdecl.} =
  ## Handle raw UTCP frame (stripped of UDP/IP headers if tunnelled)
  
  if len < uint16(sizeof(UtcpHeader)):
    kprintln("[UTCP] Drop: Frame too short")
    return

  let header = cast[ptr UtcpHeader](data)
  
  # Validate Magic
  if ntohs(header.eth_type) != ETHERTYPE_UTCP:
    # Allow 0x88B5 for now, but log if mismatch
    discard

  let seq_num = ntohll(header.seq_num)
  let flags = header.flags

  # Log Packet
  kprint("[UTCP] RX Seq="); kprint_hex(seq_num); 
  kprint(" Flags="); kprint_hex(uint64(flags)); kprintln("")

  # State Machine
  var pcb = utcp_find_pcb(header.sender_id)
  
  if pcb == nil:
    # New Connection?
    if (flags and UTCP_FLAG_SYN) != 0:
      kprintln("[UTCP] New SYN received")
      pcb = utcp_alloc_pcb()
      if pcb != nil:
        pcb.state = SYN_RCVD
        pcb.remote_id = header.sender_id
        pcb.local_id = header.target_id
        pcb.remote_seq = seq_num
        pcb.local_seq = 1000 # Randomize?
        kprintln("[UTCP] State -> SYN_RCVD. Sending SYN-ACK (TODO)")
        # TODO: Send SYN-ACK
      else:
        kprintln("[UTCP] Drop: Table full")
    else:
      kprintln("[UTCP] Drop: Packet for unknown connection")
      return

  else:
    # Existing Connection
    kprint("[UTCP] Match PCB. State="); kprint_hex(uint64(pcb.state)); kprintln("")
    
    case pcb.state:
    of SYN_RCVD:
      if (flags and UTCP_FLAG_ACK) != 0:
        pcb.state = ESTABLISHED
        kprintln("[UTCP] State -> ESTABLISHED")
    of ESTABLISHED:
      if (flags and UTCP_FLAG_DATA) != 0:
        kprintln("[UTCP] Data received")
        # TODO: Enqueue data
      elif (flags and UTCP_FLAG_FIN) != 0:
        pcb.state = CLOSED # Simplify for now
        kprintln("[UTCP] Connection-Teardown (FIN)")
    else:
      discard
