#include "PlayerAbility.h"
#include "Ability.h"
#include <stdint.h>
#include "Player.h"

#include "../util/fileUtils.h"
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

#include <vector>
using namespace std;

PlayerAbility :: PlayerAbility ()
{
  buffID = NULL_INDEX;
  rechargeTime=8;
  minLevel.resize (PLAYER_CLASS_NUMBER + 1, 255);
  setDescription("NO DESCRIPTION");
  setAttackPath("NULL_INDEX");
  setButtonIcon("NULL_INDEX");
  clearDuringBattleData();
}

PlayerAbility :: ~PlayerAbility ()
{
}

bool PlayerAbility :: loadPlayerAbility (const string & index)
{
  //load the player ability.
  if (index.empty () || index == "NULL_INDEX")
    return false;

  xmlDocPtr doc;
  xmlXPathContextPtr xpathCtx;

  string xpathStart="/AbilityManifest/PlayerAbility[@ID='";
  xpathStart+=index;
  xpathStart+="']";

  if (!prepareXMLFile (doc, xpathCtx, "resources/game/AbilityManifest.xml", xpathStart))
    {
      writeToErrorLog ("Failed to load PlayerAbility "+index, 1);
      return false;
    }

  bool tempBool = xPathExists (doc, xpathCtx, xpathStart, "", "");

  if (tempBool)
    tempBool = loadPlayerAbilityHelper (doc, xpathCtx, xpathStart);

  xmlXPathFreeContext(xpathCtx);
  xmlFreeDoc(doc);

  if (!tempBool)
    {
      return false;
    }

  setAbilityName (index);
  setEquipped (false);
  return true;
}

uint8_t PlayerAbility :: getBuffID ()
{
  return buffID;
}

uint8_t PlayerAbility :: getMinLevel (uint8_t clas)
{
  if (clas >= minLevel.size ())
    return minLevel.at (PLAYER_CLASS_NULL);

  return minLevel.at (clas);
}


//Reads status about a given ability from an XML file and loads it into this object.
//This returns true upon completion.
//Everything has a default value it will load if the element is not found.
bool PlayerAbility :: loadPlayerAbilityHelper (xmlDocPtr doc, xmlXPathContextPtr xpathCtx, const string & xpath)
{
  xmlChar * element;
  int temp;
  string gab;
  bool tempBool;

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/BuffID", "");
  if (element == NULL)
    temp = NULL_INDEX;
  else
    {
      temp = int(xmlXPathCastStringToNumber(element));
      xmlFree (element);
    }
  setBuffID (temp);

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/MinimumLevelWarrior", "");
  if (element == NULL)
    temp = 255;
  else
    {
      temp = int(xmlXPathCastStringToNumber(element));
      xmlFree (element);
    }
  setMinLevel (PLAYER_CLASS_WARRIOR, temp);

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/MinimumLevelArcher", "");
  if (element == NULL)
    temp = 255;
  else
    {
      temp = int(xmlXPathCastStringToNumber(element));
      xmlFree (element);
    }
  setMinLevel (PLAYER_CLASS_ARCHER, temp);

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/MinimumLevelMage", "");
  if (element == NULL)
    temp = 255;
  else
    {
      temp = int(xmlXPathCastStringToNumber(element));
      xmlFree (element);
    }
  setMinLevel (PLAYER_CLASS_MAGE, temp);

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/MinimumLevelSwashbuckler", "");
  if (element == NULL)
    temp = 255;
  else
    {
      temp = int(xmlXPathCastStringToNumber(element));
      xmlFree (element);
    }
  setMinLevel (PLAYER_CLASS_SWASHBUCKLER, temp);

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/SelfBuff", "");
  tempBool = false;
  if (element != NULL && (
                          xmlStrcasecmp(element, ((const xmlChar *)"true")) == 0 ||
                          xmlStrcasecmp(element, ((const xmlChar *)"yes")) == 0))
    {
      tempBool = true;
      xmlFree (element);
    }
  setSelfBuff (tempBool);

  //RechargeTime: Amount of time it takes the power to recharge. Default value is 16
  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/RechargeTime", "");
  if (element == NULL)
    temp = 16;
  else
    {
      temp = int(xmlXPathCastStringToNumber(element));
      xmlFree (element);
    }

  if (temp < 0)
    temp=0;
  else if (temp > 255)
    temp=255;

  setRechargeTime(temp);

  //Description: the PlayerAbility gets a 1-line description.
  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/Description", "");
  if (element == NULL)
    gab="NO DESCRIPTION";
  else
    {
      gab=(const char *)element;
      xmlFree (element);
    }
  setDescription (gab);

  //AttackPath: The name of the AttackPath to load.
  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/AttackPath", "");
  if (element == NULL)
    gab="NULL_INDEX";
  else
    {
      gab=(const char *)element;
      xmlFree (element);
    }
  setAttackPath (gab);

  //ButtonIcon: The name of the ButtonIcon.
  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/ButtonIcon", "");
  if (element == NULL)
    gab="NULL_INDEX";
  else
    {
      gab=(const char *)element;
      xmlFree (element);
    }
  setButtonIcon (gab);
  return true;
}

void PlayerAbility :: setSelfBuff (bool sb)
{
  selfBuff = sb;
}

void PlayerAbility :: setBuffID (uint8_t bid)
{
  buffID = bid;
}

void PlayerAbility :: setMinLevel (uint8_t clas, uint8_t lev)
{
  if (clas >= minLevel.size ())
    return;

  minLevel.at (clas) = lev;
}

uint8_t PlayerAbility :: getRechargeTime()
{
  return rechargeTime;
}

void PlayerAbility :: setRechargeTime(uint8_t rt)
{
  rechargeTime=rt;
}

const string & PlayerAbility :: getDescription ()
{
  return description;
}

void PlayerAbility :: setDescription (const string & d)
{
  description = d;
}

const string & PlayerAbility :: getAttackPath ()
{
  return attackPath;
}

void PlayerAbility :: setAttackPath (const string & d)
{
  attackPath = d;
}

const string & PlayerAbility :: getButtonIcon ()
{
  return buttonIcon;
}

void PlayerAbility :: setButtonIcon (const string & d)
{
  buttonIcon = d;
}

//clears battle data
void PlayerAbility::clearDuringBattleData()
{
  oneTimeRechargeBoost=false;
  timeSpentRecharging=0;
}

//Resets battle data, used to start the battle.
//if startTime is greater than 0, oneTimeRechargeBoost is set to true.
void PlayerAbility::startDuringBattleData(int startTime)
{
  timeSpentRecharging=rechargeTime+startTime;
  oneTimeRechargeBoost=(startTime>0);
}
  
//adds to the timeSpentRecharging
void PlayerAbility::addTime (int amount)
{
  if (abilityIsRecharged())
    return;
  timeSpentRecharging+=amount;
}

//returns true if the ability has recharged
bool PlayerAbility::abilityIsRecharged()
{
  return (timeSpentRecharging>=rechargeTime);
}

//call when power has been used to reset the timeSpentRecharging and oneTimeRechargeBoost
void PlayerAbility::useRecharge()
{
  //set timeSpentRecharging to 0 unless oneTimeRechargeBoost is active.
  //if oneTimeRechargeBoost is active, take the normal time off of it.
  if (oneTimeRechargeBoost)
    timeSpentRecharging-=rechargeTime;
  else
    timeSpentRecharging=0;
  oneTimeRechargeBoost=false;
}

//returns the amount of time left to recharge
int PlayerAbility::getTimeSpentRecharging()
{
  return timeSpentRecharging;
}
