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

#include "socket.h"
#include "intrepidcommon.h"

typedef struct
   {
   char machine[16]; /* IP Address */
   unsigned long int cpu_mips; /* /proc/cpuinfo bogomips */
   unsigned long int mem_total; /* /proc/meminfo MemTotal */
   double load_avg; /* uptime */
   time_t last_update;
   } slave_info;

typedef struct list list;

struct list
   {
   void *data;
   list *prev;
   list *next;
   };

list *machine_list;

static slave_info *find_machine (char *machine)
   {
   list *cur;
   slave_info *slave;
   
   for (cur = machine_list; cur != NULL; cur = cur->next)
      {
      slave = cur->data;
      if (strcmp (slave->machine, machine) == 0)
	 return slave;
      }
      return NULL;
   }

static void add_machine (char *machine, 
			 unsigned long int cpu_mips,
			 unsigned long int mem_total,			 
			 double load_avg)
   {
   slave_info *slave;
   list *new_list;

   printf ("Adding new machine: %s (%lu, %lu, %f)\n", machine, cpu_mips, mem_total, load_avg);
   
   slave = malloc (sizeof (slave_info));
   new_list = malloc (sizeof (list));
   
   strncpy (slave->machine, machine, 15);
   slave->machine[15] = '\0';
   slave->cpu_mips = cpu_mips;
   slave->mem_total = mem_total;
   slave->load_avg = load_avg;
   slave->last_update = time(NULL);

   new_list->data = slave;
   new_list->prev = NULL;
   new_list->next = machine_list;
   if (machine_list)
      machine_list->prev = new_list;
   
   machine_list = new_list;
   }

static slave_info *find_best_machine ()
   {
   list *cur;
   slave_info *slave, *best; 
   double power,cpu_mips,best_power=0;
   
   best = NULL;

   for (cur = machine_list; cur != NULL; cur = cur->next)
      {
      slave = cur->data;
      cpu_mips = slave->cpu_mips;
      if(slave->mem_total < 64000)
	 cpu_mips = cpu_mips * (slave->mem_total/64000);      
      power = (2 * (slave->mem_total/1024)) + cpu_mips;
      power = power * (1 - slave->load_avg);
      
      if (!best || 
	  (power >= best_power))
	 {
	 best = slave;
	 best_power = power;
	 }
      }
   
   return best;
   }

static int connect_cb (asocket *socket, void **user_data)
   {
   char mem_total_s [20];
   char cpu_mips_s [20];
   char load_avg_s [20];
   char command[10];
   unsigned long int mem_total, cpu_mips;
   double load_avg;
   slave_info *slave;
   
   /* printf ("New connection: %s\n", socket_info (socket)); */
   
   socket_recvline (socket, command, 9);
   
   if (strcmp (command, "SLAVE") == 0)
      {
      printf ("Slave Connection: %s\n", socket_info (socket));
      
      socket_recvline (socket, cpu_mips_s, 19);
      socket_recvline (socket, mem_total_s, 19);
      socket_recvline (socket, load_avg_s, 19);
      
      cpu_mips = strtoul (cpu_mips_s, NULL, 10);
      mem_total = strtoul (mem_total_s, NULL, 10);
      load_avg = strtod (load_avg_s, NULL);
      
      slave = find_machine (socket_info (socket));
      if (slave)
	 {
	 printf ("Updating machine: %s (%lu, %lu, %f)\n", socket_info (socket), cpu_mips, mem_total, load_avg);
	 slave->cpu_mips = cpu_mips;
	 slave->mem_total = mem_total;
	 slave->load_avg = load_avg;
	 slave->last_update = time(NULL);
	 }
      else
	 add_machine (socket_info (socket), cpu_mips, mem_total, load_avg);
      }
   else if (strcmp (command, "CLIENT") == 0)
      {
      printf ("Client Connection: %s\n", socket_info (socket));
      slave = find_best_machine ();
      if (slave)
	 socket_sendline (socket, slave->machine);
      else
	 socket_sendline (socket, "None");
      }
   else
      {
      printf ("Invalid command from: %s: %s\n", socket_info (socket), command);
      }
   
   return 0;
   }

int main (int argc, char *argv[])
   {
   asocket *socket;
   
   machine_list = NULL;
   
   socket = socket_listen (LBPORT);
   
   if (!socket)
      {
      perror ("socket_listen");
      return EXIT_FAILURE;
      }
   
   socket_server_start_threaded (socket, connect_cb, NULL);
   
   return EXIT_SUCCESS;
   }

