#include <stddef.h>
#include <stdint.h>
#include "syscall.h"
#include "spinlock/spinlock.h"
#include "ipc/netsock/netsock.h"
#include "util/util.h"
#include "errors.h"

int32_t SYSCALL3(sys_ipc_netsockmake, net1, proto1, port1) {
  uint16_t net = net1;
  uint16_t proto = proto1;
  uint16_t port = port1;

  IpcNetSock *netsock = ipc_netsockmake(net, proto, port);

  if (netsock == NULL) {
    return E_NOMEMORY;
  }

  spinlock_acquire(&IPC_NETSOCKS.spinlock);
  size_t idx = 0;
  IpcNetSock *ns = NULL, *nstmp = NULL;
  LL_FOREACH_SAFE_IDX(IPC_NETSOCKS.netsocks, ns, nstmp, idx) {
    if (ns == netsock) {
      break;
    }
  }
  spinlock_release(&IPC_NETSOCKS.spinlock);

  return idx;
}

int32_t SYSCALL2(sys_ipc_netsocklisten, socknum1, maxlisteners1) {
  size_t socknum = socknum1;
  size_t maxlisteners = maxlisteners1;

  spinlock_acquire(&IPC_NETSOCKS.spinlock);
  size_t idx = 0;
  IpcNetSock *ns = NULL, *nstmp = NULL;
  LL_FOREACH_SAFE_IDX(IPC_NETSOCKS.netsocks, ns, nstmp, idx) {
    if (idx == socknum) {
      break;
    }
  }
  spinlock_release(&IPC_NETSOCKS.spinlock);

  if (ns == NULL) {
    return E_NOENTRY;
  }

  return ipc_netsocklisten(ns, maxlisteners);
}

int32_t SYSCALL1(sys_ipc_netsockpollev, socknum1) {
  size_t socknum = socknum1;

  spinlock_acquire(&IPC_NETSOCKS.spinlock);
  size_t idx = 0;
  IpcNetSock *ns, *nstmp;
  LL_FOREACH_SAFE_IDX(IPC_NETSOCKS.netsocks, ns, nstmp, idx) {
    if (idx == socknum) {
      break;
    }
  }
  spinlock_release(&IPC_NETSOCKS.spinlock);

  if (ns == NULL) {
    return E_NOENTRY;
  }

  spinlock_acquire(&ns->spinlock);

  IpcNetSockEventBuffer ev;
  bool empty = false;
  rbuft_pop(&ns->eventbuffer, &ev, &empty);

  spinlock_release(&ns->spinlock);

  if (empty) {
    return E_NOTYET;
  }

  return (int32_t)ev;
}
