#include "servership.h"
#include "servergame.h"
#include "serverdata.h"
#include "serversound.h"
#include "netconfig.h"
#include "sizes.h"


ServerShip::ServerShip(struct Point Pos, struct Point Vel, struct Point Rot, int Type, float Size, float Mass, int playernum, char *playername, int bot) : ServerObject(Pos, Vel, Rot, Type, Size, Mass, playernum) {
  Bot=bot;
  InitPlayer();
  strcpy(PlayerName, playername);
  if (!Bot) {
    Connections[PlayerNum].Connected=1;
    NumConnectedPlayers++;
  }

  printf("Adding Player %d\n", PlayerNum);
}


ServerShip::~ServerShip()
{
  if (!Bot) {
    NumConnectedPlayers--;
    Connections[PlayerNum].Connected=0;
  }

  printf("Deleting Player %d\n", PlayerNum);
}


void ServerShip::InitPlayer()
{
  int i;

  for (i=0; i<MAX_SOUNDS; i++) {
    Sounds[PlayerNum][i].type=-1;
  }

  CreateRandomPosition(&Position);

  InitMatrix(InvRotationMatrix);

  InitMatrix(RotationMatrix);

  Acceleration.x=0.0;
  Acceleration.y=0.0;
  Acceleration.z=0.0;

  xAcceleration=0.0;
  yAcceleration=0.0;
  if (Bot) {
    zAcceleration=3.0*BotRatio;
    NextBotFireTime=0;
    BotShieldOnTime=0;
    Shield=(int)(BotRatio*(float)StartShield);
    Armor=(int)(BotRatio*(float)StartArmor);
  } else {
    zAcceleration=0.0;
    Shield=StartShield;
    Armor=StartArmor;
  }
  Warps=StartWarps;
  WarpTime=0;
  Nukes=StartNukes;

  Action.shield=0;
  Action.shooting=0;

  ShakeTime=0;
  ShotPos=1.0;
  Score=0;

  if (ServerGameType==DEATHMATCH) {
    Lives=-1;
  } else {
    Lives=StartLives;
  }

  AliveTime=-1;
}


void ServerShip::UpdateRotation()
{
  float Multiplier=RotationSpeed;

  if (WarpTime) {
    return;
  }

  if (Bot) {
    Multiplier*=BotRatio;
  }

  if (AliveTime==-1) {
    xRotateMatrix(Multiplier*Rotation.x, RotationMatrix, 2);
    yRotateMatrix(Multiplier*Rotation.y, RotationMatrix, 2);
    zRotateMatrix(Multiplier*Rotation.z, RotationMatrix, 2);

    xRotateMatrix(-Multiplier*Rotation.x, InvRotationMatrix, 1);
    yRotateMatrix(-Multiplier*Rotation.y, InvRotationMatrix, 1);
    zRotateMatrix(-Multiplier*Rotation.z, InvRotationMatrix, 1);

    CalcAcceleration();
  }
}


void ServerShip::UpdateVelocity()
{
  float Distance;

  if (WarpTime) {
    return;
  }

  if (AliveTime==-1) {
    Velocity.x+=Acceleration.x;
    Velocity.y+=Acceleration.y;
    Velocity.z+=Acceleration.z;

    Velocity.x*=DecelerationConstant;
    Velocity.y*=DecelerationConstant;
    Velocity.z*=DecelerationConstant;
  }
}


void ServerShip::Accelerate(int type, float Accelerate)
{
  if (type==1) {
    xAcceleration=Accelerate*AccelerationConstant;
  } else if (type==2) {
    yAcceleration=Accelerate*AccelerationConstant;
  } else if (type==3) {
    zAcceleration=Accelerate*AccelerationConstant;
  }

  CalcAcceleration();
}


void ServerShip::CalcAcceleration()
{
  Acceleration.x=-0.001*xAcceleration*RotationMatrix[0][0];
  Acceleration.y=-0.001*xAcceleration*RotationMatrix[0][1];
  Acceleration.z=-0.001*xAcceleration*RotationMatrix[0][2];
  Acceleration.x-=0.001*yAcceleration*RotationMatrix[1][0];
  Acceleration.y-=0.001*yAcceleration*RotationMatrix[1][1];
  Acceleration.z-=0.001*yAcceleration*RotationMatrix[1][2];
  Acceleration.x-=0.001*zAcceleration*RotationMatrix[2][0];
  Acceleration.y-=0.001*zAcceleration*RotationMatrix[2][1];
  Acceleration.z-=0.001*zAcceleration*RotationMatrix[2][2];
}


