#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ipserv.h"
#include "debug.h"


struct ipserv_s {
  server_config_t config;
  socket_s sock;
};


ipserv *ipserv_create(const server_config_t *config) {

  ipserv *serv;
  int arg[2];
  os_error *err;
  socket_sockaddr addr;

  serv = malloc(sizeof(ipserv));
  memcpy(&serv->config, config, sizeof(server_config_t));
  if (!serv)    return NULL;

  err = xsocket_creat(socket_AF_INET, socket_SOCK_STREAM, socket_IPPROTO_TCP, &serv->sock);
  if (err) {
    free(serv);
    dprintf("ipserv_create() : failed to create socket (errno=%d)\n", err->errnum);
    return NULL;
  }

  arg[0] = 1;
  err = xsocket_setsockopt(serv->sock, socket_SOL_SOCKET, socket_SO_REUSEADDR, (byte const *)arg, 4);
  if (err) {
    xsocket_close(serv->sock);
    free(serv);
    dprintf("ipserv_create() : failed to set socket option (err=%d)\n", err->errnum);
    return NULL;
  }

  arg[0] = 1;
  arg[1] = 60; // one minute
  err = xsocket_setsockopt(serv->sock, socket_SOL_SOCKET, socket_SO_LINGER, (byte const *)arg, 8);
  if (err) {
    xsocket_close(serv->sock);
    free(serv);
    dprintf("ipserv_create() : failed to set socket option (err=%d)\n", err->errnum);
    return NULL;
  }

  arg[0] = 1;
  err = xsocket_ioctl(serv->sock,socket_FIONBIO,(byte *) arg);
  if (err) {
    xsocket_close(serv->sock);
    free(serv);
    dprintf("ipserv_create() : failed ioctl call (err=%d)\n", err->errnum);
    return NULL;
  }

  unsigned int port = config->port;
  memset(&addr, 0, sizeof(socket_sockaddr));
  addr.sockaddr_in.af = socket_AF_INET;
  addr.sockaddr_in.port = htons(port);
  err = xsocket_bind(serv->sock, &addr, sizeof(socket_sockaddr));
  if (err) {
    xsocket_close(serv->sock);
    free(serv);
    dprintf("ipserv_create() : failed to bind to port %d (err=%d)\n", port, err->errnum);
    return NULL;
  }

  err = xsocket_listen(serv->sock, 8);
  if (err) {
    xsocket_close(serv->sock);
    free(serv);
    dprintf("ipserv_create() : failed to listen on port %d (err=%d)\n", port, err->errnum);
    return NULL;
  }

  return serv;
}


int ipserv_accept(ipserv *serv, socket_s *sock) {
//  socket_fdset s;
//  socket_timeval timeout;
  socket_sockaddr addr;
  os_error *err;
  int len;

/*  int num;
  FD_ZERO(&s);
  FD_SET(serv->sock, &s);

  timeout.sec = 0;
  timeout.usec = 0;
  num = socket_select(FD_SETSIZE, &s, NULL, NULL, &timeout);
  if (num <= 0)   return 1; */
  
  len = sizeof(socket_sockaddr);

  err = xsocket_accept(serv->sock, &addr, &len, sock);

  if ((err == 0) && (*sock >= 0)) {
    int arg[1];
    arg[0] = 1;
    dprintf("Connection on socket %d\n", *sock);
    err = xsocket_ioctl(*sock, socket_FIONBIO, (byte *) arg);

    arg[0] = 128<<10; /* This is about as big as we can make it before we start running into problems (256K sometimes gets stuck and gives constant ENOBUFS errors) */
//    err = xsocket_setsockopt(*sock, socket_SOL_SOCKET, socket_SO_SNDBUF, (char *)arg, 4);

    arg[0] = 1;
    err = xsocket_ioctl(*sock, socket_FIOASYNC, (byte *) arg);
    return 0;
  }
  return 1;
}


void ipserv_close(ipserv *serv) {
  xsocket_close(serv->sock);
  free(serv);
}

const server_config_t *ipserv_config(ipserv *serv) {
  return &serv->config;
}
