#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <ctype.h>
#include <dos.h>
#include <process.h>
#include <stdarg.h>
#include <bios.h>
#include <time.h>
#include <ctype.h>
#include "types.h"
#include "_comm.h"
#include "comm.h"
#include "error.h"
#include "gamecom.h"
#include "crc.h"

/*
===============
=
= Global Variables
=
===============
*/

comm_type comm;
extern  int      _argc;
extern  char **  _argv;

/*
===============
=
= Local Variables
=
===============
*/

//
// Same structure as in real mode
//
static gamecom_t * gamecom;
static union REGS comregs; // REGS used for communication to real mode
static boolean GAMECOM_Started=false; // whether GAMECOM has been started




/*
=================
=
= CheckParm
=
= Checks for the given parameter in the program's command line arguments
=
= Returns the argument number (1 to argc-1) or 0 if not present
=
=================
*/

int8 CheckParm (char *check)
   {
   int8      i;
	char	*parm;
   char  delims[4] = {'\\','-','/','\0'};

	for (i = 1;i<_argc;i++)
      {
      if ((parm = strtok(_argv[i],&delims[0])) == NULL)
         continue;

      if (!stricmp(check,parm) )
			return i;
      }

	return 0;
   }


/*
===============
=
= InitGAMECOM
=
===============
*/

void InitGAMECOM (void)
   {
	int32 netarg;
	int32 netaddress;

	if (GAMECOM_Started==true)
		return;
	GAMECOM_Started=true;

	netarg=CheckParm ("net");

   if ( netarg == 0 )
      {
      printf("NET parameter is not present on the command line\n");
      GAMECOM_Started=false;
      return;
      }

	netarg++;

	netaddress=atol(_argv[netarg]);
	gamecom=(gamecom_t *)netaddress;
   printf("COMMIT: Communicating on vector %ld\n",gamecom->intnum);
   comm.packet = malloc( MAXPACKETSIZE );
   comm.numplayers = gamecom->numplayers;
//   comm.consoleplayer = gamecom->consoleplayer-1;
   }

/*
===============
=
= GAMECOMActive
=
===============
*/

boolean GAMECOMActive (void)
   {
   return GAMECOM_Started;
   }

/*
================
=
= ReadPacket
=
================
*/

boolean ReadPacket (void)
   {

   // Set command (Get Packet)

	gamecom->command=CMD_GET;

   // Check to see if a packet is ready

	int386(gamecom->intnum,&comregs,&comregs);

   // Is it ready?

   if (gamecom->remotenode!=-1)
      {
      uint16 crc;
      uint16 sentcrc;
      int32 CRCoffset;

      //
      // A packet is waiting
      //
      // Calculate CRC of packet
      // not necessary in NETWORK mode but still makes good sense
      //

      CRCoffset = gamecom->datalength-sizeof(uint16);

      crc = CalculateCRC (&gamecom->data[0], CRCoffset);

      //
      // Get CRC inside packet
      //

      sentcrc = *((uint16 *)(&gamecom->data[ CRCoffset ]));

      //
      // Are the crcs the same?
      //

      if (crc!=sentcrc)
         {
         //
         // They aren't so indicate that this packet was bad
         //

         comm.badpacket=true;
         }
      else
         {
         //
         // Packet is valid and error free (upto a 16bit CRC at least)
         //
         comm.badpacket=false;
         }

      //
      // set who it came from
      //
      comm.sender = gamecom->remotenode;
      comm.packetlength = CRCoffset;

      memcpy(comm.packet, &gamecom->data[0], CRCoffset);

      return true;
      }
   else
      {
      //
      // Not ready yet
      //

      return false;
      }
   }


/*
=============
=
= WritePacket
=
=============
*/

void WritePacket (void * buffer, int32 length, int32 destination)
   {
   uint16 crc;

   //
   // set send command
   //
	gamecom->command=CMD_SEND;

   //
   // set destination
   //
   if (destination==-1)
      gamecom->remotenode=MAXNETNODES;
   else
      gamecom->remotenode=destination;

   if ( length > ( MAXPACKETSIZE - sizeof(uint16) ) )
      {
      Error("WritePacket: Overflowed buffer\n");
      }

   //
   // copy local buffer into realmode buffer
   //
   memcpy((uint8 *)&(gamecom->data[0]),(uint8 *)buffer,length);

   //
   // calculate CRC
   //
   crc = CalculateCRC (buffer, length);

   //
   // put CRC into realmode buffer packet
   //
   *((uint16 *)&gamecom->data[length])=crc;

   //
   // set size of realmode packet including crc
   //
   gamecom->datalength=length+sizeof(uint16);

   //
   // send it on its way
   //
   int386(gamecom->intnum,&comregs,&comregs);
   }




/*
=============
=
= ValidSyncPacket
=
=============
*/
boolean ValidSyncPacket ( int32 * remotecommand )
   {
   syncpackettype * sync;

   if (ReadPacket() && (comm.badpacket==false))
      {
      sync = (syncpackettype *)comm.packet;

      if ( sync->checksum == SYNCCHECKSUM )
         {
         (*remotecommand) = sync->command;
         return true;
         }
      }
   return false;
   }

/*
=============
=
= SendSyncPacket
=
=============
*/
void SendSyncPacket ( int32 command, int32 dest)
   {
   syncpackettype sync;

   sync.checksum = SYNCCHECKSUM;
   sync.command = command;
   WritePacket( &sync , sizeof(syncpackettype) , dest );
   }

/*
=============
=
= FindPlayer
=
=============
*/

void FindPlayer ( int player )
   {
   int32 command;

   while (
          ( ValidSyncPacket(&command) == false ) ||
          ( command != SYNC_HEREIAM ) ||
          ( comm.sender != player )
         )
      {
      ;
      }
   }

/*
=============
=
= SyncToMaster
=
=============
*/

void SyncToMaster ( void )
   {
   boolean done;
   time_t time;
   int32 oldsec;

   done = false;
   time = clock();
   oldsec = -1;

   while ( done == false )
      {
      int32 command;

      if ( ValidSyncPacket(&command) == true )
         {
         if (
             (comm.sender == 0) &&
             (command == SYNC_STARTGAME)
            )
            {
            done = true;
            }
         }
      time = clock();
      time /=CLOCKS_PER_SEC;
      if (time == oldsec)
			continue;
      oldsec = time;
      SendSyncPacket ( SYNC_HEREIAM, 0);
      }
   }

/*
=============
=
= FlushPackets
=
=============
*/

void FlushPackets ( void )
   {
   time_t time;
   int32 oldsec;


   time = clock();
   time /=CLOCKS_PER_SEC;
   oldsec = time+1;

   while (oldsec > time)
      {
      ReadPacket();
      time = clock();
      time /=CLOCKS_PER_SEC;
      }
   }

/*
=============
=
= SyncPlayers
=
=============
*/

void SyncPlayers ( void )
   {
   int i;

   if ( comm.consoleplayer == 0 )
      {
      for ( i=1; i < comm.numplayers; i++ )
         {
         FindPlayer( i );
         }

      for ( i=1; i < comm.numplayers; i++ )
         {
         SendSyncPacket ( SYNC_STARTGAME, i);
         SendSyncPacket ( SYNC_STARTGAME, i);
         }
      }
   else
      {
      SyncToMaster();
      }
   FlushPackets ();
   }

