#include "shippingHandler.h"

bool shippingHandler::loadToon (string & keyName)
{
  //simply a front to loadResource.
  if (keyName.empty() || keyName == "NULL_INDEX")
    {
      keyName="NULL_INDEX";
      return false;
    }
  return loadResource (keyName, "toon");
}

bool shippingHandler::loadToonHelper (const string & keyName, AnimatedToon & newToon)
{
  xmlDocPtr doc;
  xmlXPathContextPtr xpathCtx;

  if (!prepareXMLFile (doc, xpathCtx, "resources/ToonManifest.xml", ""))
    {
      writeToErrorLog("LoadToon: Failed to open ToonManifest file.", 0);
      return false;
    }

  string xpathStart="/ToonManifest/Toon[@ID='";
  xpathStart+=keyName;
  xpathStart+="']/Animation";

  xmlXPathObjectPtr xpathObj;
  xpathObj=getElementsUsingXPath (doc, xpathCtx, xpathStart, "", "");

  if (xpathObj==NULL)
    {
      //Failed to load shape. Cleanup and exit.
      xmlXPathFreeContext(xpathCtx);
      xmlFreeDoc (doc);
      writeToErrorLog ("Failed to load AnimatedToon for index", 0);
      writeToErrorLog (keyName.c_str(), 1);
      return false;
    }

  string idString;
  char searchString [255];

  for (int i=0; i<xpathObj->nodesetval->nodeNr; i++)
    {
      sprintf (searchString, "[%u]", i+1);
      idString=xpathStart+searchString;
      if (!loadAnimationHelper (doc, xpathCtx, idString, newToon))
        {
          writeToErrorLog("While loading AnimatedToon "+keyName+" we failed to load an animation.", 1);
        }
    }

  xmlXPathFreeContext(xpathCtx);
  xmlXPathFreeObject(xpathObj);
  xmlFreeDoc(doc);

  if (newToon.numberOfAnimations()==0)
    return false;

  return true;
}

bool shippingHandler::loadAnimationHelper(xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx, const string & xpath, AnimatedToon & at)
{
  string animationName;
  int numberOfLoops=-1;
  AnimatedAnimation newAnim;
  xmlChar *element;

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/Name", "");
  if (element == NULL)
    {
      writeToErrorLog("LoadAnimationHelper: /Name missing for xpath "+xpath, 1);
      return false;
    }
  else
    animationName=(const char *)element;
  xmlFree(element);

  //make sure no other animation has the same name as this one.
  vector <string> animNames;
  at.getAllAnimationNames(animNames);

  vector <string> :: iterator animIter;
  animIter=animNames.begin();
  while (animIter != animNames.end())
    {
      if ((*animIter)==animationName)
        {
          writeToErrorLog("LoadAnimationHelper: An animation named "+animationName+" already exists for this toon.", 1);
          return false;
        }
      animIter++;
    }

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/Loops", "");
  if (element == NULL)
    numberOfLoops=-1;
  else
    {
      numberOfLoops=int(xmlXPathCastStringToNumber(element));
      xmlFree(element);
    }
  newAnim.setNumLoops(numberOfLoops);

  //need to load individual animation frames.
  xmlXPathObjectPtr xpathObj;
  xpathObj=getElementsUsingXPath (doc, xpathCtx, xpath, "/Frame", "");

  if (xpathObj==NULL)
    {
      //no frames inside the animation.
      writeToErrorLog("LoadAnimationHelper: No /Frames exist in "+xpath, 1);
      return false;
    }

  string idString;
  char searchString [255];
  bool framesOK=false;

  for (int i=0; i<xpathObj->nodesetval->nodeNr; i++)
    {
      sprintf (searchString, "/Frame[%u]", i+1);
      idString=xpath+searchString;
      if(loadAnimationFrameHelper (doc, xpathCtx, idString, newAnim))
        framesOK=true;
    }

  xmlXPathFreeObject(xpathObj);

  //needs at least one legal frame.
  if (!framesOK)
    {
      writeToErrorLog("LoadAnimationHelper: No legal frames exist in "+xpath, 1);
      return false;
    }

  at.addAnimation(animationName,newAnim);
  return true;
}

