#include "env.h"
#include "iopause.h"
#include "strerr.h"
#include "ndelay.h"

#include "socket_unix.h"
#include "socket_passfd.h"
#include "client.h"

static int incoming;
static iopause_fd io[1 + MAX_CLIENTS];
static iopause_fd *incomingio;

const char* servername;

void add_client() {
  int fd;

  fd = socket_recvfd(incoming);
  if(fd < 0) {
    strerr_warn1("tinyircd: error receiving fd", &strerr_sys);
    return;
  }

  ndelay_on(fd);
  client_new(fd);
}

void doit() {
  struct taia deadline;
  struct taia stamp;
  int iolen;
  int i;

  for(;;) {
    taia_now(&stamp);
    taia_uint(&deadline, 120);
    taia_add(&deadline, &deadline, &stamp);

    iolen = 0;

    incomingio = io + iolen++;
    incomingio->fd = incoming;
    incomingio->events = IOPAUSE_READ;

    for(i = 0; i < MAX_CLIENTS; ++i) {
      if(clients[i].active) {
        clients[i].io = io + iolen++;
        clients[i].io->fd = clients[i].fd;
        clients[i].io->events = IOPAUSE_READ;
        if(clients[i].out.p)
          clients[i].io->events |= IOPAUSE_WRITE;
      }
    }

    iopause(io, iolen, &deadline, &stamp);

    for(i = 0; i < MAX_CLIENTS; ++i) {
      if(clients[i].active) {
        if(clients[i].io->revents & IOPAUSE_READ)
          client_read(&clients[i]);
        if(clients[i].io->revents & IOPAUSE_WRITE)
          client_flush(&clients[i]);
      }
    }

    if(incomingio->revents & IOPAUSE_READ)
      add_client();
  }
}

int main(int argc, char **argv) {
  const char* x;

  servername = env_get("SERVERNAME");
  if(!servername)
    strerr_die1x(100, "tinyircd: $SERVERNAME not set");

  incoming = socket_unix_dgram();

  x = env_get("INCOMING_SOCKET");
  if(!x)
    strerr_die1x(100, "tinyircd: $INCOMING_SOCKET not net");
  if(socket_unix_bind(incoming, x) < 0)
    strerr_die3sys(111, "tinyircd: unable to bind to ", x, ": ");

  doit();
  /* not reached, silence warning */
  return 111;
}
