# src/npl/nipbox/nipbox.nim

import strutils
import std
import editor

# --- SOVEREIGN NETWORKING TYPES ---
type
  EthAddr = array[6, byte]

  EthHeader {.packed.} = object
    dest: EthAddr
    src: EthAddr
    ethertype: uint16

  ArpPacket {.packed.} = object
    htype: uint16
    ptype: uint16
    hlen: uint8
    plen: uint8
    oper: uint16
    sha: EthAddr
    spa: uint32
    tha: EthAddr
    tpa: uint32

  IcmpPacket {.packed.} = object
    const_type: uint8
    code: uint8
    checksum: uint16
    id: uint16
    seq: uint16

const
  ETHERTYPE_ARP = 0x0608
  ETHERTYPE_IP = 0x0008
  ARP_OP_REQUEST = 0x0100
  ARP_OP_REPLY = 0x0200
  IP_PROTO_ICMP = 1

const MY_IP: uint32 = 0x0F02000A
const MY_MAC: EthAddr = [0x52.byte, 0x54.byte, 0x00.byte, 0x12.byte, 0x34.byte, 0x56.byte]

# --- 2. HELPERS ---

proc print(s: string) =
  if s.len > 0:
    discard write(cint(1), unsafeAddr s[0], csize_t(s.len))
  var nl = "\n"
  discard write(cint(1), unsafeAddr nl[0], 1)

proc print_raw(s: string) =
  if s.len > 0:
    discard write(cint(1), unsafeAddr s[0], csize_t(s.len))

# --- 3. LOGIC MODULES ---

var net_buf: array[1536, byte]

proc calc_checksum(buf: cptr, len: int): uint16 =
  var sum: uint32 = 0
  let ptr16 = cast[ptr UncheckedArray[uint16]](buf)
  for i in 0 ..< (len div 2):
    sum += uint32(ptr16[i])
  if (len mod 2) != 0:
    let ptr8 = cast[ptr UncheckedArray[byte]](buf)
    sum += uint32(ptr8[len-1])
  while (sum shr 16) > 0:
    sum = (sum and 0xFFFF) + (sum shr 16)
  return uint16(not sum)

proc handle_arp(rx_len: int) =
  let eth = cast[ptr EthHeader](addr net_buf[0])
  let arp = cast[ptr ArpPacket](addr net_buf[14])
  if arp.tpa == MY_IP and arp.oper == ARP_OP_REQUEST:
    arp.tha = arp.sha
    arp.tpa = arp.spa
    arp.sha = MY_MAC
    arp.spa = MY_IP
    arp.oper = ARP_OP_REPLY
    eth.dest = eth.src
    eth.src = MY_MAC
    nexus_net_tx(addr net_buf[0], 42)

proc handle_ipv4(rx_len: int) =
  let eth = cast[ptr EthHeader](addr net_buf[0])
  if net_buf[23] == IP_PROTO_ICMP:
    let dst_ip_ptr = cast[ptr uint32](addr net_buf[30])
    if dst_ip_ptr[] == MY_IP:
      let icmp = cast[ptr IcmpPacket](addr net_buf[34])
      if icmp.const_type == 8:
        icmp.const_type = 0
        icmp.checksum = 0
        let icmp_len = rx_len - 34
        icmp.checksum = calc_checksum(addr net_buf[34], icmp_len)
        let src_ip_ptr = cast[ptr uint32](addr net_buf[26])
        let tmp = src_ip_ptr[]
        src_ip_ptr[] = dst_ip_ptr[]
        dst_ip_ptr[] = tmp
        eth.dest = eth.src
        eth.src = MY_MAC
        nexus_net_tx(addr net_buf[0], uint64(rx_len))

proc poll_network() =
  let len = nexus_net_rx(addr net_buf[0], 1536)
  if len > 0:
    let eth = cast[ptr EthHeader](addr net_buf[0])
    if eth.ethertype == ETHERTYPE_ARP:
      handle_arp(int(len))
    elif eth.ethertype == ETHERTYPE_IP:
      handle_ipv4(int(len))

# --- CMDS ---

