#include "shippingHandler.h"

void shippingHandler :: drawPrimitive (Param & hash, AffineTransformationMatrix & atm)
{
  string keyName = "NULL_INDEX";
  int xPos = 0, yPos = 0;
  bool relativeToCamera=true;

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

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

  map <string, GraphicPrimitive> :: iterator iter2;
  iter2 = graphicPrimitives.find (keyName);

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

  int destX = xPos, destY = yPos, destW, destH;
  uint8_t colR, colG, colB, alph;
  uint32_t i;
  SDL_Surface *outputSurface = screenBuffer;
  int16_t minX=0, minY=0;
  AffineTransformationMatrix atmInProgress;
  FixedInt shiftX, shiftY;
  int horizAnchorX, horizAnchorY, horizDist, vertAnchorX, vertAnchorY, vertDist;

  vector<int>xPoints, yPoints, otherShapeData;

  iter2->second.fillXCoords(xPoints);
  iter2->second.fillYCoords(yPoints);
  iter2->second.fillOtherShapeData(otherShapeData);

  xPoints.insert(xPoints.begin(), 0);
  yPoints.insert(yPoints.begin(), 0);

  calculateDimensions (iter2->second.getShapeType(), xPoints, yPoints, otherShapeData, destW, destH);

  colR = iter2->second.getColorR();
  colG = iter2->second.getColorG();
  colB = iter2->second.getColorB();
  alph = iter2->second.getAlpha();

  //using the AffineTransformationMatrix:
  //Start with the identity.
  //Add a shift that puts the left side at -width/2, and bottom at -height/2.
  //   Those shifts are (-w/2 -L, -h/2 -H)
  //Apply the given atm.
  //Move the center to the destination.
  //   A shift of (destX, destY)

  if (iter2->second.getShapeType()=="Rectangle")
    {
      xPoints[0]=0; yPoints[0]=0;
      xPoints[1]=destW; yPoints[1]=destH;

      shiftX=double(destW/2.0); shiftY=double(destH/2.0);
      atmInProgress.shift(shiftX*-1, shiftY*-1);
      atmInProgress.multiplyMatrix(atm);
      calculateDestination (relativeToCamera, destX, destY);
      shiftX=destX; shiftY=destY;
      atmInProgress.shift(shiftX, shiftY);

      for (i=0; i < xPoints.size() && i < yPoints.size(); i++)
        atmInProgress.apply(xPoints[i], yPoints[i]);

      drawPrimitiveRectangle (xPoints[0],yPoints[0],xPoints[1],yPoints[1],
                              iter2->second, outputSurface, colR, colG, colB, alph);
    }
  if (iter2->second.getShapeType()=="Triangle")
    {
      minX=*min_element(xPoints.begin(), xPoints.end());
      minY=*min_element(yPoints.begin(), yPoints.end());

      shiftX=double(minX+destW/2.0);shiftY=double(minY+destH/2.0);
      atmInProgress.shift(shiftX*-1, shiftY*-1);
      atmInProgress.multiplyMatrix(atm);
      calculateDestination (relativeToCamera, destX, destY);
      shiftX=destX; shiftY=destY;
      atmInProgress.shift(shiftX, shiftY);

      for (i=0; i < xPoints.size() && i < yPoints.size(); i++)
        atmInProgress.apply(xPoints[i], yPoints[i]);

      drawPrimitiveTriangle (xPoints[0], yPoints[0],
                             xPoints[1], yPoints[1],
                             xPoints[2], yPoints[2],
                             iter2->second, outputSurface, colR, colG, colB, alph);
    }
  if (iter2->second.getShapeType()=="Line")
    {
      if (iter2->second.getOtherShapeData()[0]==1)
        {
          xPoints[0]= 0;
          xPoints[1]= destW;
        }
      else
        {
          xPoints[0]= destW;
          xPoints[1]= 0;
        }
      yPoints[0]=0;
      yPoints[1]=destH;

      shiftX=double(minX+destW/2.0); shiftY=double(minY+destH/2.0);
      atmInProgress.shift(shiftX*-1, shiftY*-1);
      atmInProgress.multiplyMatrix(atm);
      calculateDestination (relativeToCamera, destX, destY);
      shiftX=destX; shiftY=destY;
      atmInProgress.shift(shiftX, shiftY);

      for (i=0; i < xPoints.size() && i < yPoints.size(); i++)
        atmInProgress.apply(xPoints[i], yPoints[i]);

      drawPrimitiveLine (xPoints[0], yPoints[0], xPoints[1], yPoints[1], iter2->second, outputSurface, colR, colG, colB, alph);
    }
  if (iter2->second.getShapeType()=="Circle")
    {
      xPoints[0]=0; yPoints[0]=0;

      atmInProgress.multiplyMatrix(atm);
      calculateDestination (relativeToCamera, destX, destY);
      shiftX=destX; shiftY=destY;
      atmInProgress.shift(shiftX, shiftY);
      atmInProgress.apply(xPoints[0], yPoints[0]);

      //Circle is a special case. We're using the metadata to determine the radius.
      horizDist=(atmInProgress.getScaleX()*otherShapeData[0]).toInt();
      drawPrimitiveCircle (xPoints[0], yPoints[0], horizDist, iter2->second, outputSurface, colR, colG, colB, alph);
    }
  if (iter2->second.getShapeType()=="Ellipse")
    {
      xPoints[0]=0; yPoints[0]=0;
      atmInProgress.multiplyMatrix(atm);
      calculateDestination (relativeToCamera, destX, destY);
      shiftX=destX; shiftY=destY;
      atmInProgress.shift(shiftX, shiftY);
      atmInProgress.apply(xPoints[0], yPoints[0]);

      //Ellipse is a special case. We will get the radius by applying the ATM to a dummy point then getting the distance to that point.
      horizAnchorX=otherShapeData[0]; horizAnchorY=0;
      atmInProgress.apply(horizAnchorX, horizAnchorY);
      horizAnchorX-=xPoints[0];horizAnchorY-=yPoints[0];
      horizDist=intSqrt(horizAnchorX*horizAnchorX + horizAnchorY*horizAnchorY);

      vertAnchorX=0; vertAnchorY=otherShapeData[1];
      atmInProgress.apply(vertAnchorX, vertAnchorY);
      vertAnchorX-=xPoints[0];vertAnchorY-=yPoints[0];
      vertDist=intSqrt(vertAnchorX*vertAnchorX + vertAnchorY*vertAnchorY);

      drawPrimitiveEllipse (xPoints[0], yPoints[0], horizDist, vertDist, iter2->second, outputSurface, colR, colG, colB, alph);
    }
  if (iter2->second.getShapeType()=="Pixel")
    {
      //This locks and updates the surface. If you need to do a lot of these, it's relatively slow.
      xPoints[0]=0; yPoints[0]=0;
      atmInProgress.multiplyMatrix(atm);
      calculateDestination (relativeToCamera, destX, destY);
      shiftX=destX; shiftY=destY;
      atmInProgress.shift(shiftX, shiftY);
      atmInProgress.apply(xPoints[0], yPoints[0]);

      if (alph==255)
        sge_PutPixelAlpha(outputSurface, xPoints[0], yPoints[0], colR, colG, colB, alph);
      else
        sge_PutPixel(outputSurface, xPoints[0], yPoints[0], colR, colG, colB);
    }
  if (iter2->second.getShapeType()=="Bezier")
    {
      //Bezier objects are already centered around the origin.
      shiftX=double(destW/2.0); shiftY=double(destH/2.0);
      atmInProgress.shift(shiftX*-1, shiftY*-1);
      atmInProgress.multiplyMatrix(atm);
      calculateDestination (relativeToCamera, destX, destY);
      shiftX=destX; shiftY=destY;
      atmInProgress.shift(shiftX, shiftY);

      for (i=0; i < xPoints.size() && i < yPoints.size(); i++)
        atmInProgress.apply(xPoints[i], yPoints[i]);

      drawPrimitiveBezier (otherShapeData[0],
                           xPoints[0], yPoints[0],
                           xPoints[1], yPoints[1],
                           xPoints[2], yPoints[2],
                           xPoints[3], yPoints[3],
                           iter2->second, outputSurface, colR, colG, colB, alph);
    }
  if (iter2->second.getShapeType()=="FilledPolygon")
    {
      int16_t xPointArray[otherShapeData[0]], yPointArray[otherShapeData[0]];

      minX=*min_element(xPoints.begin(), xPoints.end());
      minY=*min_element(yPoints.begin(), yPoints.end());

      shiftX=double(minX+destW/2.0);shiftY=double(minY+destH/2.0);
      atmInProgress.shift(shiftX*-1, shiftY*-1);
      atmInProgress.multiplyMatrix(atm);
      calculateDestination (relativeToCamera, destX, destY);
      shiftX=destX; shiftY=destY;
      atmInProgress.shift(shiftX, shiftY);

      for (i=0; i < xPoints.size() && i < yPoints.size(); i++)
        atmInProgress.apply(xPoints[i], yPoints[i]);

      //apply scaling & rotation to remaining points
      for (i=0; i < uint32_t(otherShapeData[0]); i++)
        {
          xPointArray[i]=xPoints[i];
          yPointArray[i]=yPoints[i];
        }

      drawPrimitiveFilledPolygon (otherShapeData[0], xPointArray, yPointArray,
                                  iter2->second, outputSurface,
                                  colR, colG, colB, alph);
    }
}

