//include <libnet.h>

#include "allegro.h"

#ifdef _WIN32
#include "winalleg.h"

#include <winsock.h>
#else
#define SOCKET_ERROR -1
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <string.h>
#endif

#include "client.h"
#include "defs.h"
#include "console.h"
#include "engine.h"
#include "gfx.h"
#include "items.h"
#include "network.h"
#include "packet.h"
#include "player.h"
#include "rules.h"
#include "sound.h"

#include "demos.h"

gameClient Client;

/* Initialize the client. */
int gameClient::Init(char *serveraddress, int serverport) {
    struct hostent *he;
    struct sockaddr_in their_addr;

    Console.WriteF("Starting the client...");
	if(NetworkInit()) return -1;
    Console.WriteF("Network initialized...");

    he = gethostbyname(serveraddress);
    if (he == NULL)
    {
        return -1;
    }

    if ((sendSock = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    {
        return -1;
    }

	if(!sendSock) {
		/* Error: Unable to open server connection. */
		return -1;
	}

    their_addr.sin_family = AF_INET;
    their_addr.sin_port = htons(serverport);
    their_addr.sin_addr = *((struct in_addr *)he->h_addr);
	memset (&their_addr.sin_zero, 0, 8);

    if (connect(sendSock, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)))
    {
		return -1;
    }
    NetSetNoDelay(sendSock);
    
    Console.WriteF("Client done");
	return 0;

}

/* Shut the client down. */
void gameClient::Exit() {
   closesocket(sendSock);
   NetShutdown();
}

int gameClient::CanGet(int size)
{
    fd_set readfds;
    struct timeval tv = {0, 0};
    int retval;

#ifdef DEMOS
    if (isPlaybackMode)
       return 1;
#endif

    FD_ZERO(&readfds);
    
    FD_SET (sendSock, &readfds);

	select (sendSock+1, &readfds, NULL, NULL, &tv);
	if (FD_ISSET (sendSock, &readfds)) {
		retval = recv (sendSock, (char *)packetbuffer, size, MSG_PEEK);
        if (retval == SOCKET_ERROR)
           return 0;
        if (retval == size) return 1;
	}
    return 0;
}

int gameClient::GetMessageID()
{
	fd_set readfds;
    struct timeval tv = {0, 0};
    int retval;
    char value;

#ifdef DEMOS
    if (isPlaybackMode)
    {
       if (readPacket())
          return packetbuffer[0];
       else
          return -1;
    }
#endif

    FD_ZERO(&readfds);
    
    FD_SET (sendSock, &readfds);

	select (sendSock+1, &readfds, NULL, NULL, &tv);
	if (FD_ISSET (sendSock, &readfds)) {
		retval = recv (sendSock, &value, 1, MSG_PEEK);
        if (retval == SOCKET_ERROR)
           return -1;
        return value;
	}
    return -1;
}

void gameClient::GetMessage(int size)
{
	fd_set readfds;
    struct timeval tv = {0, 0};
    int retval;
    char value;

#ifdef DEMOS
    if (isPlaybackMode)
       return;
#endif

    FD_ZERO(&readfds);
    
    FD_SET (sendSock, &readfds);

	select (sendSock+1, &readfds, NULL, NULL, &tv);
	if (FD_ISSET (sendSock, &readfds)) {
		retval = recv (sendSock, (char *)packetbuffer, size, 0);
	}
}

int gameClient::ProcessMessage(int id)
{
   if (id < 0) return 0;
   switch(id)
   {
      case PACKET_MAPN:
         if (CanGet(PACKET_MAPN_SIZE))
         {
            int i;
            GetMessage(PACKET_MAPN_SIZE);
            for (i=0; i<21; i++)
            {
               mapName[i] = packetbuffer[1+i];
            }
            Packet(CLIENT_JNRQ);
            PacketAddByte(players[0].localTeam);
            PacketAddByte(players[0].localSkin);
            for (i=0; i<21; i++)
            {
               PacketAddByte(players[0].playerName[i]);
            }
            SendPacket(packetsend, CLIENT_JNRQ_SIZE, 1);
         }
         break;
      case PACKET_JOIN:
         if (CanGet(PACKET_JOIN_SIZE))
         {
            int i, who;
            GetMessage(PACKET_JOIN_SIZE);
            who = packetbuffer[1];
            addPlayer(who);
            worms[who].team = packetbuffer[2];
            worms[who].skin = packetbuffer[3];
            renderSkin(who, worms[who].skin, worms[who].team);
            for (i=0; i<21; i++)
            {
               worms[who].name[i] = packetbuffer[4+i];
            }
         }
         break;
      case PACKET_MOVE:
         if (CanGet(PACKET_MOVE_SIZE))
         {
            int i;
            GetMessage(PACKET_MOVE_SIZE);
            for (i=0; i<GP_NUM_PLAYERS; i++)
            {
               bufor[i] = packetbuffer[1+i];
            }
            gotKeys = 1;
         }
         break;
      case PACKET_QUIT:
         if (CanGet(PACKET_QUIT_SIZE))
         {
            int who;
            GetMessage(PACKET_QUIT_SIZE);
            who = packetbuffer[1];
            removePlayer(who);
            Console.WriteF("%s has quit...", worms[who].name);
         }
         break;
      case PACKET_WEAP:
         if (CanGet(PACKET_WEAP_SIZE))
         {
            int i, who;
            GetMessage(PACKET_WEAP_SIZE);
            who = packetbuffer[1];
            for (i=0; i<5; i++)
            {
               worms[who].weaps[i] = packetbuffer[2+i];
            }
            worms[who].active = 2;
         }
         break;
      case PACKET_STRT:
         if (CanGet(PACKET_STRT_SIZE))
         {
            GetMessage(PACKET_STRT_SIZE);
            gameStart = 1;
         }
         break;
      case PACKET_PLNM:
         if (CanGet(PACKET_PLNM_SIZE))
         {
            GetMessage(PACKET_PLNM_SIZE);
            GP_NUM_PLAYERS = packetbuffer[1];
         }
         break;
      case PACKET_LOCL:
         if (CanGet(PACKET_LOCL_SIZE))
         {
            int i;
            GetMessage(PACKET_LOCL_SIZE);
            viewport[0].player = packetbuffer[1];
            
            Packet(CLIENT_WEAP);
            for (i=0; i<5; i++)
            {
               PacketAddByte(players[0].localWeaps[i]);
            }
            SendPacket(packetsend, CLIENT_WEAP_SIZE, 1);
         }
         break;
      case PACKET_GAME:
         if (CanGet(PACKET_GAME_SIZE))
         {
            int i;
            GetMessage(PACKET_GAME_SIZE);
            gameType = packetbuffer[1];
            teamplay = packetbuffer[2];
            tactics = 0;
            if (teamplay & 2)
            {
               teamplay -= 2;
               tactics = 1;
            }
            fragLimit = packetbuffer[4];
            fragLimit = (int)packetbuffer[3] + (fragLimit << 8);
            lifeLimit = packetbuffer[6];
            lifeLimit = (int)packetbuffer[5] + (lifeLimit << 8);
            Engine.weapLoadTime = packetbuffer[8];
            Engine.weapLoadTime = (Engine.weapLoadTime << 8) + packetbuffer[7];
            Console.WriteF("Load time is set to %d", Engine.weapLoadTime);
            Engine.wormFriendlyFire = packetbuffer[10];
            Engine.wormFriendlyFire = (int)packetbuffer[9] + (Engine.wormFriendlyFire << 8);
            GP_WORM_RESPAWN = packetbuffer[12];
            GP_WORM_RESPAWN = (int)packetbuffer[11] + (GP_WORM_RESPAWN << 8);
            instantReload = packetbuffer[13];
            itemBlowup = packetbuffer[14];
            Engine.rope.sticky = packetbuffer[15];
         }
         break;
      case PACKET_CHAT:
         if (CanGet(PACKET_CHAT_SIZE))
         {
            int who;
            GetMessage(PACKET_CHAT_SIZE);
            who = packetbuffer[1];
            Console.WriteF("%s: %s", worms[who].name, &packetbuffer[2]);
            Sound.playSound(talkSnd, worms[viewport[0].player].coords.x, worms[viewport[0].player].coords.y);
         }
         break;
      default:
         allegro_message("Unknown packet arrived");
         return 0;
         break;
   }
   return 1;
}

/* Send data to the server. */
void gameClient::SendPacket(unsigned char *data, int len, char ack) {

	net_send_rdm(sendSock, data, len);

}
