#include <stdint.h>
#include <stddef.h>
#include "hal/hal.h"
#include "spinlock/spinlock.h"
#include "netsock.h"
#include "errors.h"
#include "pico_socket.h"
#include "dlmalloc/malloc.h"
#include "util/util.h"
#include "sysdefs/ipcnetsock.h"
#include "kprintf.h"

IpcNetSocks IPC_NETSOCKS;

void ipc_netsockinit(void) {
  hal_memset(&IPC_NETSOCKS, 0, sizeof(IPC_NETSOCKS));
  spinlock_init(&IPC_NETSOCKS.spinlock);
}

void ipc_netsock_event(uint16_t ev, struct pico_socket *sock1) {
  IpcNetSock *netsock, *netsocktmp;

  spinlock_acquire(&IPC_NETSOCKS.spinlock);
  LL_FOREACH_SAFE(IPC_NETSOCKS.netsocks, netsock, netsocktmp) {
    if (netsock->picosock == sock1)
      break;
  }
  spinlock_release(&IPC_NETSOCKS.spinlock);

  if (netsock == NULL) {
    return;
  }
    
  spinlock_acquire(&netsock->spinlock);

  /* if (ev & PICO_SOCK_EV_RD) { */
  /*   uint8_t *buf = dlmalloc(IPC_PIPE_MAX); */
  /*   int32_t len = 0; */
  /*   int32_t read; */
  /*   do { */
  /*     read = pico_socket_read(netsock->picosock, buf + len, IPC_PIPE_MAX - len); */
  /*     if (read > 0) { */
  /*       len += read; */
  /*     } */
  /*   } while(read > 0); */
  /*   ipc_pipewrite(netsock->datapipe, buf, len); */
  /*   dlfree(buf); */
  /* } */

  /* if (ev & PICO_SOCK_EV_WR) { */
  /*   uint8_t *buf = dlmalloc(IPC_PIPE_MAX); */
  /*   int32_t read = ipc_piperead(netsock->datapipe, buf, IPC_PIPE_MAX); */
  /*   if (read > 0) { */
  /*     pico_socket_write(netsock->picosock, buf, read); */
  /*   } */
  /*   dlfree(buf); */
  /* } */
  
  /* if (ev & PICO_SOCK_EV_FIN) { */
  /*   // normal close */
  /* } */

  

  /* if (ev & PICO_SOCK_EV_CLOSE) { */
  /*   pico_socket_shutdown(netsock->picosock, PICO_SHUT_WR); */
  /* } */

  rbuft_push(&netsock->eventbuffer, &ev);
  
  spinlock_release(&netsock->spinlock);
}

IpcNetSock *ipc_netsockmake(uint16_t net, uint16_t proto, uint16_t port) {
  IpcNetSock *netsock = dlmalloc(sizeof(*netsock));
  if (netsock == NULL) {
    return NULL;
  }

  netsock->picosock = pico_socket_open(net, proto, &ipc_netsock_event);
  if (netsock->picosock == NULL) {
    goto err_sock_open;
  }
  
  spinlock_init(&netsock->spinlock);
  netsock->datapipe = dlmalloc(sizeof(*netsock->datapipe));
  ipc_pipeinit(netsock->datapipe, (uint64_t)-1);

  uint8_t *eventbuffer = dlmalloc(sizeof(IpcNetSockEventBuffer) * IPC_NETSOCK_EVENTBUFFER_MAX);
  rbuft_init(&netsock->eventbuffer, eventbuffer, sizeof(IpcNetSockEventBuffer), IPC_NETSOCK_EVENTBUFFER_MAX);

  uint16_t port_be = short_be(port);
  struct pico_ip4 inaddr_any = {0};
  pico_socket_bind(netsock->picosock, &inaddr_any, &port_be);

  spinlock_acquire(&IPC_NETSOCKS.spinlock);
  LL_APPEND(IPC_NETSOCKS.netsocks, netsock);
  spinlock_release(&IPC_NETSOCKS.spinlock);

  return netsock;

err_sock_open:
    dlfree(netsock);
    return NULL;
}

int32_t ipc_netsocklisten(IpcNetSock *netsock, size_t maxlisteners) {
  spinlock_acquire(&netsock->spinlock);
  int32_t r = pico_socket_listen(netsock->picosock, (int)maxlisteners);
  spinlock_release(&netsock->spinlock);
  return r == 0 ? E_OK : E_NETSOCKLISTEN;
}
