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

#include "sys/socket.h"
//#include "sys/byteorder.h"
#include "sys/select.h"
#include "sys/ioctl.h"
#include "netinet/in.h"
#include "sys/errno.h"

#include "ipserv.h"


struct ipserv_s {
  int sock;
};


ipserv *ipserv_create(unsigned short port) {

  ipserv *serv;
  int arg[2], err;
  struct sockaddr_in addr;

  serv = malloc(sizeof(ipserv));
  if (!serv)    return NULL;

  serv->sock = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
  if (serv->sock == -1) {
    free(serv);
#ifndef BUILD_MODULE
    fprintf(stderr, "ipserv_create() : failed to create socket (errno=%d)\n", errno);
#endif
    return NULL;
  }

  err = setsockopt(serv->sock, SOL_SOCKET, SO_REUSEADDR, (char *)arg, 4);
  if (err) {
    close(serv->sock);
    free(serv);
#ifndef BUILD_MODULE
    fprintf(stderr, "ipserv_create() : failed to set socket option (err=%d)\n", err);
#endif
    return NULL;
  }

  arg[0] = 1;
  arg[1] = 60; // one minute
  err = setsockopt(serv->sock, SOL_SOCKET, SO_LINGER, (char *)arg, 8);
  if (err) {
    close(serv->sock);
    free(serv);
#ifndef BUILD_MODULE
    fprintf(stderr, "ipserv_create() : failed to set socket option (err=%d)\n", err);
#endif
    return NULL;
  }

  memset(&addr, 0, sizeof(struct sockaddr_in));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr.s_addr = 0;
  err = bind(serv->sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
  if (err) {
    close(serv->sock);
    free(serv);
#ifndef BUILD_MODULE
    fprintf(stderr, "ipserv_create() : failed to bind to port %d (err=%d)\n", port, err);
#endif
    return NULL;
  }

  err = listen(serv->sock, 8);
  if (err) {
    close(serv->sock);
    free(serv);
#ifndef BUILD_MODULE
    fprintf(stderr, "ipserv_create() : failed to listen on port %d (err=%d)\n", port, err);
#endif
    return NULL;
  }

  return serv;
}


int ipserv_accept(ipserv *serv, int *sock) {
  fd_set s;
  struct timeval timeout;
  struct sockaddr_in addr;
  int err, num, len;

  FD_ZERO(&s);
  FD_SET(serv->sock, &s);

  timeout.tv_sec = 0;
  timeout.tv_usec = 0;
  num = select(FD_SETSIZE, &s, NULL, NULL, &timeout);
  if (num <= 0)   return 1;

  *sock = accept(serv->sock, (struct sockaddr *)&addr, &len);
  if (*sock >= 0) {
    int arg[1];
    arg[0] = 1;
#ifndef BUILD_MODULE
    fprintf(stderr, "Connection on socket %d\n", *sock);
#endif
    err = ioctl(*sock, FIONBIO, arg);
    return 0;
  }
  return 1;
}


void ipserv_close(ipserv *serv) {

  close(serv->sock);
  free(serv);
}