void shippingHandler :: drawPrimitiveCircle (int16_t x1, int16_t y1, int16_t diameter, GraphicPrimitive & sourceToUse, SDL_Surface * outputSurface, uint8_t colR, uint8_t colG, uint8_t colB, uint8_t alph)
{
  //anti-aliased circle
  if (!sourceToUse.getFilled() && sourceToUse.getAntiAliased() && alph==255)
    sge_AACircle(outputSurface, x1, y1, diameter/2, colR, colG, colB);
  else if (!sourceToUse.getFilled() && sourceToUse.getAntiAliased() && alph<255)
    sge_AACircleAlpha(outputSurface, x1, y1, diameter/2, colR, colG, colB, alph);
  //anti-aliased filled circle. Note there is no alpha option for this.
  else if (sourceToUse.getFilled() && sourceToUse.getAntiAliased() && alph==255)
    sge_AAFilledCircle(outputSurface, x1, y1, diameter/2, colR, colG, colB);
  else if (sourceToUse.getFilled() && sourceToUse.getAntiAliased() && alph<255)
    //there is no aafiledcirclealpha.
    sge_FilledCircleAlpha(outputSurface, x1, y1, diameter/2, colR, colG, colB, alph);
  //filled circle.
  else if (sourceToUse.getFilled() && alph==255)
    sge_FilledCircle(outputSurface, x1, y1, diameter/2, colR, colG, colB);
  else if (sourceToUse.getFilled() && alph<255)
    sge_FilledCircleAlpha(outputSurface, x1, y1, diameter/2, colR, colG, colB, alph);
  //and a circle without anything fancy.
  else if (alph==255)
    sge_Circle(outputSurface, x1, y1, diameter/2, colR, colG, colB);
  else if (alph<255)
    sge_CircleAlpha(outputSurface, x1, y1, diameter/2, colR, colG, colB, alph);
}

