#include "allegro.h"

#include "UnitList.h"
#include "Unit.h"
#include "PlayerUnit.h"
#include "InputObj.h"
#include "Round.h"
#include "MusicSound.h"
#include "UnitCreate.h"
#include "UnitTemplate.h"
#include "Loader.h"
#include "gameConstants.h"
#include "tasmMath.h"
#include "Campaign.h"

#include <vector>
#include <string>
using namespace std;

UnitList :: UnitList ()
{
  uC = NULL;
  units = new vector <Unit *>;
}

void UnitList :: allUnitsMove (InputObj * iO, MusicSound * ms, Loader * lder)
{
  //tells all of the units in the queue to move (figure out what they want to do this moment.

  //first, the player collects juice gained from the previous round.
  PlayerUnit * p1 = NULL;
  vector <Unit *> * playerFinder = search (searchForPCs, 0);

  if (playerFinder -> size () > 0)
    p1 = dynamic_cast <PlayerUnit *> (playerFinder -> at (0));

  giveJuiceToPlayer (p1);

  vector <Unit *> ::iterator the_iterator;

  the_iterator = units -> begin ();
  while(the_iterator < units -> end()) {
    (*the_iterator) -> decideAction ();
    the_iterator ++;
  }

  the_iterator = units -> begin ();
  while(the_iterator < units -> end()) {
    (*the_iterator) -> dealDamage ();
    the_iterator ++;
  }

  the_iterator = units -> begin ();
  while(the_iterator < units -> end()) {
     (*the_iterator) -> takeDamage ();
    the_iterator ++;
  }

  the_iterator = units -> begin ();
  while(the_iterator < units -> end()) {
    if ((*the_iterator) -> getRemoveThisUnit ())
      units -> erase (the_iterator);
    the_iterator ++;
  }
}

void UnitList :: makeUnits (Loader * lder, Round * rnd, InputObj * iO)
{
  //makes units by looking at the unitCreates.
  //step 1: decrement all unitCreate times and create any and all units 

  Unit * un = NULL;
  PlayerUnit * pUN = NULL;
  UnitTemplate * utt = NULL;

  fixed pX = itofix (0);
  fixed pY = itofix (0);
  fixed dA = itofix (0);
  fixed dS = ftofix (1.0);

  int sub = subUnitNone;
  bool timeCheck = true, enemyCheck = true;

  vector <UnitCreate *> ::iterator the_iterator;
  vector <Unit *> * enemies = search (searchByAffiliation, affiliationEnemy);
  int numEnemies = enemies -> size ();;
  the_iterator = uC -> begin ();

  while(the_iterator < uC -> end()) {
    (*the_iterator) -> timeToCreate--;

    timeCheck = true;
    enemyCheck = true;

    if ((*the_iterator) -> unitCreateRule & unitCreateRulesAtTime && (*the_iterator) -> timeToCreate > 0)
      timeCheck = false;

    if ((*the_iterator) -> unitCreateRule & unitCreateRulesEnemies && numEnemies >  (*the_iterator) -> enemiesToCreate)
      enemyCheck = false;

    if (timeCheck && enemyCheck)
      {
	for (int i = 0; i < (*the_iterator) -> numToCreate; i ++)
	  {
	    pX = itofix (0);
	    pY = itofix (0);
	    dA = itofix (0);
	    dS = ftofix (1.0);
	    sub = (*the_iterator) -> sub;

	    //position the unit accordingly
	    utt = lder -> getUnitTemplate (*((*the_iterator) -> unitType));
	    positionUnitOnSide (pX, pY, dA, (*the_iterator), utt, rnd);
	    
	    //add this unit to the list
	    if (utt != NULL && !utt -> isThePC)
	      {
		un = new Unit (this, utt, (*the_iterator) -> own, pX, pY, dA, dS, sub);
		un -> setRound (rndInProgress);
		units -> push_back (un);
	      }
	    else if (utt != NULL && utt -> isThePC)
	      {
		pUN = new PlayerUnit (this, utt, (*the_iterator) -> own, pX, pY, dA, dS, sub);
		pUN -> setInputObj (iO);
		pUN -> setRound (rndInProgress);
		units -> push_back (static_cast <Unit *> (pUN));
	      }
	  }

	//remove this unitCreate
	uC -> erase (the_iterator);
      }

    the_iterator++;
  }
}

