#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 "sounddefs.h"
#include "serversound.h"


extern ServerShip *ServerShips[MAX_PLAYERS];
extern ServerObject *ServerObjects[MAX_OBJECTS];


void *MainPlayerThread(void *threadid);
void SetupShipSendData(int shipnum, int threadnum, ShipDesc *ShipData);
void SetupObjSendData(int objnum, struct ObjectDesc *ObjData);
void SendPlayerData(int threadnum);
void SetupStatusSendData(char *GameStatus, int playernum);
void TransferData(char *SendData, int *DataSize, int threadnum);


void *MainPlayerThread(void *threadid)
{
  int n, rc;
  int threadnum;
  pthread_t readthread;

  threadnum=(int)threadid;

  SendPlayerData(threadnum);

  pthread_exit(NULL);
}


// Main function which loops and continuously sends data to the player
void SendPlayerData(int threadnum)
{
  int i, j;
  int DataSize=0;
  char SendData[MAX_PACKET_SIZE];
  char GameStatus[MAX_OBJECTS+MAX_PLAYERS+1];
  struct ObjectDesc ObjData;
  struct ShipDesc ShipData;

  while(!StopServer) {
    pthread_mutex_lock(&ServerLock);
#ifdef DEBUG
    printf("Main Player Thread %d Awake\n", threadnum);
#endif
    if (!ServerShips[threadnum]) {
      pthread_mutex_unlock(&ServerLock);
      break;
    }

    // Send object data to player
    for (i=0; i<MAX_OBJECTS; i++) {
      if (ServerObjects[i]) {
        *(SendData+DataSize)=OBJ_SEND_TYPE;
        DataSize++;
        SetupObjSendData(i, &ObjData);
        memcpy(SendData+DataSize, &ObjData, sizeof(struct ObjectDesc));
        DataSize+=sizeof(struct ObjectDesc);

        if (DataSize > PacketSize) {
          TransferData(SendData, &DataSize, threadnum);
        }
      }
    }
    
    // Send ship data to player
    for (i=0; i<MAX_PLAYERS; i++) {
      if (ServerShips[i]) {
        *(SendData+DataSize)=SHIP_SEND_TYPE;
        DataSize++;
        SetupShipSendData(i, threadnum, &ShipData);
        memcpy(SendData+DataSize, &ShipData, sizeof(struct ShipDesc));
        DataSize+=sizeof(struct ShipDesc);
      }

      if (DataSize > PacketSize) {
        TransferData(SendData, &DataSize, threadnum);
      }
    }

    
    // Send sound data to players
    for (i=0; i<MAX_SOUNDS; i++) {
      if (Sounds[threadnum][i].type!=-1) {
        *(SendData+DataSize)=SOUND_SEND_TYPE;
        DataSize++;
        memcpy(SendData+DataSize, &Sounds[threadnum][i],
               sizeof(SoundStruct));
        DataSize+=sizeof(SoundStruct);
        Sounds[threadnum][i].type=-1;
      }

      if (DataSize > PacketSize) {
        TransferData(SendData, &DataSize, threadnum);
      }
    }

    // Send general status update to player
    SetupStatusSendData(GameStatus, threadnum);
    *(SendData+DataSize)=STATUS_SEND_TYPE;
    memcpy(SendData+DataSize+1, GameStatus, MAX_OBJECTS+MAX_PLAYERS+1);
    DataSize+=MAX_OBJECTS+MAX_PLAYERS+1+1;
    TransferData(SendData, &DataSize, threadnum);

#ifdef DEBUG
    printf("Main Player Thread %d Sleeping\n", threadnum);
#endif
    pthread_mutex_unlock(&ServerLock);
    usleep(NetSleepTime*1000);
  }
}


void SetupShipSendData(int shipnum, int threadnum, ShipDesc *ShipData)
{
  ShipData->Number=shipnum;
  memcpy(&ShipData->Position, &ServerShips[shipnum]->Position, sizeof(struct Point));
  ShipData->Score=ServerShips[shipnum]->Score;
  ShipData->Lives=ServerShips[shipnum]->Lives;
  ShipData->Size=ServerShips[shipnum]->ObjectSize;
  ShipData->ShieldOn=ServerShips[shipnum]->Action.shield && ServerShips[shipnum]->Shield;
  strcpy(ShipData->PlayerName, ServerShips[shipnum]->PlayerName);
  if (ServerShips[shipnum]->AliveTime==-1) {
    ShipData->Alive=1;
  } else {
    ShipData->Alive=0;
  }

  if (ServerShips[shipnum]->WarpTime) {
    ShipData->Warping=1;
  } else {
    ShipData->Warping=0;
  }

  if (shipnum==threadnum) {
    ShipData->Shield=ServerShips[shipnum]->Shield;
    ShipData->Armor=ServerShips[shipnum]->Armor;
    ShipData->Warps=ServerShips[shipnum]->Warps;
    ShipData->Nukes=ServerShips[shipnum]->Nukes;
    if (ServerShips[shipnum]->ShakeTime) {
      ShipData->Shaking=1;
    } else {
      ShipData->Shaking=0;
    }
    memcpy(&ShipData->RotationMatrix, &ServerShips[shipnum]->RotationMatrix, sizeof(float [3][3]));
  } else {
    ShipData->Shield=0;
    ShipData->Armor=0;
    ShipData->Shaking=0;
    ShipData->Warps=0;
    ShipData->Nukes=0;
    memcpy(&ShipData->RotationMatrix, &ServerShips[shipnum]->InvRotationMatrix, sizeof(float [3][3]));
  }
}


void SetupObjSendData(int objnum, ObjectDesc *ObjData)
{
  ObjData->Type=ServerObjects[objnum]->ObjectType;
  ObjData->Number=objnum;
  ObjData->Size=ServerObjects[objnum]->ObjectSize;
  memcpy(&ObjData->Position, &ServerObjects[objnum]->Position, sizeof(struct Point));
  memcpy(&ObjData->RotationMatrix, &ServerObjects[objnum]->RotationMatrix, sizeof(float [3][3]));
}


//
//  Function to send the status of each object and player,
//  whether they're alive or dead
//
void SetupStatusSendData(char *GameStatus, int playernum)
{
  int i, j;

  for (i=0; i<MAX_OBJECTS; i++) {
    if (ServerObjects[i]) {
      GameStatus[i]=1;
    } else {
      GameStatus[i]=0;
    }
  }
  for (i=0; i<MAX_PLAYERS; i++) {
    if (ServerShips[i]) {
      GameStatus[i+MAX_OBJECTS]=1;
    } else {
      GameStatus[i+MAX_OBJECTS]=0;
    }
  }

  if (ServerGameRunning) {
    GameStatus[MAX_OBJECTS+MAX_PLAYERS]=1;
  } else {
    GameStatus[MAX_OBJECTS+MAX_PLAYERS]=0;
  }
}


void TransferData(char *SendData, int *DataSize, int threadnum)
{
  int rc;

  if ((rc=sendto(listenfd, SendData, *DataSize, 0,
                 (struct sockaddr *)&Connections[threadnum].PlayerIp,
                 sizeof(struct sockaddr))) < 0) {
    printf("write error\n");
    if (MaxPlayers==1) {
      StopServer=1;
    }
    if (ServerShips[threadnum]) {
      DeletePlayer(threadnum);
    }
    pthread_mutex_unlock(&ServerLock);
    pthread_exit(NULL);
  }
  *DataSize=0;
}
