#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <limits.h>
#include <iostream.h>
#include <fstream.h>
#include <strings.h>
#include <string.h>
#include <new>
#include <list>

#include "server.h"
#include "comm.h"
#include "ego.h"
#include "nanny.h"
#include "support.h"

int main(int argc, char** argv)
{
   signal( SIGTERM,   (&sig_term_handler));
   signal( SIGALRM,   (&sig_term_handler));
   signal( SIGFPE,    (&sig_term_handler));
   signal( SIGHUP,    (&sig_term_handler));
   signal( SIGILL,    (&sig_term_handler));
   signal( SIGINT,    (&sig_term_handler));
   signal( SIGIO,     (&sig_term_handler));
   signal( SIGPIPE,   (&sig_term_handler));
   signal( SIGPROF,   (&sig_term_handler));
   signal( SIGQUIT,   (&sig_term_handler));
   signal( SIGSTOP,   (&sig_term_handler));
   signal( SIGTERM,   (&sig_term_handler));
   signal( SIGTSTP,   (&sig_term_handler));
   signal( SIGTTIN,   (&sig_term_handler));
   signal( SIGTTOU,   (&sig_term_handler));
   signal( SIGUSR1,   (&sig_term_handler));
   signal( SIGUSR2,   (&sig_term_handler));
   signal( SIGVTALRM, (&sig_term_handler));
   signal( SIGXCPU,   (&sig_term_handler));
   signal( SIGXFSZ,   (&sig_term_handler));
   signal( SIGSEGV,   (&sig_term_handler));
   signal( SIGSEGV,   (&sig_term_handler));

   srand( time(0) );
   run_the_game( 4000 );
   return server_reboot;
}

void run_the_game( int port )
{
   int  s;
   s = init_socket( port );
   game_loop( s );
   close_sockets( s );
   }

void game_loop( int s )
{
   list<Ego*>::iterator itr;
   list<Ego*> Bad_Elist;
   Ego* newlink;
   int pulse = 0;
   int did_pulse = 0;
   int link_count = 0;
   int last_link_count = 0;
   fd_set input_set, output_set, exc_set;
   struct timeval last_time, now, timespent, timeout, null_time;
   struct timeval time_since_pulse;
   struct timezone init;
   static struct timeval opt_time;

   null_time.tv_sec = 0;
   null_time.tv_usec = 0;
   time_since_pulse.tv_sec = 0;
   time_since_pulse.tv_usec = 0;
   opt_time.tv_usec = PULSE;
   opt_time.tv_sec = 0;
   gettimeofday( &last_time, NULL );

   while( !do_shutdown )
   {
      FD_ZERO( &input_set );
      FD_ZERO( &output_set );
      FD_ZERO( &exc_set );
      FD_SET( s, &input_set );
      int maxdesc = s;
      int desc;

      for( itr = Bad_Elist.begin(); itr != Bad_Elist.end(); itr++ )
      {
         Elist.remove( *itr );
         link_count--;
         delete (*itr);
         }
      Bad_Elist.clear();

      Elist.sort();
      Elist.unique();

      for( itr = Elist.begin(); itr != Elist.end(); itr++ )
      {
         (*itr)->idle++;
         desc = (*itr)->descriptor();
         if( (*itr)->getstate() == WAITTOCLOSE )
         {
            Bad_Elist.push_back(*itr);
         }
         else if( desc <= 0 )
         {
            cerr << "Descriptor out of range\n";
            Bad_Elist.push_back( *itr );
         }
         else
         {
            if(desc > maxdesc)
               maxdesc = desc;
            FD_SET(desc, &input_set);
            FD_SET(desc, &exc_set);
            FD_SET(desc, &output_set);
         }
      }
      for( itr = Bad_Elist.begin(); itr != Bad_Elist.end(); itr++ )
      {
         Elist.remove( *itr );
         delete (*itr);
         link_count--;
      }
      Bad_Elist.clear();

      gettimeofday( &now, &init );
      timespent = timediff( &now, &last_time );
      timeout = timediff( &opt_time, &timespent );
      last_time.tv_sec = now.tv_sec + timeout.tv_sec;
      last_time.tv_usec = now.tv_usec + timeout.tv_usec;
      if( last_time.tv_usec >= 1000000 )
      {
         last_time.tv_usec -= 1000000;
         last_time.tv_sec++;
      }
      time_since_pulse.tv_usec += last_time.tv_usec;
      time_since_pulse.tv_sec += last_time.tv_sec;
      if(time_since_pulse.tv_usec >= 1000000)
      {
         time_since_pulse.tv_usec -= 1000000;
         time_since_pulse.tv_sec++;
      }
      if(time_since_pulse.tv_sec >= 10)
      {
         pulse++;
         time_since_pulse.tv_usec = 0;
         time_since_pulse.tv_sec = 0;
         did_pulse = TRUE;
      }

      null_time.tv_sec = 0;
      null_time.tv_usec = 0;

      if(last_link_count != link_count)
      {
         cerr << "Links: " << link_count << " Pulse: " << pulse << "\n";
         last_link_count=link_count;
      }

      if( select( maxdesc + 1, &input_set, &output_set,
                               &exc_set, &null_time ) < 0 )
      {
         if(errno == EBADF)
         {
            do_shutdown = TRUE;
            exit(100);
         }
         else if(errno != EINTR)
         {
            do_shutdown = TRUE;
            exit(100);
         }
      }

      if( select( 0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout ) < 0 )
      {
         if( errno == EBADF )
         {
            do_shutdown = TRUE;
            exit(100);
         }
         else if(errno != EINTR)
         {
            do_shutdown = TRUE;
            exit(100);
         }
      }

// Process any new connections
      if( FD_ISSET( s, &input_set ) )
      {
         newlink = new Ego( "guest", new_connection( s ) );
         Elist.push_back( newlink );
         link_count++;
      }

      for( itr = Elist.begin(); itr != Elist.end(); itr++ )
         nanny(*itr);

// Read data if availible from all connections
      for( itr = Elist.begin(); itr != Elist.end(); itr++ )
      {
         desc=(*itr)->descriptor();
         if( FD_ISSET( desc, &exc_set ) )
         {
            FD_CLR( desc, &input_set );
            FD_CLR( desc, &output_set );
            Bad_Elist.push_back( *itr );
            continue;
         }
         if( FD_ISSET( desc, &input_set ) )
            if(!(*itr)->read())
            {
               FD_CLR(desc, &output_set);
               Bad_Elist.push_back(*itr);
            }
      }
// Write data to all connections
      for( itr = Elist.begin(); itr != Elist.end(); itr++ )
         (*itr)->write();
   }
   cout << "Shutting down" << endl;
}