void shippingHandler :: drawPrimitiveLine (int16_t x1, int16_t y1, int16_t x2, int16_t y2, GraphicPrimitive & sourceToUse, SDL_Surface * outputSurface, uint8_t colR, uint8_t colG, uint8_t colB, uint8_t alph)
{
  int destW = x1-x2, destH = y1-y2;

  //check for vertical lines.
  if (destW == 0 && alph==255)
    sge_VLine(outputSurface, x1, y1, y2, colR, colG, colB);
  else if (destW == 0 && alph<255)
    sge_VLineAlpha(outputSurface, x1, y1, y2, colR, colG, colB, alph);
  //check for horizontal lines.
  else if (destH == 0 && alph==255)
    sge_HLine(outputSurface, x1, x2, y1, colR, colG, colB);
  else if (destH == 0 && alph<255)
    sge_HLineAlpha(outputSurface, x1, x2, y1, colR, colG, colB, alph);
  //now anti aliasing.
  else if (sourceToUse.getAntiAliased() && alph==255)
    sge_AALine(outputSurface, x1, y1, x2, y2, colR, colG, colB);
  else if (sourceToUse.getAntiAliased() && alph<255)
    sge_AALineAlpha(outputSurface, x1, y1, x2, y2, colR, colG, colB, alph);
  //now just write the line.
  else if (!sourceToUse.getAntiAliased() && alph==255)
    sge_Line(outputSurface, x1, y1, x2, y2, colR, colG, colB);
  else if (!sourceToUse.getAntiAliased() && alph<255)
    sge_LineAlpha(outputSurface, x1, y1, x2, y2, colR, colG, colB, alph);
}

void shippingHandler :: drawPrimitiveEllipse (int16_t x1, int16_t y1, int16_t diameterX, int16_t diameterY, GraphicPrimitive & sourceToUse, SDL_Surface * outputSurface, uint8_t colR, uint8_t colG, uint8_t colB, uint8_t alph)
{
  //anti-aliased ellipse
  if (!sourceToUse.getFilled() && sourceToUse.getAntiAliased() && alph==255)
    sge_AAEllipse(outputSurface, x1, y1, diameterX/2, diameterY/2, colR, colG, colB);
  else if (!sourceToUse.getFilled() && sourceToUse.getAntiAliased() && alph<255)
    sge_AAEllipseAlpha(outputSurface, x1, y1, diameterX/2, diameterY/2, colR, colG, colB, alph);
  //anti-aliased filled ellipse. Note there is no alpha option for this.
  else if (sourceToUse.getFilled() && sourceToUse.getAntiAliased() && alph==255)
    sge_AAFilledEllipse(outputSurface, x1, y1, diameterX/2, diameterY/2, colR, colG, colB);
  else if (sourceToUse.getFilled() && sourceToUse.getAntiAliased() && alph<255)
    //there is no aafiledcellipsealpha.
    sge_FilledEllipseAlpha(outputSurface, x1, y1, diameterX/2, diameterY/2, colR, colG, colB, alph);
  //filled ellipse.
  else if (sourceToUse.getFilled() && alph==255)
    sge_FilledEllipse(outputSurface, x1, y1, diameterX/2, diameterY/2, colR, colG, colB);
  else if (sourceToUse.getFilled() && alph<255)
    sge_FilledEllipseAlpha(outputSurface, x1, y1, diameterX/2, diameterY/2, colR, colG, colB, alph);
  //and an ellipse without anything fancy.
  else if (alph==255)
    sge_Ellipse(outputSurface, x1, y1, diameterX/2, diameterY/2, colR, colG, colB);
  else if (alph<255)
    sge_EllipseAlpha(outputSurface, x1, y1, diameterX/2, diameterY/2, colR, colG, colB, alph);
}

