#include <stdio.h>
#include <libnet.h>
#include "network.h"

static NET_DRIVERLIST drivers;
static NET_DRIVERNAME *drivernames;

/* Server data */
static NET_CONN *client[MAX_CLIENTS] = { NULL, };
static NET_CONN *listen = NULL;

/* Client data */
static NET_CONN *server = NULL;

static int num_clients;

static int driver_count = 0;
static int init = 0;

void network_init(char *configfile)
{
   if (!init) {
      /* Configure libnet */
      net_loadconfig(configfile);

      /* Initialize Libnet */
      net_init();

      /* Detect drivers */
      drivers = net_detectdrivers(net_drivers_all);
      drivernames = net_getdrivernames(drivers); 
   
      driver_count = net_driverlist_count(drivers);
   }

   init = 1;
}

int get_nr_network_drivers(void)
{
   return driver_count;
}

char *get_network_driver_name(int n)
{
   if (n>=driver_count)
      return NULL;

   return drivernames[n].name;
}

/*******************/
/*  Server code    */
/*******************/

/* Return TRUE if the server was started succesfully */
int start_server(int driver_nr)
{
   NET_DRIVERLIST drv;
   int n;
   
   num_clients = 0;
   for (n = 0; n < MAX_CLIENTS; n++) {
      client[n] = NULL;
   }
   
   drv = net_driverlist_create();
   net_driverlist_clear(drv);
   net_driverlist_add(drv, drivernames[driver_nr].num);

   if (!net_initdrivers(drv)) {
      /* Error: Failed to initialize driver. */
      net_driverlist_destroy (drv);
      return 0;
   }
   net_driverlist_destroy(drv);

   listen = net_openconn(drivernames[driver_nr].num, "acae");
   if (!listen) {
      /* Error: Failed to open listening connection. */
      return 0;
   }

   if(net_listen(listen)) {
      /* Error: Cannot listen. */
      return 0;
   }
   
   /* Server started succesfully */
   return 1;
}

/* Shut the server down. */
void shutdown_server(void)
{
   int n;

   for (n = 0; n < MAX_CLIENTS; n++) {
      if (client[n]) {
         net_closeconn(client[n]);
         client[n] = NULL;
      }
   }

   if (listen) 
      net_closeconn(listen);
   listen = NULL;
   
   num_clients = 0;
}

/* Check for connecting clients.
   0 = no new connection yet 
   >0 number of open connections
*/
int server_listen(void)
{
   NET_CONN *newconn;
   int n;

   newconn = net_poll_listen(listen);

   if (newconn == NULL)
      return 0;
      
   /* Find free client */
   for (n=0; n<MAX_CLIENTS; n++) {
      if (!client[n]) {
         client[n] = newconn;
         num_clients++;
         return num_clients;
      }
   }

   /* Failed to add new client */
   net_closeconn(newconn);
   return 0;
}

/* Receive data from a client, and store them in the provided array (must
   have room for 1024 bytes). Returns the size of the stored data. */
int server_receive(void *data, int *sender)
{
   int l = 0;
   int n;
   
   for (n = 0; n < MAX_CLIENTS; n++) {
      if (client[n])   {
         l = net_receive_rdm (client[n], data, 1024);
         if(l > 0) {
            if (sender) 
               *sender = n;

            return l;
         }
      }
   }
   return 0;
}

/* Send data to a specific client. */
void server_send(void *data, int len, int whom)
{
   if (client[whom])
      net_send_rdm(client[whom], data, len);
}

/* Send data to all connected clients. */
void server_send_all(void *data, int len)
{
   int n;

   for(n = 0; n < MAX_CLIENTS; n++) {
      if (client[n])
         server_send(data, len, n);
   }
}

/* Tell the server to disconnect a client */
void server_disconnect_client(int whom)
{
   if (client[whom]) {
      net_closeconn(client[whom]);
      client[whom] = NULL;
      num_clients--;
   }
}

/*******************/
/*  Client code    */
/*******************/

int start_client(const char *serveraddress, const int driver_nr)
{
   NET_DRIVERLIST drv;
   
   num_clients = 0;
   
   drv = net_driverlist_create();
   net_driverlist_clear (drv);
   net_driverlist_add (drv, drivernames[driver_nr].num);

   if (!net_initdrivers (drv)) {
      /* Error: Failed to initialize driver. */
      net_driverlist_destroy (drv);
      return 0;
   }
   net_driverlist_destroy (drv);

   server = net_openconn(drivernames[driver_nr].num, NULL);

   if(!server) {
      /* Cannot connect to server */
      return 0;
   }

   if(net_connect(server, serveraddress)) {
      /* Error: Can't connect to that address. */
      net_closeconn(server);
      return 0;
   }
   
   /* Client started succesfully */
   return 1;
}

void shutdown_client(void)
{
   if(server)
      net_closeconn(server);
   server = NULL;
}

/* Returns 1 if the connection between the client and the server is up */
/* Returns 0 if there is no connection yet */
/* Returns < 0 if there was an error */
int client_is_connected(void) 
{
   int x;

   x = net_poll_connect(server);

   if (x == 0)
      return 0;

   if(x < 0) {
      net_closeconn(server);

      /* Error: Failed to receive server response. */
      return -1;
   }

   return 1;
}

/* Receive data from the server, and store them in the provided array (must
   have room for 1024 bytes). Returns the size of the stored data. */
int client_receive(void *data)
{
   return net_receive_rdm(server, data, 1024);
}

/* Send data to the server. */
void client_send(void *data, int len)
{
   net_send_rdm(server, data, len);
}
