#include <math.h>
#include <wiiuse/wpad.h>
#include <stdio.h>
#include <snd.h>
#include <fcntl.h>
#include "oggplayer.h"
#include "missilecommand.h"

Game::Game()
{
	imgload.load("missile.png");
	imgload.load("silo.png");
	imgload.load("shockwave.png");
  imgload.load("crosshairs.png");
  imgload.load("grass.png");
  imgload.load("silodome.png");
  imgload.load("targetred.png");
  imgload.load("targetyellow.png");
  imgload.load("dashcircle.png");
  imgload.load("black.png");
  imgload.load("blue.png");
  imgload.load("red.png");
  
  pcmload.load("firemissile.raw");
  pcmload.load("siloexplode.raw");
  pcmload.load("shockwaveboom.raw");
}

Game::~Game()
{
  set<Actor*>::iterator it;

  for(it = actors.begin(); it != actors.end(); ++it)
    delete *it;
}


const f32 SILO1X = 100;
const f32 SILO2X = 320;
const f32 SILO3X = 540;
const f32 SILOY = 430;

u32 MAXMISSILES = 10;

void Game::initialize()
{
  set<Actor*>::iterator it;
  set<SiloActor*>::iterator sit;

  for(it = actors.begin(); it != actors.end(); ++it)
    delete *it;
  actors.clear();
  silos.clear();
  missiles.clear();
  playermissiles.clear();
  enemymissiles.clear();
  shockwaves.clear();

  // Create silos
  siloleft = new SiloActor(SILO1X,SILOY);
  siloleft->SetImage(imgload.get("silo.png"));
  actors.insert(siloleft);
  silos.insert(siloleft);
  silodomeleft.SetImage(imgload.get("silodome.png"));
  silodomeleft.SetPosition(siloleft->getX()-32,siloleft->getY()-32);

  silocenter = new SiloActor(SILO2X,SILOY);
  silocenter->SetImage(imgload.get("silo.png"));
  actors.insert(silocenter);
  silos.insert(silocenter);
  silodomecenter.SetImage(imgload.get("silodome.png"));
  silodomecenter.SetPosition(silocenter->getX()-32,silocenter->getY()-32);

  siloright = new SiloActor(SILO3X,SILOY);
  siloright->SetImage(imgload.get("silo.png"));
  actors.insert(siloright);
  silos.insert(siloright);
  silodomeright.SetImage(imgload.get("silodome.png"));
  silodomeright.SetPosition(siloright->getX()-32,siloright->getY()-32);
  
  for(sit = silos.begin(); sit != silos.end(); ++sit) {
    SiloActor *silo = *sit;
    silo->healthbar.SetImage(imgload.get("red.png"));
    silo->healthbar.SetPosition(silo->getX()-2,silo->getY()+20);
    silo->healthbar.SetStretchHeight(2);
    silo->ammobar.SetImage(imgload.get("blue.png"));
    silo->ammobar.SetPosition(silo->getX()-2,silo->getY()+30);
    silo->ammobar.SetStretchHeight(2);
  }

  // Create grass
  grass.SetImage(imgload.get("grass.png"));
  grass.SetPosition(0,SILOY);
  
  // Create crosshairs
  crosshairs.SetImage(imgload.get("crosshairs.png"));
}

void Game::activate()
{
  set<SiloActor*>::iterator it;
  for(it = silos.begin(); it != silos.end(); ++it)
    (*it)->activate();
}