void shippingHandler :: drawPrimitiveRectangle (int16_t x1, int16_t y1, int16_t x2, int16_t y2, GraphicPrimitive & sourceToUse, SDL_Surface * outputSurface, uint8_t colR, uint8_t colG, uint8_t colB, uint8_t alph)
{
  if (alph==255 && !sourceToUse.getFilled())
    {
      sge_Rect(outputSurface, x1, y1, x2, y2, colR, colG, colB);
    }
  else if (alph<255 && !sourceToUse.getFilled())
    {
      sge_RectAlpha(outputSurface, x1, y1, x2, y2, colR, colG, colB, alph);
    }
  else if (alph==255 && !sourceToUse.getFilled())
    {
      sge_FilledRect(outputSurface, x1, y1, x2, y2, colR, colG, colB);
    }
  else
    {
      sge_FilledRectAlpha(outputSurface, x1, y1, x2, y2, colR, colG, colB, alph);
    }
}

void shippingHandler :: drawPrimitiveTriangle (int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t x3, int16_t y3, GraphicPrimitive & sourceToUse, SDL_Surface * outputSurface, uint8_t colR, uint8_t colG, uint8_t colB, uint8_t alph)
{
  if (alph==255 && !sourceToUse.getAntiAliased() && !sourceToUse.getFilled())
    sge_Trigon(outputSurface, x1, y1, x2, y2, x3, y3, colR, colG, colB);
  else if (alph<255 && !sourceToUse.getAntiAliased() && !sourceToUse.getFilled())
    sge_TrigonAlpha(outputSurface, x1, y1, x2, y2, x3, y3, colR, colG, colB, alph);
  else if (alph==255 && sourceToUse.getAntiAliased() && !sourceToUse.getFilled())
    sge_AATrigon(outputSurface, x1, y1, x2, y2, x3, y3, colR, colG, colB);
  else if (alph<255 && sourceToUse.getAntiAliased() && !sourceToUse.getFilled())
    sge_AATrigonAlpha(outputSurface, x1, y1, x2, y2, x3, y3, colR, colG, colB, alph);
  else if (alph==255 && sourceToUse.getFilled())
    sge_FilledTrigon(outputSurface, x1, y1, x2, y2, x3, y3, colR, colG, colB);
  else if (alph<255 && sourceToUse.getFilled())
    sge_FilledTrigonAlpha(outputSurface, x1, y1, x2, y2, x3, y3, colR, colG, colB, alph);
}

void shippingHandler :: drawPrimitiveBezier (int16_t level, int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t x3, int16_t y3, int16_t x4, int16_t y4, GraphicPrimitive & sourceToUse, SDL_Surface * outputSurface, uint8_t colR, uint8_t colG, uint8_t colB, uint8_t alph)
{
  if (alph==255 && sourceToUse.getAntiAliased())
    sge_AABezier(outputSurface, x1, y1, x2, y2, x3, y3, x4, y4, level, colR, colG, colB);
  else if (alph<255 && sourceToUse.getAntiAliased())
    sge_AABezierAlpha(outputSurface, x1, y1, x2, y2, x3, y3, x4, y4, level, colR, colG, colB, alph);
  else if (alph==255 && !sourceToUse.getAntiAliased())
    sge_Bezier(outputSurface, x1, y1, x2, y2, x3, y3, x4, y4, level, colR, colG, colB);
  else if (alph<255 && !sourceToUse.getAntiAliased())
    sge_BezierAlpha(outputSurface, x1, y1, x2, y2, x3, y3, x4, y4, level, colR, colG, colB, alph);
}

void shippingHandler :: drawPrimitiveFilledPolygon (int16_t numberOfPoints, int16_t * xPoints, int16_t * yPoints, GraphicPrimitive & sourceToUse, SDL_Surface * outputSurface, uint8_t colR, uint8_t colG, uint8_t colB, uint8_t alph)
{
  if (sourceToUse.getAntiAliased())
    sge_AAFilledPolygon(outputSurface, numberOfPoints, xPoints, yPoints, colR, colG, colB);
  else if (alph==255)
    sge_FilledPolygon(outputSurface, numberOfPoints, xPoints, yPoints, colR, colG, colB);
  else
    sge_FilledPolygonAlpha(outputSurface, numberOfPoints, xPoints, yPoints, colR, colG, colB, alph);
}