void ServerShip::die()
{
  AliveTime=250;
  ShakeTime=0;
  WarpTime=0;

  Position.x+=1.0*RotationMatrix[2][0];
  Position.y+=1.0*RotationMatrix[2][1];
  Position.z+=1.0*RotationMatrix[2][2];

  Velocity.x=0.0;
  Velocity.y=0.0;
  Velocity.z=0.0;

  Shield=0;
  Action.shooting=0;
  Action.shield=0;
}


void ServerShip::live()
{
  AliveTime=-1;

  CreateRandomPosition(&Position);

  InitMatrix(InvRotationMatrix);
  InitMatrix(RotationMatrix);

  Velocity.x=0.0;
  Velocity.y=0.0;
  Velocity.z=0.0;

  Acceleration.x=0.0;
  Acceleration.y=0.0;
  Acceleration.z=0.0;

  xAcceleration=0.0;
  yAcceleration=0.0;
  if (Bot) {
    zAcceleration=3.0*BotRatio;
    Shield=(int)(BotRatio*(float)StartShield);
    Armor=(int)(BotRatio*(float)StartArmor);
  } else {
    zAcceleration=0.0;
    Shield=StartShield;
    Armor=StartArmor;
  }

  Warps=StartWarps;
  Nukes=StartNukes;

  Action.shooting=0;
  Action.shield=0;
}


void ServerShip::FireShot()
{
  struct Point shotVelocity, shotPosition, shotRotation;
  int i, j;

  if (AliveTime==-1) {
    AddSound(PlayerNum, &Position, ShotSound);
    shotVelocity.x=Velocity.x-ShotSpeed*RotationMatrix[2][0];
    shotVelocity.y=Velocity.y-ShotSpeed*RotationMatrix[2][1];
    shotVelocity.z=Velocity.z-ShotSpeed*RotationMatrix[2][2];

    shotPosition.x=Position.x+0.047*ShotPos*RotationMatrix[0][0]+
                   0.022*RotationMatrix[1][0]-0.06*RotationMatrix[2][0];
    shotPosition.y=Position.y+0.047*ShotPos*RotationMatrix[0][1]+
                   0.022*RotationMatrix[1][1]-0.06*RotationMatrix[2][1];
    shotPosition.z=Position.z+0.047*ShotPos*RotationMatrix[0][2]+
                   0.022*RotationMatrix[1][2]-0.06*RotationMatrix[2][2];
    ShotPos*=-1.0;

    shotRotation.x=0.0;
    shotRotation.y=0.0;
    shotRotation.z=0.0;

    for (i=0; i<MAX_OBJECTS; i++) {
      if (ServerObjects[i]==NULL) {
        ServerObjects[i]=new ServerObject(shotPosition, shotVelocity,
                                          shotRotation, SHOT_TYPE, 0.01,
                                          0.05, PlayerNum);
        memcpy(ServerObjects[i]->RotationMatrix, InvRotationMatrix,
               sizeof(float [3][3]));
        break;
      }
    }
  }
}


void ServerShip::FireNuke()
{
  struct Point nukeVelocity, nukePosition, nukeRotation;
  int i, j;

  if (AliveTime==-1 && !WarpTime && Nukes) {
    AddSound(PlayerNum, &Position, FireNukeSound);
    for (i=0; i<MAX_PLAYERS; i++) {
      if (i!=PlayerNum) {
        AddSound(i, &Position, NukeAlertSound);
      }
    }

    nukeVelocity.x=Velocity.x-NukeSpeed*RotationMatrix[2][0];
    nukeVelocity.y=Velocity.y-NukeSpeed*RotationMatrix[2][1];
    nukeVelocity.z=Velocity.z-NukeSpeed*RotationMatrix[2][2];

    nukePosition.x=Position.x-0.1*RotationMatrix[1][0]-0.06*RotationMatrix[2][0];
    nukePosition.y=Position.y-0.1*RotationMatrix[1][1]-0.06*RotationMatrix[2][1];
    nukePosition.z=Position.z-0.1*RotationMatrix[1][2]-0.06*RotationMatrix[2][2];

    nukeRotation.x=0.0;
    nukeRotation.y=0.0;
    nukeRotation.z=0.0;

    for (i=0; i<MAX_OBJECTS; i++) {
      if (ServerObjects[i]==NULL) {
        ServerObjects[i]=new ServerObject(nukePosition, nukeVelocity,
                                          nukeRotation, NUKE_TYPE, 0.1,
                                          0.05, PlayerNum);
        memcpy(ServerObjects[i]->RotationMatrix, InvRotationMatrix,
               sizeof(float [3][3]));
        Nukes--;
        break;
      }
    }
  }
}