proc do_mkfs() =
  print("[mkfs] Partitioning Ledger...")
  var sb: array[512, byte]
  sb[0] = 0x53; sb[1] = 0x46; sb[2] = 0x53; sb[3] = 0x31
  sb[4] = 0x00; sb[5] = 0x00; sb[6] = 0x00; sb[7] = 0x02
  sb[12] = 0x01
  nexus_blk_write(0, addr sb[0], 512)
  var zero: array[512, byte]
  for i in 0 ..< 512: zero[i] = 0
  nexus_blk_write(1, addr zero[0], 512)
  print("[mkfs] Complete.")

proc do_cat(filename: string) =
  let fd = open(cstring(filename), 0)
  if fd < 0:
    print("cat: error opening " & filename)
    return
  var buf: array[1024, char]
  while true:
    let n = read(fd, addr buf[0], 1024)
    if n <= 0: break
    discard write(cint(1), addr buf[0], csize_t(n))
  discard close(fd)
  print("")

proc do_ls() =
  var buf: array[2048, char]
  let n = list_files(addr buf[0], 2048)
  if n > 0:
    var s = newString(n)
    copyMem(addr s[0], addr buf[0], n)
    print(s)

proc start_editor(filename: string) =
  editor.start_editor(filename)

# --- ENGINE ---

proc dispatch_command(input: string)

proc run_script(filename: string) =
  if filename.len == 0: return
  print("[Init] Sourcing " & filename & "...")
  let fd = open(cstring(filename), 0)
  if fd < 0:
    print("[Init] Script not found: " & filename)
    return
  var line = ""
  var buf: array[1, char]
  while true:
    let n = read(fd, addr buf[0], 1)
    if n <= 0: break
    if buf[0] == '\n':
      let t = line.strip()
      if t.len > 0 and not t.startsWith("#"):
        print_raw("+ " & t & "\n")
        dispatch_command(t)
      line = ""
    elif buf[0] != '\r':
      line.add(buf[0])

  let t = line.strip()
  if t.len > 0 and not t.startsWith("#"):
    print_raw("+ " & t & "\n")
    dispatch_command(t)

  discard close(fd)
  print("[Init] Done.")

proc dispatch_command(input: string) =
  let trimmed = input.strip()
  if trimmed.len == 0: return

  var cmd = ""
  var arg = ""
  var space = false
  for c in trimmed:
    if not space and c == ' ': space = true
    elif not space: cmd.add(c)
    else: arg.add(c)

  if cmd == "exit": exit(0)
  elif cmd == "echo": print(arg)
  elif cmd == "cat": do_cat(arg)
  elif cmd == "ls": do_ls()
  elif cmd == "mkfs": do_mkfs()
  elif cmd == "mount": discard nexus_syscall(0x204, 0)
  elif cmd == "matrix":
    if arg == "on": discard nexus_syscall(0x100, 1)
    else: discard nexus_syscall(0x100, 0)
  elif cmd == "ed": start_editor(arg)
  elif cmd == "source": run_script(arg)
  elif cmd == "help":
    print("NipBox v0.4 (Sovereign Supervisor)")
    print("echo, cat, ls, mkfs, mount, matrix, ed, source, exit")
  else:
    print("Unknown command: " & cmd)

proc main() =
  print("\n╔═══════════════════════════════════════╗")
  print("║   SOVEREIGN SUPERVISOR v0.4 ACTIVE    ║")
  print("╚═══════════════════════════════════════╝")

  # Auto-Mount
  discard nexus_syscall(0x204, 0)

  # Init
  run_script("/etc/init.nsh")

  print_raw("\nroot@nexus:# ")
  var inputBuffer = ""
  while true:
    poll_network()
    var c: char
    if nexus_read_nonblock(0, addr c, 1) > 0:
      if c == '\n' or c == '\r':
        print_raw("\n")
        dispatch_command(inputBuffer)
        inputBuffer = ""
        print_raw("root@nexus:# ")
      elif c == '\b' or c == char(127):
        if inputBuffer.len > 0:
          print_raw("\b \b")
          inputBuffer.setLen(inputBuffer.len - 1)
      else:
        inputBuffer.add(c)
        var s = ""
        s.add(c)
        print_raw(s)
    nexus_yield()

when isMainModule: main()