void shippingHandler::getPrimitiveDimensions (const string & name, uint16_t & width, uint16_t & height)
{
  if (name.empty() || name == "NULL_INDEX")
    return;

  map <string, GraphicPrimitive> :: iterator iter2;
  iter2 = graphicPrimitives.find (name);

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

  vector<int16_t>xPoints(iter2->second.getXCoords());
  vector<int16_t>yPoints(iter2->second.getYCoords());
  vector<int16_t>otherShapeData(iter2->second.getOtherShapeData());

  xPoints.insert(xPoints.begin(), 0);
  yPoints.insert(yPoints.begin(), 0);

  string gab = iter2->second.getShapeType();

  int wa, ha;

  calculateDimensions (gab, xPoints, yPoints, otherShapeData, wa, ha);

  width=wa;
  height=ha;
}


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

  return loadResource (keyName, "primitive");
}

bool shippingHandler :: loadPrimitiveHelper (const string & index, GraphicPrimitive & prim)
{
  //loads the primitive and returns true if there are no errors.

  //parse the xml file and find the filename for the given index
  xmlDocPtr doc;
  xmlXPathContextPtr xpathCtx;
  string tempString;
  int temp;
  bool loadedShapeInfo;

  if (!prepareXMLFile (doc, xpathCtx, "resources/PrimitiveManifest.xml", ""))
    {
      writeToErrorLog("Unable to open PrimitiveManifest file.", 0);
      return false;
    }

  xmlChar *keyword;

  //does this entry exist?
  if (!xPathExists (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']"))
    {
      xmlXPathFreeContext(xpathCtx);
      xmlFreeDoc(doc);
      writeToErrorLog("PrimitiveManifest does not have the entry for "+index, 1);
      return false;
    }

  //It needs colors.
  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/ColorRed");
  if (keyword==NULL)
    {
      xmlXPathFreeContext(xpathCtx);
      xmlFreeDoc(doc);
      writeToErrorLog("PrimitiveManifest: "+index+" has no ColorRed entry.", 1);
      return false;
    }

  temp=int(xmlXPathCastStringToNumber(keyword));
  if (temp < 0)
    temp = 0;
  if (temp > 255)
    temp = 255;

  prim.setColorR(uint8_t(temp));
  xmlFree(keyword);

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/ColorBlue");
  if (keyword==NULL)
    {
      xmlXPathFreeContext(xpathCtx);
      xmlFreeDoc(doc);
      writeToErrorLog("PrimitiveManifest: "+index+" has no ColorBlue entry.", 1);
      return false;
    }

  temp=int(xmlXPathCastStringToNumber(keyword));
  if (temp < 0)
    temp = 0;
  if (temp > 255)
    temp = 255;

  prim.setColorB(uint8_t(temp));
  xmlFree(keyword);

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/ColorGreen");
  if (keyword==NULL)
    {
      xmlXPathFreeContext(xpathCtx);
      xmlFreeDoc(doc);
      writeToErrorLog("PrimitiveManifest: "+index+" has no ColorGreen entry.", 1);
      return false;
    }

  temp=int(xmlXPathCastStringToNumber(keyword));
  if (temp < 0)
    temp = 0;
  if (temp > 255)
    temp = 255;

  prim.setColorG(uint8_t(temp));
  xmlFree(keyword);

  //Alpha level is optional.
  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Alpha");
  if (keyword!=NULL)
    {
      temp=int(xmlXPathCastStringToNumber(keyword));
      if (temp < 0)
        temp = 0;
      if (temp > 255)
        temp = 255;

      prim.setAlpha(uint8_t(temp));
      xmlFree(keyword);
      keyword = NULL;
    }
  else
    prim.setAlpha(255);

  //filled is optional.
  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Filled");
  if (keyword!=NULL)
    {
      tempString=(const char *)keyword;
      if (tempString == "true" || tempString == "yes")
        prim.setFilled (true);
      else
        prim.setFilled (false);
      xmlFree(keyword);
      keyword = NULL;
    }
  else
    prim.setFilled (false);

  //antiAliased is optional.
  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/AntiAliased");
  if (keyword!=NULL)
    {
      tempString=(const char *)keyword;
      if (tempString == "true" || tempString == "yes")
        prim.setAntiAliased (true);
      else
        prim.setAntiAliased (false);
      xmlFree(keyword);
      keyword = NULL;
    }
  else
    prim.setAntiAliased (false);

  //Now you get the shape. The shape can be one of many things.
  loadedShapeInfo = false;

  if (xPathExists (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Pixel"))
    {
      prim.setShapeType("Pixel");
      loadedShapeInfo = true;
    }
  else if (xPathExists (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Line"))
    {
      prim.setShapeType("Line");
      loadedShapeInfo = loadPrimitiveHelperLine(index, prim, doc, xpathCtx);
    }
  else if (xPathExists (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Rectangle"))
    {
      prim.setShapeType("Rectangle");
      loadedShapeInfo = loadPrimitiveHelperRectangle(index, prim, doc, xpathCtx);
    }
  else if (xPathExists (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Circle"))
    {
      prim.setShapeType("Circle");
      loadedShapeInfo = loadPrimitiveHelperCircle(index, prim, doc, xpathCtx);
    }
  else if (xPathExists (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Ellipse"))
    {
      prim.setShapeType("Ellipse");
      loadedShapeInfo = loadPrimitiveHelperEllipse(index, prim, doc, xpathCtx);
    }
  else if (xPathExists (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Bezier"))
    {
      prim.setShapeType("Bezier");
      loadedShapeInfo = loadPrimitiveHelperBezier(index, prim, doc, xpathCtx);
    }
  else if (xPathExists (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Triangle"))
    {
      prim.setShapeType("Triangle");
      loadedShapeInfo = loadPrimitiveHelperTriangle(index, prim, doc, xpathCtx);
    }
  else if (xPathExists (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/FilledPolygon"))
    {
      prim.setShapeType("FilledPolygon");
      loadedShapeInfo = loadPrimitiveHelperFilledPolygon(index, prim, doc, xpathCtx);
    }
  else
    {
      writeToErrorLog ("loadPrimitiveHelper: No recognized shape for "+index, 2);
      loadedShapeInfo = false;
    }

  xmlXPathFreeContext(xpathCtx);
  xmlFreeDoc(doc);

  if (keyword != NULL)
    xmlFree(keyword);

  if (!loadedShapeInfo)
    {
      writeToErrorLog ("loadPrimitiveHelper: Primitive Shape failed to load "+index, 2);
      return false;
    }
  return true;
}

bool shippingHandler :: loadPrimitiveHelperBezier (const string & index, GraphicPrimitive & prim, xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx)
{
  int16_t temp;
  xmlChar *keyword;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Bezier/Level");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushOtherShapeData(temp);
      xmlFree(keyword);
    }
  else
    return false;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Bezier/Control1X");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushXCoord(temp);
      xmlFree(keyword);
    }
  else
    return false;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Bezier/Control1Y");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushYCoord(temp);
      xmlFree(keyword);
    }
  else
    return false;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Bezier/Control2X");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushXCoord(temp);
      xmlFree(keyword);
    }
  else
    return false;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Bezier/Control2Y");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushYCoord(temp);
      xmlFree(keyword);
    }
  else
    return false;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Bezier/EndpointX");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushXCoord(temp);
      xmlFree(keyword);
    }
  else
    return false;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Bezier/EndpointY");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushYCoord(temp);
      xmlFree(keyword);
    }
  else
    return false;

  return true;
}

bool shippingHandler :: loadPrimitiveHelperTriangle (const string & index, GraphicPrimitive & prim, xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx)
{
  int16_t temp;
  xmlChar *keyword;

  //why don't I just search for pointX and pointY?
  char searchString [255];
  string idStringX, idStringY;
  bool done=false;
  int i = 0;

  while (!done)
    {
      sprintf (searchString, "[%u]", i+1);
      idStringX="']/Triangle/PointX";
      idStringX+=searchString;
      idStringY="']/Triangle/PointY";
      idStringY+=searchString;
      keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, idStringX);
      if (keyword==NULL)
        {
          //could not find the data.
          done=true;
          continue;
        }
      else
        {
          temp=int16_t(xmlXPathCastStringToNumber(keyword));
          xmlFree(keyword);
          prim.pushXCoord(temp);
        }

      keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, idStringY);
      if (keyword==NULL)
        {
          //could not find the data.
          done=true;
          continue;
        }
      else
        {
          temp=int16_t(xmlXPathCastStringToNumber(keyword));
          xmlFree(keyword);
          prim.pushYCoord(temp);
        }
      i++;

      if (i >= 2)
        done=true; //we only need the first two points
    }

  if (i < 2)
    return false;

  return true;
}

bool shippingHandler :: loadPrimitiveHelperFilledPolygon (const string & index, GraphicPrimitive & prim, xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx)
{
  int16_t temp;
  xmlChar *keyword;
  vector<int16_t> pointsX, pointsY;
  int16_t numberOfPoints;

  //why don't I just search for pointX and pointY?
  char searchString [255];
  string idStringX, idStringY;
  bool done=false;
  int i = 0;

  while (!done)
    {
      sprintf (searchString, "[%u]", i+1);
      idStringX="']/FilledPolygon/PointX";
      idStringX+=searchString;
      idStringY="']/FilledPolygon/PointY";
      idStringY+=searchString;
      keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, idStringX);
      if (keyword==NULL)
        {
          //could not find the data.
          done=true;
          continue;
        }
      else
        {
          temp=int16_t(xmlXPathCastStringToNumber(keyword));
          xmlFree(keyword);
          pointsX.push_back(temp);
        }

      keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, idStringY);
      if (keyword==NULL)
        {
          //could not find the data.
          done=true;
          continue;
        }
      else
        {
          temp=int16_t(xmlXPathCastStringToNumber(keyword));
          xmlFree(keyword);
          pointsY.push_back(temp);
        }
      i++;
    }

  //you need 3 points to make a polygon. The first point is (0,0) so we need 2 more.
  if (pointsX.size() < 2 || pointsY.size() < 2)
    return false;

  //how many points does this object have? The minimum count of the two vectors.
  if (pointsX.size() <= pointsY.size())
    numberOfPoints = pointsX.size();
  else
    numberOfPoints = pointsY.size();

  numberOfPoints++;

  prim.pushOtherShapeData(numberOfPoints);

  //OK, now load the data into shape info, with alternating pops.
  for (int i = 0; i < numberOfPoints-1; i ++)
    {
      prim.pushXCoord(pointsX[i]);
      prim.pushYCoord(pointsY[i]);
    }

  return true;
}

bool shippingHandler :: loadPrimitiveHelperCircle (const string & index, GraphicPrimitive & prim, xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx)
{
  //this must have the other coordinate.
  int16_t temp;
  xmlChar *keyword;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Circle/Radius");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushOtherShapeData(temp);
      xmlFree(keyword);
    }
  else
    return false;

  return true;
}

bool shippingHandler :: loadPrimitiveHelperEllipse (const string & index, GraphicPrimitive & prim, xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx)
{
  int16_t temp;
  xmlChar *keyword;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Ellipse/RadiusX");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushOtherShapeData(temp);
      xmlFree(keyword);
    }
  else
    return false;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Ellipse/RadiusY");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushOtherShapeData(temp);
      xmlFree(keyword);
    }
  else
    return false;

  return true;
}

bool shippingHandler :: loadPrimitiveHelperLine (const string & index, GraphicPrimitive & prim, xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx)
{
  //this must have the other coordinate.
  int16_t temp;
  xmlChar *keyword;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Line/Width");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushXCoord(temp);
      xmlFree(keyword);
    }
  else
    {
      writeToErrorLog("LoadPrimitiveLine: No /Width found for "+index, 1);
      return false;
    }

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Line/Height");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushYCoord(temp);
      xmlFree(keyword);
    }
  else
    {
      writeToErrorLog("LoadPrimitiveLine: No /Height found for "+index, 1);
      return false;
    }

  int16_t drawLineToTheRight=0;

  if (prim.getXCoords()[0] < 0 && prim.getXCoords()[1] < 0)
    drawLineToTheRight=1;
  else if (prim.getXCoords()[0] > 0 && prim.getXCoords()[1] > 0)
    drawLineToTheRight=1;
  else
    drawLineToTheRight=0;

  prim.pushOtherShapeData(drawLineToTheRight);

  return true;
}

bool shippingHandler :: loadPrimitiveHelperRectangle (const string & index, GraphicPrimitive & prim, xmlDocPtr & doc, xmlXPathContextPtr & xpathCtx)
{
  //this must have the other coordinate.
  int16_t temp;
  xmlChar *keyword;

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Rectangle/Width");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushXCoord(temp);
      xmlFree(keyword);
    }
  else
    {
      writeToErrorLog("LoadPrimitiveRectangle: No /Width found for "+index, 1);
      return false;
    }

  keyword = getFirstElementUsingXPath (doc, xpathCtx, "/PrimitiveManifest/Primitive[@ID='", index, "']/Rectangle/Height");
  if (keyword!=NULL)
    {
      temp=int16_t(xmlXPathCastStringToNumber(keyword));
      prim.pushYCoord(temp);
      xmlFree(keyword);
    }
  else
    {
      writeToErrorLog("LoadPrimitiveRectangle: No /Height found for "+index, 1);
      return false;
    }

  return true;
}

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

  map <string, GraphicPrimitive> :: iterator iter2;
  iter2 = graphicPrimitives.find (keyName);

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

  iter2->second.decRefCount();

  if (iter2->second.getRefCount()==0)
    {
      graphicPrimitives.erase(iter2);
    }
}