void ServerShip::DoUpdates()
{
  if (Bot) {
    CalcBotRotation();

    NextBotFireTime++;
    if (NextBotFireTime >= (int)((float)BotFireTime*(1.0/BotRatio))) {
      NextBotFireTime=0;
      FireShot();
    }

    if (BotNukeOdds) {
      if (!(rand()%BotNukeOdds)) {
        FireNuke();
      }
    }

    if (BotShieldOnTime) {
      BotShieldOnTime--;
      if (!BotShieldOnTime) {
        Action.shield=0;
      }
    }
  }

  UpdatePosition();
  UpdateRotation();
  UpdateVelocity();

  if (WarpTime) {
    WarpTime--;
    if (!WarpTime) {
      Velocity.x=0.0;
      Velocity.y=0.0;
      Velocity.z=0.0;
    }
  } else {
    if (Action.shield && Shield && AliveTime==-1) {
      Shield--;
    }

    if (ShakeTime) {
      ShakeTime--;
    }

    if (Action.shooting) {
      FireShot();
      Action.shooting=0;
    }
  }

  if (AliveTime > 0) {
    AliveTime--;
    if (AliveTime==0) {
      if (ServerGameType!=DEATHMATCH && !Bot) {
        Lives--;
        if (Lives) {
          live();
        }
      } else {
        live();
      }
    }
  }
}


int ServerShip::TakeDamage(int DamageAmount)
{
  if (DamageAmount > 0) {
    Armor-=DamageAmount;
    if (Armor <= 0) {
      Armor=0;
      AddSoundAll(&Position, ShipHitSound);
      CreateExplosion(PlayerNum, 1);
      die();
      return(1);
    } else {
      if (!Bot) {
        AddSound(PlayerNum, &Position, CrunchSound);
        ShakeTime+=30;
        if (ShakeTime > 100) {
          ShakeTime=100;
        }
      } else {
        BotShieldOnTime=(int)(BotRatio*(float)BotShieldTime);
        Action.shield=1;
      }
    }
  }
  return(0);
}


void ServerShip::CalcBotRotation()
{
  float Distance;
  int Closest;
  struct Point RelPosition;

  if ((Closest=FindClosestObject(&Distance))!=-1) {
    if (Distance < 0.2) {
      CalcShipRelPosition(ServerObjects[Closest], &RelPosition);

      if (RelPosition.x > 0.0) {
        Rotation.y=-1.0;
      } else {
        Rotation.y=1.0;
      }

      if (RelPosition.y > 0.0) {
        Rotation.x=-1.0;
      } else {
        Rotation.x=1.0;
      }
      return;
    }
  }

  if ((Closest=FindClosestShip(&Distance))!=-1) {
    CalcShipRelPosition(ServerShips[Closest], &RelPosition);

    if (RelPosition.x > 0.0) {
      Rotation.y=1.0;
    } else {
      Rotation.y=-1.0;
    }

    if (RelPosition.y > 0.0) {
      Rotation.x=1.0;
    } else {
      Rotation.x=-1.0;
    }

    if (Distance < 0.3) {
      Rotation.x*=-1.0;
      Rotation.y*=-1.0;
    }
  }
}


int ServerShip::FindClosestShip(float *Distance)
{
  int i;
  float tempDistance;
  int ClosestShip=-1;

  for (i=0; i<MAX_PLAYERS; i++) {
    if (ServerShips[i] && i!=PlayerNum) {
      if (ServerShips[i]->AliveTime==-1) {
        tempDistance=CalcDistance(&Position, &ServerShips[i]->Position);
        if (ClosestShip==-1 || tempDistance < *Distance) {
          ClosestShip=i;
          *Distance=tempDistance-(ObjectSize+ServerShips[i]->ObjectSize);
        }
      }
    }
  }

  return(ClosestShip);
}


