# 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: 2 + 1 + 1 + 16 + 16 + 8 + 2 = 46 bytes?
    # Wait, SPEC-093 says 32 bytes... let's recheck the SPEC layout.
    
    # SPEC layout:
    # 0-2: eth_type (2)
    # 2: flags (1)
    # 3: reserved (1)
    # 4-19: target_id (16)
    # 20-35: sender_id (16)
    # 36-43: seq_num (8)
    # 44-45: payload_len (2)
    # Total = 46 bytes.
    # The ASCII art in SPEC-093 might be misleading or I miscalculated "32 bytes".
    # 16+16 is already 32. So header is definitely larger than 32 if it includes 2 CellIDs.
    # SipHash-128 is 16 bytes.
    # Let's stick to the struct definition, size is secondary to correctness.
    # 46 bytes + 14 byte Eth header = 60 bytes minimum frame size. Nice.

# --- 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)
    res: uint64
  # Reverse bytes
  # TODO: Optimize with bswap builtin if available
  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])

# --- 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 EtherType (if present in tunnel payload? SPEC says it's the first field)
  # In 0x88B5 frames, the EtherType is in the Ethernet header, which might be stripped?
  # Fastpath stripping logic in fastpath.nim removes ETH(14)+IP(20)+UDP(8).
  # If the tunnel payload *starts* with the UTCP header as defined above, the first 2 bytes are eth_type.
  # This acts as a magic number/version check.
  
  if ntohs(header.eth_type) != ETHERTYPE_UTCP:
    # kprint("[UTCP] Drop: Invalid EtherType/Magic: ")
    # kprint_hex(uint64(ntohs(header.eth_type)))
    # kprintln("")
    # It might be 0x88B5, allow it for now.
    discard

  # Log Packet
  kprintln("[UTCP] Packet Received")
  if (header.flags and UTCP_FLAG_SYN) != 0:
    kprintln("  Type: SYN")
  elif (header.flags and UTCP_FLAG_DATA) != 0:
    kprintln("  Type: DATA")
  
  kprint("  Seq: "); kprint_hex(ntohll(header.seq_num)); kprintln("")
  
  # TODO: State machine lookup