void shippingHandler::calculateDimensions (const string & shapeType, vector<int16_t> & xCoords, vector<int16_t> & yCoords, vector<int16_t> & otherShapeData, int & w, int & h)
{
  int minX=0, maxX=0, minY=0, maxY=0;

  //check the dimensions depending on the shape.

  if (shapeType == "Pixel")
    {
      w = 0; h=0;
      return;
    }
  else if (shapeType == "Circle")
    {
      w=intAbs(otherShapeData[0])*2;
      h=intAbs(otherShapeData[0])*2;
      return;
    }
  else if (shapeType == "Ellipse")
    {
      w=intAbs(xCoords[1])*2;
      h=intAbs(yCoords[1])*2;
      return;
    }
  else if (shapeType=="Line" || shapeType=="Rectangle" || shapeType == "Bezier" || shapeType == "FilledPolygon" || shapeType == "Triangle")
    {
      maxX=*max_element(xCoords.begin(), xCoords.end());
      minX=*min_element(xCoords.begin(), xCoords.end());

      maxY=*max_element(yCoords.begin(), yCoords.end());
      minY=*min_element(yCoords.begin(), yCoords.end());

      w=maxX-minX;
      h=maxY-minY;
      return;
    }
}

void shippingHandler::calculateDimensions (const string & shapeType, vector<int> & xCoords, vector<int> & yCoords, vector<int> & otherShapeData, int & w, int & h)
{
  int minX=0, maxX=0, minY=0, maxY=0;

  //check the dimensions depending on the shape.

  if (shapeType == "Pixel")
    {
      w = 0; h=0;
      return;
    }
  else if (shapeType == "Circle")
    {
      w=intAbs(otherShapeData[0])*2;
      h=intAbs(otherShapeData[0])*2;
      return;
    }
  else if (shapeType == "Ellipse")
    {
      w=intAbs(xCoords[1])*2;
      h=intAbs(yCoords[1])*2;
      return;
    }
  else if (shapeType=="Line" || shapeType=="Rectangle" || shapeType == "Bezier" || shapeType == "FilledPolygon" || shapeType == "Triangle")
    {
      maxX=*max_element(xCoords.begin(), xCoords.end());
      minX=*min_element(xCoords.begin(), xCoords.end());

      maxY=*max_element(yCoords.begin(), yCoords.end());
      minY=*min_element(yCoords.begin(), yCoords.end());

      w=maxX-minX;
      h=maxY-minY;
      return;
    }
}

