/** \file Mesh.cpp
 * \brief The implementation of the 'Mesh' class.
 */

#include "Mesh.h"

#include "VertexBones.h"
#include "XmlTools.h"

#include <xercesc/dom/DOM.hpp>
#include <xercesc/dom/DOMDocument.hpp>
#include <xercesc/dom/DOMDocumentType.hpp>
#include <xercesc/dom/DOMElement.hpp>
#include <xercesc/dom/DOMImplementation.hpp>
#include <xercesc/dom/DOMImplementationLS.hpp>
#include <xercesc/dom/DOMNodeIterator.hpp>
#include <xercesc/dom/DOMNodeList.hpp>
#include <xercesc/dom/DOMText.hpp>
XERCES_CPP_NAMESPACE_USE

namespace OSkA
{
	Mesh::Mesh()
	{
		vertices.resize(0);
		normals.resize(0);
		triangleIndices.resize(0);
		boneWeights.resize(0);
	}

	Mesh::~Mesh()
	{
		clear();
	}

	void Mesh::clear()
	{
		vertices.clear();
		normals.clear();
		triangleIndices.clear();

		for(int i=0; i<boneWeights.size(); i++)
			delete boneWeights[i];
		boneWeights.clear();
	}

	void Mesh::addVertex(float x, float y, float z)
	{
		vertices.push_back(x);
		vertices.push_back(y);
		vertices.push_back(z);
	}

	void Mesh::addNormal(float x, float y, float z)
	{
		normals.push_back(x);
		normals.push_back(y);
		normals.push_back(z);
	}

	void Mesh::addTriangle(int v1, int v2, int v3)
	{
		triangleIndices.push_back(v1);
		triangleIndices.push_back(v2);
		triangleIndices.push_back(v3);
	}

	float* Mesh::getVertexArray()
	{
		float* vertexArray=new float[3*getVerticesCount()];
		for(int i=0; i<3*getVerticesCount(); i++)
			vertexArray[i]=vertices[i];
		return vertexArray;
	}

	float* Mesh::getNormalArray()
	{
		float* normalArray=new float[3*getVerticesCount()];
		for(int i=0; i<3*getVerticesCount(); i++)
			normalArray[i]=normals[i];
		return normalArray;
	}

	int* Mesh::getTriangleArray()
	{
		int* triangleArray=new int[3*getTrianglesCount()];
		for(int i=0; i<3*getTrianglesCount(); i++)
			triangleArray[i]=triangleIndices[i];
		return triangleArray;
	}

	VertexBones* Mesh::getVertexBones(int vertex_index)
	{
		return boneWeights[vertex_index];
	}

	int Mesh::getVerticesCount()
	{
		return vertices.size()/3;
	}

	int Mesh::getTrianglesCount()
	{
		return triangleIndices.size()/3;
	}

	bool Mesh::loadFile(char* filename)
	{
		try
		{
			XMLPlatformUtils::Initialize(); // Initialize Xerces infrastructure
		}
		catch(XMLException& e)
		{
			return false;
		}

		xercesc::XercesDOMParser *parser=new XercesDOMParser;

		parser->setValidationScheme(XercesDOMParser::Val_Never);
		parser->setDoNamespaces(false);
		parser->setDoSchema(false);
		parser->setLoadExternalDTD(false);

		xercesc::DOMDocument* xmlDoc;
		try
		{
			parser->parse(filename);
			xmlDoc=parser->getDocument();
			if(!xmlDoc)// Do we have no document? Will happen when file does not exist.
				return false;
		}
		catch(xercesc::XMLException& e)
		{
			return false;
		}

		clear();

		DOMNodeList* superNodes;
		DOMElement* currentSn;
		DOMNodeList* nodes;

		vertices.resize(0);
		normals.resize(0);
		triangleIndices.resize(0);
		boneWeights.resize(0);

		superNodes=getNodeList(xmlDoc, "sharedgeometry");
		for(int sn=0; sn<superNodes->getLength(); sn++)
		{
			currentSn=dynamic_cast< xercesc::DOMElement* >(superNodes->item(sn));

			nodes=getNodeList(currentSn, "position");
			for(int n=0; n<nodes->getLength(); n++)
			{
				addVertex(
					getFloatAttribute(nodes->item(n), "x"), 
					getFloatAttribute(nodes->item(n), "y"), 
					getFloatAttribute(nodes->item(n), "z")
					);
			}

			nodes=getNodeList(currentSn, "normal");
			for(int n=0; n<nodes->getLength(); n++)
			{
				addNormal(
					getFloatAttribute(nodes->item(n), "x"), 
					getFloatAttribute(nodes->item(n), "y"), 
					getFloatAttribute(nodes->item(n), "z")
					);
			}
		}

		superNodes=getNodeList(xmlDoc, "submeshes");
		for(int sn=0; sn<superNodes->getLength(); sn++)
		{
			currentSn=dynamic_cast< xercesc::DOMElement* >(superNodes->item(sn));

			nodes=getNodeList(currentSn, "face");
			for(int n=0; n<nodes->getLength(); n++)
			{
				addTriangle(
					getIntAttribute(nodes->item(n), "v1"), 
					getIntAttribute(nodes->item(n), "v2"), 
					getIntAttribute(nodes->item(n), "v3")
					);
			}
		}

		superNodes=getNodeList(xmlDoc, "boneassignments");
		for(int sn=0; sn<superNodes->getLength(); sn++)
		{
			currentSn=dynamic_cast< xercesc::DOMElement* >(superNodes->item(sn));

			nodes=getNodeList(currentSn, "vertexboneassignment");
			for(int n=0; n<nodes->getLength(); n++)
			{
				addBoneAssignement(
					getIntAttribute(nodes->item(n), "vertexindex"), 
					getIntAttribute(nodes->item(n), "boneindex"), 
					getFloatAttribute(nodes->item(n), "weight")
					);
			}
		}

		delete parser;
		XMLPlatformUtils::Terminate();

		return true;
	}

	void Mesh::addBoneAssignement(int vertex, int bone, float weight)
	{
		if(vertex >=boneWeights.size())
		{
			boneWeights.resize(vertex+1);
			for(int i=0; i<boneWeights.size(); i++)
				if(!boneWeights[i])
					boneWeights[i]=new VertexBones();
		}
		boneWeights[vertex]->addBone(bone, weight);
	}
}