void UnitList :: positionUnitOnSide (fixed & pX, fixed & pY, fixed & ang, UnitCreate * uuC, UnitTemplate * utt, Round * rnd)
{
  //places the unit on the correct side of the arena, reading the necessary data from uuC.
  //pX and pY are altered.

  fixed aX = rnd -> getArenaSizeX ();
  fixed aY = rnd -> getArenaSizeY ();

  fixed temp1;
  fixed temp2;
  fixed temp3;

  switch (uuC -> side)
    {
      //1 - 8 are counter-clockwise. 
    case (1): //upper-left corner
      temp1 = aX / 4;
      temp2 = aY / 4;
      temp3 = itofix (rand () % fixtoi (temp1 + temp2));
      if (temp3 < temp2)
	{ pX = 1; pY = temp3;}
      else
	{ pY = 1; pX = temp3 - temp2;}
      break;
    case (2): //top
      temp1 = aX / 4;
      temp2 = aX / 2;
      temp3 = itofix (rand () % fixtoi (temp2));
      pY = 1;
      pX = temp1 + temp3;
      break;
    case (3): //upper-right corner
      temp1 = aX / 4;
      temp2 = aY / 4;
      temp3 = itofix (rand () % fixtoi (temp1 + temp2));
      if (temp3 < temp1)
	{ pY = 1; pX = (3 * temp1) + temp3;}
      else
	{ pY = temp3 - temp1; pX = aX - 1;}
      break;
    case (4): //right
      temp1 = aY / 4;
      temp2 = aY / 2;
      temp3 = itofix (rand () % fixtoi (temp2));
      pX = aX - 1;
      pY = temp1 + temp3;
      break;
    case (5): //lower-right corner
      temp1 = aX / 4;
      temp2 = aY / 4;
      temp3 = itofix (rand () % fixtoi (temp1 + temp2));
      if (temp3 < temp1)
	{ pY = aY - 1; pX = (3 * temp1) + temp3;}
      else
	{ pY = (3 * temp2) + temp3 - temp1; pX = aX - 1;}
      break;
    case (6): //bottom
      temp1 = aX / 4;
      temp2 = aX / 2;
      temp3 = itofix (rand () % fixtoi (temp2));
      pY = aY - 1;
      pX = temp1 + temp3;
      break;
    case (7): //bottom-left corner
      temp1 = aX / 4;
      temp2 = aY / 4;
      temp3 = itofix (rand () % fixtoi (temp1 + temp2));
      if (temp3 < temp2)
	{ pX = 1; pY = temp3 + (3 * temp2);}
      else
	{ pY = aY - 1; pX = temp3 - temp2;}
      break;
    case (8): //left
      temp1 = aY / 4;
      temp2 = aY / 2;
      temp3 = itofix (rand () % fixtoi (temp2));
      pX = 1;
      pY = temp1 + temp3;
      break;
    case (9):
      //9 means exactly in the center
      pX = aX/2;
      pY = aY/2;
      break;
    case (10):
      //exact positioning.
      pX = uuC -> posX;
      pY = uuC -> posY;
      break;
    case (11):
      //totally random!!
      pX = itofix (rand () % fixtoi (aX));
      pY = itofix (rand () % fixtoi (aY));
      break;
    default:
      break;
    }

  //angle and stuff
  if (uuC -> side == 10)
    ang = uuC -> getAngle ();
  else if (uuC -> side == 11)
    {
      //random!
      ang = itofix (rand () % 256);
    }
  else
    {
      //aim them so they point at the middle!
      ang = calcAngle (pX, pY, aX/2, aY/2, ang);
    }
}