void shippingHandler::calculateDestination (bool respectCamera, int & destX, int & destY)
{
  //translates the given destX and destY to screen coordinates.
  //this function takes the dimensions of the objects into effect.
  //it takes the camera position into effect.

  if (respectCamera)
    {
      destX=(screenResolutionX/2 + destX - observerX);
      destY=(screenResolutionY/2 + destY - observerY);
    }
}


//draw directly to the buffer.
void shippingHandler::drawPrimitiveCircleDirect (Param & hash)
{
  int x=0, y=0, radius=10, colR=255, colG=255, colB=255, alph=255;
  bool antiAlias=false, filled=false;
  GraphicPrimitive g;

  hash.getInt("X", x);
  hash.getInt("Y", y);
  hash.getInt("Radius", radius);
  hash.getInt("Red", colR);
  hash.getInt("Green", colG);
  hash.getInt("Blue", colB);
  hash.getInt("Alpha", alph);
  hash.getBool("Filled", filled);
  hash.getBool("Anti Aliased", antiAlias);
  g.setFilled(filled);
  g.setAntiAliased(antiAlias);

  drawPrimitiveCircle (x, y, radius * 2, g, screenBuffer, colR, colG, colB, alph);
}

//points is a vector containing x then y coordinates. So (points[2n],points[2n+1]), is the location of the nth point.
void shippingHandler::drawPrimitiveFilledPolygonDirect (Param & hash, vector<int>& points)
{
  int  colR=255, colG=255, colB=255, alph=255, numPoints=0;
  bool antiAlias=false;
  GraphicPrimitive g;

  numPoints=points.size()/2;
  //need at least 3 points
  if (numPoints < 3)
    return;

  int16_t xPoints[numPoints], yPoints[numPoints];
  for (int n = 0; n < numPoints; n++)
    {
      xPoints[n]=points[2*n];
      yPoints[n]=points[2*n + 1];
    }

  hash.getInt("Red", colR);
  hash.getInt("Green", colG);
  hash.getInt("Blue", colB);
  hash.getInt("Alpha", alph);
  hash.getBool("Anti Aliased", antiAlias);
  g.setAntiAliased(antiAlias);

  drawPrimitiveFilledPolygon (numPoints, xPoints, yPoints, g, screenBuffer, colR, colG, colB, alph);
}

