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


#include "servership.h"
#include "thread.h"
#include "serverdata.h"
#include "servergame.h"
#include "netdata.h"
#include "netconfig.h"
#include "mainplayerthread.h"


extern ServerShip *ServerShips[MAX_PLAYERS];


// Function to loop and continuously read all network data
void ReadNetworkData()
{
  int i, n, playernum, rc;
  char readval[64];
  int threadnum;
  int keypress;
  int WaitTime;
  struct sockaddr_in PlayerIp;
#ifdef CYGWIN
  int addrlen=16;
#else
  socklen_t addrlen=16;
#endif
  struct timezone tz;
  struct timeval now;
  KeyValuesStruct KeyValues;
  GameParametersStruct GameParameters;

  // Setup game parameters
  GameParameters.MaxPlayers=htons(MaxPlayers);
  GameParameters.WorldWidth=ServerWorldWidth;
  GameParameters.MaxShield=htonl(ServerMaxShield);
  GameParameters.MaxArmor=htonl(ServerMaxArmor);
  if (MaxPlayers==1) {
    GameParameters.GameType=htons(SINGLE_PLAYER);
  } else {
    GameParameters.GameType=htons(ServerGameType);
  }

  while (!StopServer) {
    if ((n=recvfrom(listenfd, readval, 64, 0,
                    (struct sockaddr *)&PlayerIp, &addrlen)) <= 0) {
      continue;
    }
    pthread_mutex_lock(&ServerLock);
#ifdef DEBUG
    printf("Read Network Data Thread Awake\n");
#endif

    // Check for status update packet
    if (n==sizeof(KeyValuesStruct)) {
      for (i=0; i<MAX_PLAYERS; i++) {
        if (Connections[i].Connected) {
          if (Connections[i].PlayerIp.sin_addr.s_addr==PlayerIp.sin_addr.s_addr &&
              Connections[i].PlayerIp.sin_port==PlayerIp.sin_port) {
            gettimeofday(&Connections[i].LastPacketTime, &tz);
            memcpy(&KeyValues, readval, sizeof(KeyValuesStruct));
            /*
              if (KeyValues.xForwardKey && !KeyValues.xBackwardKey) {
              ServerShips[i]->xAcceleration=1.0;
              } else if (KeyValues.xBackwardKey && !KeyValues.xForwardKey) {
              ServerShips[i]->xAcceleration=-1.0;
              } else {
              ServerShips[i]->xAcceleration=0.0;
              }
              if (KeyValues.yForwardKey && !KeyValues.yBackwardKey) {
              ServerShips[i]->yAcceleration=1.0;
              } else if (KeyValues.yBackwardKey && !KeyValues.yForwardKey) {
              ServerShips[i]->yAcceleration=-1.0;
              } else {
              ServerShips[i]->yAcceleration=0.0;
              }
            */
            if (KeyValues.zForwardKey && !KeyValues.zBackwardKey) {
              ServerShips[i]->zAcceleration=3.0;
            } else if (KeyValues.zBackwardKey && !KeyValues.zForwardKey) {
              ServerShips[i]->zAcceleration=-1.0;
            } else {
              ServerShips[i]->zAcceleration=0.0;
            }

            if (KeyValues.xFRotateKey && !KeyValues.xBRotateKey) {
              ServerShips[i]->Rotation.x=1.0;
            } else if (KeyValues.xBRotateKey && !KeyValues.xFRotateKey) {
              ServerShips[i]->Rotation.x=-1.0;
            } else {
              ServerShips[i]->Rotation.x=0.0;
            }
            if (KeyValues.yFRotateKey && !KeyValues.yBRotateKey) {
              ServerShips[i]->Rotation.y=1.0;
            } else if (KeyValues.yBRotateKey && !KeyValues.yFRotateKey) {
              ServerShips[i]->Rotation.y=-1.0;
            } else {
              ServerShips[i]->Rotation.y=0.0;
            }
            if (KeyValues.zFRotateKey && !KeyValues.zBRotateKey) {
              ServerShips[i]->Rotation.z=1.0;
            } else if (KeyValues.zBRotateKey && !KeyValues.zFRotateKey) {
              ServerShips[i]->Rotation.z=-1.0;
            } else {
              ServerShips[i]->Rotation.z=0.0;
            }

            ServerShips[i]->Action.shield=KeyValues.ShieldKey;
          }
        }
      }
    } else if (n==MAX_PLAYERNAME_LENGTH) {
      // Check if player is already connected, if so delete them and reconnect
      for (i=0; i<MAX_PLAYERS; i++) {
        if (Connections[i].Connected) {
          if (Connections[i].PlayerIp.sin_addr.s_addr==PlayerIp.sin_addr.s_addr &&
              Connections[i].PlayerIp.sin_port==PlayerIp.sin_port) {
            DeletePlayer(i);
          }
        }
      }

      if (NumConnectedPlayers==MaxPlayers) {
        pthread_mutex_unlock(&ServerLock);
        continue;
      }

      for (i=0; i<MAX_PLAYERS; i++) {
        if (!Connections[i].Connected && !ServerShips[i]) {
          // Send Game Parameters to player and connect them to the game
          GameParameters.PlayerNum=htons(i);
          if (sendto(listenfd, &GameParameters, sizeof(GameParametersStruct),
                     0, (struct sockaddr *)&PlayerIp, addrlen) < 0) {
            printf("Write Error\n");
            break;
          }

          memcpy(&Connections[i].PlayerIp, &PlayerIp,
                 sizeof(struct sockaddr_in));
          gettimeofday(&Connections[i].LastPacketTime, &tz);
          readval[MAX_PLAYERNAME_LENGTH-1]='\0';

          // Spawn off a thread for the connected player
          if (rc = pthread_create(&ServerThreads[i], NULL,
                                  MainPlayerThread, (void *)i)) {
            printf("ERROR, return code from pthread_create() is %d\n", rc);
            exit(8);
          }

          struct Point Position, Rotation, Velocity;
          CreateRandomPosition(&Position);
          Rotation.x=0.0;
          Rotation.y=0.0;
          Rotation.z=0.0;
          Velocity.x=0.0;
          Velocity.y=0.0;
          Velocity.z=0.0;
          ServerShips[i]=new ServerShip(Position, Velocity, Rotation, SHIP_TYPE,
                                        ShipSize, 1.0, i, readval);
          break;
        }
      }
    } else if (n==1) {
      for (i=0; i<MAX_PLAYERS; i++) {
        if (Connections[i].Connected) {
          if (Connections[i].PlayerIp.sin_addr.s_addr==PlayerIp.sin_addr.s_addr &&
              Connections[i].PlayerIp.sin_port==PlayerIp.sin_port) {
            keypress=*readval/0x10;
            if (*readval > 0x10) {
              *readval-=0x10;
            }

            if (keypress) {
              switch (*readval) {
              case FIRE_KEY:
                ServerShips[i]->Action.shooting=1;
                break;
              case WARP_KEY:
                ServerShips[i]->WarpShip();
                break;
              case NUKE_KEY:
                ServerShips[i]->FireNuke();
                break;
              case X_FORWARD_KEY:
                ServerShips[i]->Accelerate(1, 1.0);
                break;
              case Y_FORWARD_KEY:
                ServerShips[i]->Accelerate(2, 1.0);
                break;
              case Z_FORWARD_KEY:
                ServerShips[i]->Accelerate(3, 3.0);
                break;
              case X_BACKWARD_KEY:
                ServerShips[i]->Accelerate(1, -1.0);
                break;
              case Y_BACKWARD_KEY:
                ServerShips[i]->Accelerate(2, -1.0);
                break;
              case Z_BACKWARD_KEY:
                ServerShips[i]->Accelerate(3, -1.0);
                break;
              case X_FROTATE_KEY:
                ServerShips[i]->Rotation.x+=1.0;
                if (ServerShips[i]->Rotation.x > 1.0) {
                  ServerShips[i]->Rotation.x=1.0;
                }
                break;
              case Y_FROTATE_KEY:
                ServerShips[i]->Rotation.y+=1.0;
                if (ServerShips[i]->Rotation.y > 1.0) {
                  ServerShips[i]->Rotation.y=1.0;
                }
                break;
              case Z_FROTATE_KEY:
                ServerShips[i]->Rotation.z+=1.0;
                if (ServerShips[i]->Rotation.z > 1.0) {
                  ServerShips[i]->Rotation.z=1.0;
                }
                break;
              case X_BROTATE_KEY:
                ServerShips[i]->Rotation.x-=1.0;
                if (ServerShips[i]->Rotation.x < -1.0) {
                  ServerShips[i]->Rotation.x=-1.0;
                }
                break;
              case Y_BROTATE_KEY:
                ServerShips[i]->Rotation.y-=1.0;
                if (ServerShips[i]->Rotation.y < -1.0) {
                  ServerShips[i]->Rotation.y=-1.0;
                }
                break;
              case Z_BROTATE_KEY:
                ServerShips[i]->Rotation.z-=1.0;
                if (ServerShips[i]->Rotation.z < -1.0) {
                  ServerShips[i]->Rotation.z=-1.0;
                }
                break;
              case SHIELD_KEY:
                ServerShips[i]->Action.shield=1;
                break;
              case ABORT_KEY:
                if (MaxPlayers==1) {
                  StopServer=1;
                }
                if ((rc=sendto(listenfd, "\0", 1, 0,
                               (struct sockaddr *)&Connections[i].PlayerIp,
                               sizeof(struct sockaddr))) < 0) {
                  printf("write error\n");
                }
                DeletePlayer(i);
                break;
              }
            } else {
              switch (*readval) {
              case X_FORWARD_KEY:
                ServerShips[i]->Accelerate(1, 0.0);
                break;
              case Y_FORWARD_KEY:
                ServerShips[i]->Accelerate(2, 0.0);
                break;
              case Z_FORWARD_KEY:
                ServerShips[i]->Accelerate(3, 0.0);
                break;
              case X_BACKWARD_KEY:
                ServerShips[i]->Accelerate(1, 0.0);
                break;
              case Y_BACKWARD_KEY:
                ServerShips[i]->Accelerate(2, 0.0);
                break;
              case Z_BACKWARD_KEY:
                ServerShips[i]->Accelerate(3, 0.0);
                break;
              case X_FROTATE_KEY:
                ServerShips[i]->Rotation.x-=1.0;
                if (ServerShips[i]->Rotation.x < -1.0) {
                  ServerShips[i]->Rotation.x=-1.0;
                }
                break;
              case Y_FROTATE_KEY:
                ServerShips[i]->Rotation.y-=1.0;
                if (ServerShips[i]->Rotation.y < -1.0) {
                  ServerShips[i]->Rotation.y=-1.0;
                }
                break;
              case Z_FROTATE_KEY:
                ServerShips[i]->Rotation.z-=1.0;
                if (ServerShips[i]->Rotation.z < -1.0) {
                  ServerShips[i]->Rotation.z=-1.0;
                }
                break;
              case X_BROTATE_KEY:
                ServerShips[i]->Rotation.x+=1.0;
                if (ServerShips[i]->Rotation.x > 1.0) {
                  ServerShips[i]->Rotation.x=1.0;
                }
                break;
              case Y_BROTATE_KEY:
                ServerShips[i]->Rotation.y+=1.0;
                if (ServerShips[i]->Rotation.y > 1.0) {
                  ServerShips[i]->Rotation.y=1.0;
                }
                break;
              case Z_BROTATE_KEY:
                ServerShips[i]->Rotation.z+=1.0;
                if (ServerShips[i]->Rotation.z > 1.0) {
                  ServerShips[i]->Rotation.z=1.0;
                }
                break;
              case SHIELD_KEY:
                ServerShips[i]->Action.shield=0;
                break;
              }
            }
            break;
          }
        }
      }
    }

    // Check if any players are timed out
    gettimeofday(&now, &tz);
    for (i=0; i<MAX_PLAYERS; i++) {
      if (Connections[i].Connected) {
        WaitTime=(now.tv_sec-Connections[i].LastPacketTime.tv_sec)*1000000+
                 (now.tv_usec-Connections[i].LastPacketTime.tv_usec);
        // Check if a player hasn't responded for 10 seconds
        if (WaitTime > 10000000) {
          if (MaxPlayers==1) {
            StopServer=1;
          }
          DeletePlayer(i);
        }
      }
    }

#ifdef DEBUG
    printf("Read Network Data Thread Sleeping\n");
#endif
    pthread_mutex_unlock(&ServerLock);
  }

  close(listenfd);

  ServerStarted=0;

  pthread_exit(NULL);
}