void Game::update()
{
  set<Actor*>::iterator it;
  set<SiloActor*>::iterator sit;
  set<MissileActor*>::iterator mit;
  set<ShockwaveActor*>::iterator swit;
  set<MissileActor*> mtoerase;
  set<ShockwaveActor*> stoerase;

  // Input
  struct ir_t ir;
  WPAD_IR(0,&ir);
  px = ir.sx;
  py = ir.sy;

  // Fire player missile
  if(WPAD_ButtonsDown(0) & WPAD_BUTTON_B ||
     WPAD_Data(0)->exp.type == EXP_NUNCHUK &&
     WPAD_ButtonsDown(0) & WPAD_NUNCHUK_BUTTON_Z) {
    SiloActor *firingSilo = NULL;

    // Use the closest silo
    for(sit = silos.begin(); sit != silos.end(); ++sit) {
      SiloActor *silo = *sit;
      if(!silo->canLaunch())
        continue;
      if(!firingSilo || abs(silo->getX()-px) < abs(firingSilo->getX()-px))
        firingSilo = silo;
    }

    // Unless they're holding a D-pad direction to select a silo
    if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_LEFT && siloleft->canLaunch())
      firingSilo = siloleft;
    else if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_UP && silocenter->canLaunch())
      firingSilo = silocenter;
    else if(WPAD_ButtonsHeld(0) & WPAD_BUTTON_RIGHT && siloright->canLaunch())
      firingSilo = siloright;
    else 

    // Or holding the nunchuck stick in a direction
    if(WPAD_Data(0)->exp.type == EXP_NUNCHUK &&
       WPAD_Data(0)->exp.nunchuk.js.mag > .5) {
      f32 ang = WPAD_Data(0)->exp.nunchuk.js.ang;
      if(ang > 225 && ang < 315 && siloleft->canLaunch())
        firingSilo = siloleft;
      else if((ang >= 315 || ang < 45) && silocenter->canLaunch())
        firingSilo = silocenter;
      else if(ang >= 45 && ang < 135 && siloright->canLaunch())
        firingSilo = siloright;
    }
    
    if(firingSilo) {
      PcmData *sound = pcmload.get("firemissile.raw");
      if(sound)
        sound->play();
      firingSilo->fireMissile();
      MissileActor *missile = new MissileActor(firingSilo->getX(),SILOY,ir.sx,ir.sy,MissileActor::PLAYER);
      actors.insert(missile);
      missiles.insert(missile);
      playermissiles.insert(missile);
      missile->SetImage(imgload.get("missile.png"));
      missile->activate();
      missile->missileTarget.SetPosition(px-8, py-8);
      missile->missileTarget.SetZoom(2.0);
      missile->missileLine.SetImage(imgload.get("blue.png"));
      missile->missileLine.SetRotation(missile->getRotation()/2.0);
      missile->missileLine.SetStretchWidth(.5);
    }
  }

  // Update everything
  for(it = actors.begin(); it != actors.end(); ++it)
    (*it)->update();

  // Set the silo aim
  for(sit = silos.begin(); sit != silos.end(); ++sit)
    (*sit)->aimAt(px, py);

  // Missile-shockwave collisions
  for(swit = shockwaves.begin(); swit != shockwaves.end(); ++swit) {
    f32 mx,my,sx,sy,r;
    ShockwaveActor *shock = *swit;
    sx = shock->getX();
    sy = shock->getY();
    r = shock->getR();
    if(sy < 60)
      continue;
    for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
      MissileActor *missile = *mit;
      mx = missile->getX();
      my = missile->getY();
      if((mx-sx)*(mx-sx)+(my-sy)*(my-sy) < r*r)
        missile->explode();
    }
  }
  
  // Missile-base collisions
  for(sit = silos.begin(); sit != silos.end(); ++sit) {
    f32 mx,my,sx,sy,r;
    SiloActor *silo = *sit;
    sx = silo->getX();
    sy = silo->getY();
    r = 32;
    for(mit = enemymissiles.begin(); mit != enemymissiles.end(); ++mit) {
      MissileActor *missile = *mit;
      mx = missile->getX();
      my = missile->getY();
      if((mx-sx)*(mx-sx)+(my-sy)*(my-sy) < r*r) {
        PcmData *sound = pcmload.get("siloexplode.raw");
        if(sound)
          sound->play();
        missile->explode();
        silo->directHit();
      }
    }
  }

  // Base-shockwave collisions
  for(sit = silos.begin(); sit != silos.end(); ++sit) {
    f32 swx,swy,swr,sx,sy,sr;
    SiloActor *silo = *sit;
    sx = silo->getX();
    sy = silo->getY();
    sr = 32;
    for(swit = shockwaves.begin(); swit != shockwaves.end(); ++swit) {
      ShockwaveActor *shock = *swit;
      swx = shock->getX();
      swy = shock->getY();
      swr = shock->getR();
      if((swx-sx)*(swx-sx)+(swy-sy)*(swy-sy) < (sr+swr)*(sr+swr))
        silo->splashHit();
    }
  }

  // Missile cleanup
  for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
    MissileActor *missile = *mit;
    if(missile->isExploded()) {
      PcmData *sound = pcmload.get("shockwaveboom.raw");
      if(sound)
        sound->play();
      ShockwaveActor *shock = new ShockwaveActor(missile->getX(), missile->getY());
      shockwaves.insert(shock);
      actors.insert(shock);
      shock->SetImage(imgload.get("shockwave.png"));
      shock->activate();
      mtoerase.insert(missile);
    } else if(!missile->isActive()) {
      mtoerase.insert(missile);
    }
  }
  for(mit = mtoerase.begin(); mit != mtoerase.end(); ++mit) {
    MissileActor *missile = *mit;
    playermissiles.erase(missile);
    enemymissiles.erase(missile);
    missiles.erase(missile);
    actors.erase(missile);
    delete missile;
  }

  // Shockwave cleanup
  for(swit = shockwaves.begin(); swit != shockwaves.end(); ++swit) {
    ShockwaveActor *shock = *swit;
    if(shock->isFinished())
      stoerase.insert(shock);
  }
  for(swit = stoerase.begin(); swit != stoerase.end(); ++swit) {
    ShockwaveActor *shock = *swit;
    shockwaves.erase(shock);
    actors.erase(shock);
    delete shock;
  }

  // Launch enemy missiles
  while(enemymissiles.size() < MAXMISSILES) {
    f32 xs = rand() % 640;
    f32 xf = rand() % 640;
    bool dosplit = rand() % 10 == 0; // 1 in 10
    int splitafter = -1;
    if(dosplit)
      splitafter = rand() % 200 + 160;
    MissileActor *missile = new MissileActor(xs, -60, xf, SILOY, MissileActor::ENEMY, splitafter);
    missiles.insert(missile);
    enemymissiles.insert(missile);
    actors.insert(missile);
    missile->SetImage(imgload.get("missile.png"));
    missile->activate();
    missile->missileCircle.SetImage(imgload.get("dashcircle.png"));
    missile->missileCircle.SetZoom(0.7);
    missile->missileLine.SetImage(imgload.get("red.png"));
    missile->missileLine.SetRotation(missile->getRotation()/2.0);
    missile->missileLine.SetStretchWidth(.5);
  }

  // Split missiles
  for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
    MissileActor *missile = *mit;
    int splittime = missile->getSplitTime();
    int t = missile->getT();
    if(splittime != -1 && t >= splittime &&
        !missile->isSplit() && missile->isActive() && !missile->isExploded()) {
      int splitcount = rand() % 4 + 2; // 2 to 5 submissiles
      missile->split(splitcount);
      for(int i = 0; i < splitcount; i++) {
        f32 targetx = rand() % 640;
        MissileActor::Type owner = missile->getOwner();
        MissileActor *splitmissile = new MissileActor(missile->getX(), missile->getY(),
            targetx, SILOY, owner);
        actors.insert(splitmissile);
        missiles.insert(splitmissile);
        if(owner == MissileActor::ENEMY)
          enemymissiles.insert(splitmissile);
        else
          playermissiles.insert(splitmissile);
        splitmissile->setSplitParent(missile);
        splitmissile->SetImage(imgload.get("missile.png"));
        splitmissile->activate();
        splitmissile->missileCircle.SetImage(imgload.get("dashcircle.png"));
        splitmissile->missileCircle.SetZoom(0.7);
        if(owner == MissileActor::ENEMY)
          splitmissile->missileLine.SetImage(imgload.get("red.png"));
        else
          splitmissile->missileLine.SetImage(imgload.get("blue.png"));
        splitmissile->missileLine.SetRotation(splitmissile->getRotation()/2.0);
        splitmissile->missileLine.SetStretchWidth(.5);
      }
    }
  }
}