//Drawing a line.
void shippingHandler::drawPrimitiveLineDirect (Param & hash)
{
  int colR=255, colG=255, colB=255, alph=255, x1=0, y1=0, x2=0, y2=0;
  bool antiAlias=false;
  GraphicPrimitive g;

  hash.getInt("X1", x1);
  hash.getInt("Y1", y1);
  hash.getInt("X2", x2);
  hash.getInt("Y2", y2);
  hash.getInt("Red", colR);
  hash.getInt("Green", colG);
  hash.getInt("Blue", colB);
  hash.getInt("Alpha", alph);
  hash.getBool("Anti Aliased", antiAlias);
  g.setAntiAliased(antiAlias);

  drawPrimitiveLine (x1,y1,x2,y2,g, screenBuffer, colR, colG, colB, alph);
}

void shippingHandler::drawPrimitiveRectangleDirect (Param & hash)
{
  int colR=255, colG=255, colB=255, alph=255, x1=0, y1=0, x2=0, y2=0;
  bool filled=false;
  GraphicPrimitive g;

  hash.getInt("X1", x1);
  hash.getInt("Y1", y1);
  hash.getInt("X2", x2);
  hash.getInt("Y2", y2);
  hash.getInt("Red", colR);
  hash.getInt("Green", colG);
  hash.getInt("Blue", colB);
  hash.getInt("Alpha", alph);
  hash.getBool("Filled", filled);
  g.setFilled(filled);

  drawPrimitiveRectangle (x1, y1, x2, y2, g, screenBuffer, colR, colG, colB, alph);
}
