#include "allegro.h"

#include "PlayerUnit.h"
#include "InputObj.h"
#include "Unit.h"
#include "AddUnitTemplate.h"
#include "gameConstants.h"
#include "tasmMath.h"

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

PlayerUnit :: PlayerUnit (UnitList * u, UnitTemplate * uts, Unit * ownerUnit, fixed pX, fixed pY, fixed dA, fixed dS, int su) : Unit (u, uts, ownerUnit, pX, pY, dA, dS, su)
{
  hitPoints = maxHP / 3;

  chainTimer = 0;
  chainStep = 0;

  score = 0;

  hpAttack1 = 0;
  hpAttack2 = 0;
  hpAttack4 = 0;

  juiceGained = 0;

  vector <AddUnitTemplate *> * auts = NULL;

  auts = getAddedUnits (1);
  for (int i = 0; i < int(auts -> size ()); i ++)
    hpAttack1 += auts -> at (i) -> costOfAttack;

  auts = getAddedUnits (2);
  for (int i = 0; i < int(auts -> size ()); i ++)
    hpAttack2 += auts -> at (i) -> costOfAttack;

  auts = getAddedUnits (4);
  for (int i = 0; i < int(auts -> size ()); i ++)
    hpAttack4 += auts -> at (i) -> costOfAttack;

  goodStatusMax = new vector <int> (numGoodStatus, 1);
  badStatusMax = new vector <int> (numBadStatus, 1);
}

void PlayerUnit :: draw (BITMAP * buffer, MusicSound * ms, bool gamePaused)
{
  //first, draw the cursor.
  
  BITMAP * pic = NULL;

  pic = ut -> vecPics -> at (charAnimAim) -> at (0);

  //(just draw the "aimUnit" in position)
  //draw aimUnit first
  if (pic != NULL)
    pivot_scaled_sprite (buffer, 
			 pic,
			 fixtoi (posX), fixtoi (posY),
			 pic -> w / 2,
			 pic -> h / 2,
			 drawAngle, drawScale);
  
  Unit :: draw (buffer, ms, gamePaused);
}

void PlayerUnit :: decideAction ()
{
  //read the input object, make decisions based on it.
  int walkVert = 0;
  int walkHoriz = 0;

  //chainTimer && chainStep
  if (chainTimer > 0)
    {
      chainTimer --;
      if (chainTimer <= 0)
	{ chainTimer = 0; chainStep = 0;}
    }

  //check for statuses here
  if (charAnim == charAnimDead)
    return;

  if (charAnim != charAnimStun)
    {
      if (iO -> getKeyPressed () -> at (keyUp))
	{walk (true); walkVert--;}
      if (iO -> getKeyPressed () -> at (keyDown))
	{walk (true); walkVert++;}
      if (iO -> getKeyPressed () -> at (keyLeft))
	{walk (true); walkHoriz--;}
      if (iO -> getKeyPressed () -> at (keyRight))
	{walk (true); walkHoriz++;}

      if (walkVert == -1 && walkHoriz == 0)
	setWalkAngle (itofix (0));
      if (walkVert == 1 && walkHoriz == 0)
	setWalkAngle (itofix (128));
      
      if (walkVert == 0 && walkHoriz == 1)
	setWalkAngle (itofix (64));
      if (walkVert == 0 && walkHoriz == -1)
	setWalkAngle (itofix (192));
      
      if (walkVert == -1 && walkHoriz == 1)
	setWalkAngle (itofix (32));
      if (walkVert == -1 && walkHoriz == -1)
	setWalkAngle (itofix (224));
      if (walkVert == 1 && walkHoriz == 1)
    setWalkAngle (itofix (96));
      if (walkVert == 1 && walkHoriz == -1)
	setWalkAngle (itofix (160));
    }

  int i = 0, index, attackToCheck, suT = subUnitNone;
  bool inputCheck = false;
  bool stopChain = false;
  bool chainCooldown = false;
  vector <AddUnitTemplate *> * auts = new vector <AddUnitTemplate *> (0);

  if (addedUnits -> size () < 3)
    index = addedUnits -> size ();
  else
    index = 3;

  for (i = 0; i < 3; i ++) 
    {
      inputCheck = false;

      auts -> clear ();
      if (i == 0)
	attackToCheck = 1;
      else if (i == 1)
	attackToCheck = 2;
      else
	attackToCheck = 4;

      auts = getAddedUnits (attackToCheck);

      if (auts -> at (0) -> clickToUse && iO -> getMouseClicked () -> at (i))
	inputCheck = true;
      
      if (!auts -> at (0) -> clickToUse && iO -> getMousePressed () -> at (i))
	inputCheck = true;

      if (attackToCheck == 4 && auts -> at (0) -> clickToUse && iO -> getKeyClicked () -> at (keyHyper))
	inputCheck = true;

      if (attackToCheck == 4 && !auts -> at (0) -> clickToUse && iO -> getKeyPressed () -> at (keyHyper))
	inputCheck = true;
      
      if (inputCheck && (charAnim != charAnimStun || ((getUnitPropertyPlayer () & unitPropertyPlayerClearStunFor3rdAttack) && attackToCheck == 4))  )
	{
	  //use playerchains to enhance the next attack

	  if (canAddUnit (auts))
	  {
	    if (attackToCheck == 1)
	      {
		if (getUnitPropertyPlayer () & unitPropertyPlayerChainAttack1)
		  {chainStep ++; stopChain = false;}
		else
		  {chainStep = 0; chainTimer = 1; stopChain = true;}
	      }

	    if (attackToCheck == 2)
	      {
		if (getUnitPropertyPlayer () & unitPropertyPlayerChainAttack2)
		  {chainStep ++;}
		else
		  {chainStep = 0; chainTimer = 1;}
		stopChain = true;

		if (chainStep >= 2)
		  chainCooldown = true;
	      }

	    if (attackToCheck >= 3)
	      {
		if (getUnitPropertyPlayer () & unitPropertyPlayerChainAttack4)
		  {chainStep ++;}
		else
		  {chainStep = 0; chainTimer = 1;}
		stopChain = true;
	      }

	    if (chainStep <= 1)
	      {
		suT = subUnitNone;
		chainTimer = 30;
	      }
	    else if (chainStep == 2)
	      {
		suT = subUnitBig;
		chainTimer = 30;
	      }
	    else if (chainStep >= 3)
	      {
		suT = subUnitBiggest;
		stopChain = true;
		chainCooldown = true;
	      }

	    if (stopChain)
	      { chainStep = 0; chainTimer = 0;}

	    //clear stun if it's the 3rd attack
	    if ((getUnitPropertyPlayer () & unitPropertyPlayerClearStunFor3rdAttack) && attackToCheck == 4)
	      clearBadStatus (badStatusStun);

	    addUnit (auts, chainCooldown, suT);
	  }
	}
    }
  //so, about aiming...
  aim ();
}