void UnitList :: drawUnits (BITMAP * buffer, MusicSound * ms, bool gamePaused)
{
  //draws the arena all of the units on buffer.
  //also ends the round for all the units, if the game is not paused.

  //eventually calls the unit
  vector <Unit *> :: iterator the_iterator;

  the_iterator = units -> begin ();
  while(the_iterator < units -> end()) {
    (*the_iterator) -> draw (buffer, ms, gamePaused);
    the_iterator++;
  }

  if (gamePaused) return;

  the_iterator = units -> begin ();
  while(the_iterator < units -> end()) {
    (*the_iterator) -> endOfRound ();
    the_iterator ++;
  }
}

void UnitList :: setupRound (Round * rnd)
{
  //Reads data from rnd.
  //copies the unitCreates from it, and appends the player unit at the end.
  //assumes the round has been loaded already

  uC = rnd -> getUnitsToAppear ();

  rndInProgress = rnd;
  return;
}

void UnitList :: clear ()
{
  units -> clear ();
  if (uC != NULL)
    uC -> clear ();
}

int UnitList :: getPlayerScore ()
{
  //finds the player and returns the score.
  //returns 0 otherwise.

  PlayerUnit * p1 = NULL;

  vector <Unit*> * players = search (searchForPCs, 0);
  if (players -> size () <= 0) return 0;
  p1 = static_cast <PlayerUnit *> (players -> at (0));

  return p1 -> getScore ();
}

void UnitList :: addUnitCreate (UnitCreate * uuuc)
{
  uC -> push_back (uuuc);
}

vector <Unit *> * UnitList :: search (int criterion, int parameter)
{
  //will search through the current Units for the parameter and return a vector of them.

  vector <Unit *> * ans = new vector <Unit *> (0);
  bool addUnitToAns = false;

  vector <Unit *> :: iterator the_iterator;
  the_iterator = units -> begin ();
  while(the_iterator < units -> end()) {
    addUnitToAns = false;

    if (criterion == searchByAffiliation && (*the_iterator) -> getAffiliation () == parameter)
      addUnitToAns = true;

    if (criterion == searchForPCs && dynamic_cast <PlayerUnit *> (*the_iterator) != NULL)
      {
	addUnitToAns = true;
      }

    if (addUnitToAns)
      ans -> push_back (*the_iterator);
    the_iterator++;
  }

  return ans;
}

bool UnitList :: thisUnitExists (Unit * uuuuun)
{
  //looks through vector for the unit with the same pointer.
  //returns false if it cannot be found.

  vector <Unit *> :: iterator the_iterator;
  the_iterator = units -> begin ();
  while(the_iterator < units -> end()) {
    if ((*the_iterator) == uuuuun)
      return true;
    the_iterator++;
  }
  return false;
}

vector <Unit *> ::iterator UnitList :: beginOfList ()
{
  //returns an iterator to the front of the unitlist.
  return units -> begin ();
}

vector <Unit *> ::iterator UnitList :: endOfList ()
{
  return units -> end ();
}

void UnitList :: giveJuiceToPlayer (PlayerUnit * p1)
{
  //collects the juice from the enemies and gives it to the player.
  int totalJuice = 0;

  if (p1 == NULL)
    return;

  if (p1 -> getCharAnim () == charAnimDead)
    return;

  //OK, the player can get juice.

  vector <Unit *> :: iterator iter;
  iter = units -> begin ();
  while(iter < units -> end()) {
    (*iter) -> giveJuiceToPlayer (totalJuice);
    iter++;
  }

  p1 -> addJuiceGained (totalJuice);
  p1 -> resetJuiceGained ();
}

bool UnitList :: anyRemainingAffiliation (int affil, Loader * lder)
{
  //look through units and unitCreates, searching for affil
  vector<Unit *>::iterator iter1;
  vector<UnitCreate *>::iterator iter2;
  UnitTemplate * utt = NULL;

  iter1 = units -> begin();
  iter2 = uC -> begin();

  while(iter1 < units -> end()) {
    if ((*iter1) -> getAffiliation () == affil)
      return true;
    iter1++;
  }

  while(iter2 < uC -> end()) {
    utt = lder -> getUnitTemplate (*((*iter2) -> unitType));

    if (utt != NULL && utt -> affiliation == affil)
      return true;
    iter2++;
  }

  return false;
}

UnitList :: ~UnitList ()
{
}