void Game::draw()
{
  set<MissileActor*>::iterator mit;
  set<SiloActor*>::iterator sit;
  set<ShockwaveActor*>::iterator swit;
  
  for(swit = shockwaves.begin(); swit != shockwaves.end(); ++swit) {
    ShockwaveActor *shock = *swit;
    shock->SetPosition(shock->getX()-32, shock->getY()-32);
    shock->SetZoom(shock->getR()/32.0);
    shock->SetTransparency(128);
    shock->Draw();
  }
  
  for(mit = playermissiles.begin(); mit != playermissiles.end(); ++mit) {
    MissileActor *missile = *mit;
    if(missile->getT()/3 % 2)
      missile->missileTarget.SetImage(imgload.get("targetred.png"));
    else
      missile->missileTarget.SetImage(imgload.get("targetyellow.png"));
    missile->missileTarget.Draw();
  }
  
  for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
    MissileActor *missile = *mit;
    if(!missile->isSplit() && missile->getOwner() == MissileActor::ENEMY) {
      missile->missileCircle.SetPosition(missile->getX()-32, missile->getY()-32);
      missile->missileCircle.SetRotation(missile->getT()*1.5);
      missile->missileCircle.Draw();
    }
    f32 sx = missile->getStartX();
    f32 sy = missile->getStartY();
    f32 mx = missile->getX();
    f32 my = missile->getY();
    f32 len = sqrt((sx-mx)*(sx-mx)+(sy-my)*(sy-my));
    f32 linex = (mx+sx)/2;
    f32 liney = (my+sy)/2;
    missile->missileLine.SetPosition(linex-2,liney-2);
    missile->missileLine.SetStretchHeight(len/4.0);
    missile->missileLine.Draw();
  }

  for(mit = missiles.begin(); mit != missiles.end(); ++mit) {
    MissileActor *missile = *mit;
    if(!missile->isSplit()) {
      missile->SetPosition(missile->getX()-2, missile->getY()-4);
      missile->SetRotation(missile->getRotation()/2.0);
      missile->Draw();
    }
  }
  
  for(sit = silos.begin(); sit != silos.end(); ++sit) {
    SiloActor *silo = *sit;
    silo->SetPosition(silo->getX()-8, silo->getY()-32);
    silo->SetRotation(silo->getRotation()/2.0);
    silo->SetZoom(1.5);
    silo->Draw();
  }

  silodomeleft.Draw();
  silodomecenter.Draw();
  silodomeright.Draw();

  grass.Draw();  
  
  for(sit = silos.begin(); sit != silos.end(); ++sit) {
    SiloActor *silo = *sit;
    silo->healthbar.SetStretchWidth(silo->getHealthPct()*30.0);
    silo->healthbar.Draw();
    silo->ammobar.SetStretchWidth(silo->getAmmoPct()*30.0);
    silo->ammobar.Draw();
  }

  static int rot = 0;
  crosshairs.SetPosition(px-8, py-8);
  crosshairs.SetRotation(rot);
  crosshairs.SetZoom(5.0);
  crosshairs.Draw();
  rot+=3;
}