void PlayerUnit :: reduceScore (int scoreRedux)
{
  score -= scoreRedux;
}

void PlayerUnit :: takeDamage ()
{
  Unit :: takeDamage ();

  //check for player-specific rules (like granting invincibility)
}

void PlayerUnit :: addJuiceGained (int juice)
{
  //adds juice to juiceGained.
  int juiceToGain = juice;

  if (getGoodStatus () -> at (goodStatusMoreJuice) > 0)
    juiceToGain *= 5;

  if (getBadStatus () -> at (badStatusNoJuice) > 0)
    juiceToGain = 0;

  if (getGoodStatus () -> at (goodStatusMoreJuice) > 0 && getBadStatus () -> at (badStatusNoJuice) > 0)
    juiceToGain = juice;

  juiceGained += juiceToGain;
  score += juice;
}

int PlayerUnit :: getScore ()
{ return score; }

int PlayerUnit :: getJuiceGained ()
{
  return juiceGained;
}

void PlayerUnit :: resetJuiceGained ()
{
  //called at the start of the round (so the player doesn't regenerate...)
  hitPoints += juiceGained;
  juiceGained = 0;
}

void PlayerUnit :: setInputObj (InputObj * i)
{
  iO = i;
}

void PlayerUnit :: aim ()
{
  int cursorX = 0, cursorY = 0;  
  fixed micX = itofix (0), micY = itofix (0), micDist = itofix (0), micAngle = itofix (0), turnDist = itofix (0);
  int ct = iO -> getMouseControlThreshold ();

  //change angle to point at mouse. The arena's scaling is taken into account.

  iO -> getMouseMickeys (cursorX, cursorY);

  //transformation for screen vs cartesian coordinates
  micX = itofix (-1 * cursorX);
  micY = itofix (cursorY);
  micDist = fixhypot (micX, micY);

  if (micDist > itofix (1))
    {
      //get angle
      if (cursorY == 0 && cursorX < 0)
	micAngle = itofix (-64);
      else if (cursorY == 0  && cursorX > 0)
	micAngle = itofix (64);
      else
	micAngle = fixatan (fixdiv (micX, micY));
      
      //correct the atan response: answers will only range from -64 to 64, crossing 0 (point up only)...need to rotate answer if it should be pointing down

      if (micY > itofix (0))
	micAngle -= itofix (128);
	      
      //keeps micAngle in the range of -256 to 256
      micAngle = micAngle ^ 256;
      
      //combine control stlye 1&2
      //use fast mouse jerks to quickly get the angle
      //move slowly to fine tune the angle

      if (micDist > itofix (ct))
	{
	  //Idea #1: direct - make the cursor point in the direction the mouse moves. Will need to control the threshold value so when user releases mouse, doesn't turn around (might just want only fullscreen for this, since the mouse WILL scroll outside of the window.) Also, it's inaccurate.
	  setFaceAngle (micAngle);
	}
      else
	{
	  //Idea #2: rotate - the mouse direction acts as a rotation force to 'rotate' the cursor around. It may be unintuitive for large rotations.
	  //ok, compare the angles
	  turnDist = angleDist (micAngle, drawAngle);
	  //ok rotate based on the difference between the angles
	  setFaceAngle (drawAngle + turnDist / 10);
	}
    }
}

int PlayerUnit :: getHPCostOfAttack (int attNum)
{
  if (attNum == 1)
    return hpAttack1;
  else if (attNum == 2)
    return hpAttack2;
  else
    return hpAttack4;
}

void PlayerUnit :: setHPCostOfAttack (int attNum, int cost)
{
  if (attNum == 1)
    hpAttack1 = cost;
  else if (attNum == 2)
    hpAttack2 = cost;
  else if (attNum == 3)
    hpAttack4 = cost;
}

void PlayerUnit :: changeBadStatusMax (int index, int val)
{ badStatusMax -> at (index) = val;}

void PlayerUnit :: changeGoodStatusMax (int index, int val)
{ goodStatusMax -> at (index) = val;}

vector <int> * PlayerUnit :: getGoodStatusMax ()
{ return goodStatusMax;}

vector <int> * PlayerUnit :: getBadStatusMax ()
{ return badStatusMax;}

PlayerUnit :: ~PlayerUnit ()
{
}