int ServerShip::FindClosestObject(float *Distance)
{
  int i;
  float tempDistance1, tempDistance2;
  struct Point tempPosition1, tempPosition2;
  int ClosestObject=-1;

  for (i=0; i<MAX_OBJECTS; i++) {
    if (ServerObjects[i]) {
      if (ServerObjects[i]->ObjectType==ASTEROID_TYPE) {
        tempDistance1=CalcDistance(&Position, &ServerObjects[i]->Position)-(ObjectSize+ServerObjects[i]->ObjectSize);
        tempPosition1.x=Position.x+Velocity.x/100.0;
        tempPosition1.y=Position.y+Velocity.y/100.0;
        tempPosition1.z=Position.z+Velocity.z/100.0;
        tempPosition2.x=ServerObjects[i]->Position.x+ServerObjects[i]->Velocity.x/100.0;
        tempPosition2.y=ServerObjects[i]->Position.y+ServerObjects[i]->Velocity.y/100.0;
        tempPosition2.z=ServerObjects[i]->Position.z+ServerObjects[i]->Velocity.z/100.0;
        tempDistance2=CalcDistance(&tempPosition1, &tempPosition2)-(ObjectSize+ServerObjects[i]->ObjectSize);
        if (ClosestObject==-1 || (tempDistance1 > tempDistance2 && tempDistance1 < *Distance)) {
          ClosestObject=i;
          *Distance=tempDistance1;
        }
      }
    }
  }

  return(ClosestObject);
}


void ServerShip::CalcShipRelPosition(ServerObject *obj, struct Point *relPosition)
{
  struct Point tmpPoint;

  tmpPoint.x=obj->Position.x-Position.x;
  tmpPoint.y=obj->Position.y-Position.y;
  tmpPoint.z=obj->Position.z-Position.z;

  if (tmpPoint.x > WorldWidth) {
    tmpPoint.x-=WorldWidth*2.0;
  }
  if (tmpPoint.y > WorldWidth) {
    tmpPoint.y-=WorldWidth*2.0;
  }
  if (tmpPoint.z > WorldWidth) {
    tmpPoint.z-=WorldWidth*2.0;
  }
  if (tmpPoint.x < -WorldWidth) {
    tmpPoint.x+=WorldWidth*2.0;
  }
  if (tmpPoint.y < -WorldWidth) {
    tmpPoint.y+=WorldWidth*2.0;
  }
  if (tmpPoint.z < -WorldWidth) {
    tmpPoint.z+=WorldWidth*2.0;
  }

  relPosition->x=RotationMatrix[0][0]*tmpPoint.x+RotationMatrix[0][1]*tmpPoint.y+RotationMatrix[0][2]*tmpPoint.z;
  relPosition->y=RotationMatrix[1][0]*tmpPoint.x+RotationMatrix[1][1]*tmpPoint.y+RotationMatrix[1][2]*tmpPoint.z;
  relPosition->z=RotationMatrix[2][0]*tmpPoint.x+RotationMatrix[2][1]*tmpPoint.y+RotationMatrix[2][2]*tmpPoint.z;
}


void ServerShip::IncreaseArmor(int ArmorAmount)
{
  Armor+=ArmorAmount;

  if (Armor > ServerMaxArmor) {
    Armor=ServerMaxArmor;
  }
}


void ServerShip::IncreaseShield(int ShieldAmount)
{
  Shield+=ShieldAmount;

  if (Shield > ServerMaxShield) {
    Shield=ServerMaxShield;
  }
}


void ServerShip::DecreaseShield(int ShieldAmount)
{
  Shield-=ShieldAmount;

  if (Shield < 0) {
    Shield=0;
  }
}


void ServerShip::WarpShip()
{
  if (!WarpTime && Warps && AliveTime==-1) {
    ShakeTime=0;
    Warps--;
    WarpTime=200;
    Velocity.x=-0.2*RotationMatrix[2][0];
    Velocity.y=-0.2*RotationMatrix[2][1];
    Velocity.z=-0.2*RotationMatrix[2][2];
  }
}
