#include "../network.h"
#include "network_server.h"
#include <stdlib.h>
#include <stdio.h>

SOCKET listener;
clientState *rejecter;
clientState _clients[MAX_CLIENTS];

int numclients = 0;
int quit = FALSE;

void cleanup() {
  if (listener != -1)
    closesocket(listener);
#ifdef WIN32
  WSACleanup();
#endif
  free(rejecter);
}

int init_listener() {
  struct sockaddr_in addr;
  struct hostent *host;
  int result;

  // create listener socket
  listener = socket(PF_INET, SOCK_STREAM, 0);
  if (listener == -1) {
    perror("socket");
    cleanup();
    return -1;
  }

  // get information about the host
  memset(&addr, 0, sizeof(addr));
  host = gethostbyname("rikas.ath.cx");

  if (host == NULL) {
    perror("gethostbyname");
    cleanup();
    return -1;
  }

  // bind the socket to an address and port
  //memcpy(&addr.sin_addr, host->h_addr_list[0], sizeof(host->h_addr_list[0]));
  addr.sin_family = AF_INET;
  addr.sin_port   = htons(DEFAULT_PORT);

  result = bind(listener, (struct sockaddr *)&addr, sizeof(addr));
  if (result == -1) {
    perror("bind");
    cleanup();
    return -1;
  }

  //  listen for connections
  result = listen(listener, 5);
  if (result == -1) {
    perror("listen");
    cleanup();
    return -1;
  }
  return 0;
}

void add_client(int *maxfd) {
  int i;
  int accepted = FALSE;

  printf("We got a connection!\n");

  // check for an empty slot
  for (i=0; i<MAX_CLIENTS; i++) {
    // if slot empty
    if (!_clients[i].connected) {
      _clients[i].socket = accept(listener, NULL, NULL);

      // error accepting
      if (_clients[i].socket == -1) {
        perror("accept");
      }

      // client accepted
      else {
        accepted = TRUE;
        _clients[i].connected = TRUE;
        printf("accepted on %d!\n", _clients[i].socket);
        if (_clients[i].socket > *maxfd) {
          *maxfd = _clients[i].socket;
        }
        // increase number of clients
        numclients++;
        break;
      }
    }
  }

  // no more slots for client
  if (!accepted) {
    rejecter->socket = accept(listener, NULL, NULL);
    client_addPacket(&rejecter, PACKET_SERVER_FULL, "server full");
    printf("connection rejected!\n");
  }
}

int main() {
  struct timeval tv;
  int result = 0;
  int i;
  int delay = 4;
  packet *p;
  unsigned int maxfd;
  fd_set input;

  wsock_init();

  // cleans the client sockets
  memset(_clients, 0, sizeof(_clients));
  maxfd = listener;

  // listen for connections
  if (init_listener() < 0) {
    printf("error initializing listener\n");
    return 1;
  }

  // to reject connections
  rejecter = (clientState*)malloc(sizeof(clientState));

  while (!quit) {
    FD_ZERO(&input);
    FD_SET(listener, &input);

    // sets all active client sockets to read
    for (i=0; i<numclients; i++) {
      if (_clients[i].connected) {
        FD_SET(_clients[i].socket, &input);
      }
    }

    tv.tv_sec  = delay;
    tv.tv_usec = 0;

    printf("\n** TICK **\n");
    result = select(maxfd + 1, &input, NULL, NULL, &tv);
    switch (result) {
      // error in select
      case -1:
        perror("select"); break;

      // nothing to process
      case 0: break;

      // a number of sockets are ready for reading
      default: {
        //  accept connection
        if (FD_ISSET(listener, &input)) {
          printf("listener set\n");
          add_client(&maxfd);
        }
        // one of the sockets is sending data. find it
        else {
          for (i=0; i<numclients; i++) {
            if (FD_ISSET(_clients[i].socket, &input)) {
              printf("received from %d\n", _clients[i].socket);
              p = packet_read(&_clients[i]);

              // invalid packet (client crash causes this)
              if (p->type == PACKET_INVALID) {
                // one client out
                numclients--;
                free(p);
              }
              else {
                printf("received message type: %d %s\n", p->type, p->buffer);
                server_handle_packet(&_clients[i], p);
                free(p->buffer);
                free(p);
              }
            }
          }
        }
        break;
      }
    }
    // in the end send all pending packets
    for (i=0; i<numclients; i++) {
      client_sendall(&_clients[i]);
    }
  }

  // close all clients
  printf("closing all connections...\n");
  for (i=0; i<numclients; i++) {
    closesocket(_clients[i].socket);
  }
  cleanup();
  return (0);
}
