import socket
import ../../core/ion/memory
import ion_client

proc console_write(p: pointer, len: csize_t) {.importc, cdecl.}

#[
# --- Heap Allocator (Slab Backed) ---
# DISABLED: Using stubs.zig heap for NipBox stability
const HEADER_SIZE = 32

proc malloc*(size: csize_t): pointer {.exportc, cdecl.} =
  if size > (2048 - HEADER_SIZE): return nil

  var pkt: IonPacket
  if not ion_user_alloc(addr pkt): return nil

  if pkt.data == nil: return nil

  # Store metadata at the start of the slab
  var header_ptr = cast[ptr IonPacket](pkt.data)
  header_ptr[] = pkt

  # Return pointer after header
  return cast[pointer](cast[uint](pkt.data) + uint(HEADER_SIZE))

proc free*(p: pointer) {.exportc, cdecl.} =
  if p == nil: return

  # Recover Metadata
  let slab_addr = cast[uint](p) - uint(HEADER_SIZE)
  let header_ptr = cast[ptr IonPacket](slab_addr)

  # Free using the stored packet (contains correct ID)
  ion_user_free(header_ptr[])
]#

proc sleep*(seconds: uint32) {.exportc, cdecl.} =
  # Busy loop sleep
  var i: int = 0
  let limit = int(seconds) * 50_000_000
  while i < limit:
    i += 1

# Basic SockAddr struct match (IPv4)
type
  SockAddrIn = object
    sin_family: uint16
    sin_port: uint16
    sin_addr: uint32
    sin_zero: array[8, char]

proc socket*(domain, sock_type, protocol: int): int {.exportc, cdecl.} =
  # Domain=2 (AF_INET), Type=1 (SOCK_STREAM)
  # We ignore them for now and just give a Nexus Socket
  return new_socket()

proc connect*(fd: int, sock_addr: pointer, len: int): int {.exportc, cdecl.} =
  if sock_addr == nil: return -1

  # Cast raw pointer to SockAddrIn
  let sin = cast[ptr SockAddrIn](sock_addr)

  # Call the Shim
  # Note: Linux sockaddr_in is Big Endian for Port/IP usually
  # NPK is likely running the same endianness as Kernel (Little Endian on RISC-V/x86)
  # But `connect` expects Network Byte Order (Big Endian).
  # We pass it raw to connect_flow, which will store it.

  return connect_flow(fd, sin.sin_addr, sin.sin_port)

proc write*(fd: cint, buf: pointer, count: csize_t): int {.exportc, cdecl.} =
  if fd == 1 or fd == 2:
    when defined(is_kernel):
      # 1. Allocate a Console Slab
      var pkt = ion_alloc()
      if pkt.data == nil: return -1

      # 2. Copy the string (Cap at SLAB_SIZE)
      let safe_count = min(int(count), SLAB_SIZE)
      copyMem(pkt.data, buf, safe_count)
      pkt.len = uint16(safe_count)

      # 3. Push to KERNEL CONSOLE (Port 0)
      ion_egress_to_port(0, pkt)

      return int(safe_count)
    else:
      # Membrane side: Direct to UART for Phase 7
      console_write(buf, count)
      return int(count)

  # Handle Sockets (fd > 2)
  return send_flow(int(fd), buf, int(count))
#[
proc read*(fd: int, buf: pointer, count: int): int {.exportc, cdecl.} =
  # TODO: Lookup socket, check RX ring
  return -1 # EBADF
]#