bool shippingHandler::loadAnimationFrameHelper(xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx, const string & xpath, AnimatedAnimation & newAnim)
{
  string frameToLoad, soundToLoad;
  AffineTransformationMatrix atmToLoad;
  AudioTransformation atToLoad;
  uint32_t frameDuration;
  int frameDur;
  xmlChar *element;
  int tempInt;

  //frame duration must be positive
  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/Duration", "");
  if (element == NULL)
    {
      writeToErrorLog("LoadAnimationFrameHelper: No /Duration exist in "+xpath, 1);
      return false;
    }
  else
    {
      tempInt=int(xmlXPathCastStringToNumber(element));
      xmlFree(element);
      if (tempInt<=0)
        {
          writeToErrorLog("LoadAnimationFrameHelper: Duration must be positive: "+xpath, 1);
          return false;
        }
      frameDuration=uint32_t(tempInt);
      frameDur=tempInt;
    }

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/Graphic", "");
  if (element == NULL)
    frameToLoad="NULL_INDEX";
  else
    {
      frameToLoad=(const char *)element;
      xmlFree(element);
    }

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "/Sound", "");
  if (element == NULL)
    soundToLoad="NULL_INDEX";
  else
    {
      soundToLoad=(const char *)element;
      xmlFree(element);
    }

  if (xPathExists(doc, xpathCtx, xpath, "/AudioTransformation", ""))
    loadAudioTransformationHelper (doc, xpathCtx, xpath+"/AudioTransformation", atToLoad);

  xmlXPathObjectPtr xpathObj;
  string idString;
  char searchString [255];

  xpathObj=getElementsUsingXPath (doc, xpathCtx, xpath, "/GraphicEffect", "");

  if (xpathObj!=NULL)
    {
      for (int i=0; i<xpathObj->nodesetval->nodeNr; i++)
        {
          sprintf (searchString, "/GraphicEffect[%u]", i+1);
          idString=xpath+searchString;
          loadGraphicEffectHelper (doc, xpathCtx, idString, atmToLoad);
        }
    }

  //load graphics and sound
  loadFrame(frameToLoad);
  loadSound(soundToLoad);

  Param termsToAdd;
  termsToAdd.setInt("Frame Duration", frameDur);
  termsToAdd.setString("Frame", frameToLoad);
  termsToAdd.setString("Sound", soundToLoad);

  newAnim.add(termsToAdd, atmToLoad, atToLoad);
  return true;
}

//This version uses AffineTransformationMatrix
void shippingHandler::animateToon (Param & hash, AffineTransformationMatrix & atmToUse, AnimatedState & as)
{
  string keyName = "NULL_INDEX";
  int xPos = 0, yPos = 0;
  bool respectCamera=true;

  hash.getString("Resource Name", keyName);
  hash.getInt("X", xPos);
  hash.getInt("Y", yPos);
  hash.getBool("Relative to Camera", respectCamera);

  if (keyName.empty() || keyName == "NULL_INDEX")
    return;

  map <string, AnimatedToon> :: iterator iter2;
  iter2 = toons.find (keyName);

  if (iter2 == toons.end())
    return;

  string frame, audio;
  AffineTransformationMatrix atm;
  AudioTransformation at;
  Param frameHash;

  hash.setInt("Time Right Now", SDL_GetTicks());

  iter2->second.getNext(hash, atm, at, as);

  atm.multiplyMatrix(atmToUse);

  hash.getString("Frame", frame);
  frameHash.setString("Resource Name", frame);
  frameHash.setInt("X", xPos);
  frameHash.setInt("Y", yPos);
  frameHash.setBool("Relative to Camera", respectCamera);
  drawFrame(frameHash, atm);

  hash.getString("Sound", audio);
  playSound(audio, at);
}

void shippingHandler::releaseToon (const string & keyName)
{
  if (keyName.empty() || keyName == "NULL_INDEX")
    return;

  map <string, AnimatedToon> :: iterator iter2;
  iter2 = toons.find (keyName);

  if (iter2 == toons.end())
    return;

  iter2->second.decRefCount();

  vector <string> resources;
  vector <string> :: iterator resourceIterator;

  if (iter2->second.getRefCount()==0)
    {
      resources.clear();
      iter2->second.getAllGraphicFrames(resources);
      resourceIterator=resources.begin();
      while (resourceIterator != resources.end())
        {
          releaseFrame((*resourceIterator));
          resourceIterator++;
        }

      resources.clear();
      iter2->second.getAllAudioBuffers(resources);
      resourceIterator=resources.begin();
      while (resourceIterator != resources.end())
        {
          releaseSound((*resourceIterator));
          resourceIterator++;
        }
      toons.erase(iter2);
    }
}

bool shippingHandler::loadGraphicEffectHelper(xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx, const string & xpath, AffineTransformationMatrix & atmToLoad)
{
  //extract the string
  xmlChar *element;
  string str, op, strValue;
  double dblValue;
  FixedInt value;

  element = getFirstElementUsingXPath (doc, xpathCtx, xpath, "", "");
  if (element == NULL)
    return false;
  else
    str=(const char *)element;
  xmlFree(element);

  //now extract stuff from the value.
  if (str.find(" ", 0)==string::npos)
    return false;

  op=str.substr(0, str.find(" ", 0));
  strValue=str.substr(str.find(" ", 0));

  istringstream stringStream(strValue);

  if (!(stringStream>>dblValue))
    return false;

  value=FixedInt(dblValue);

  if (op=="ScaleWidth")
    atmToLoad.scale(value, 1);
  else if (op=="ScaleHeight")
    atmToLoad.scale(1, value);
  else
    return false;

  return true;
}
