#include "ModelManager.h"

#include "MeshManager.h"
#include "ShaderManager.h"

#include "Utilities/StringUtilities.h"
#include "Utilities/Log.h"

#include "tinyxml.h"
#include "physfs.h"

#include <iostream>

using namespace std;

namespace BlueCore
{

//------------------------------------------------------------------------------
void Model::render()
{
	ModelMesh->render();
	//glEnable(GL_LIGHTING);
	//glDepthFunc ( GL_LEQUAL );
	//glEnable ( GL_DEPTH_TEST );
	//glDepthMask ( GL_TRUE );

	/*
	 glEnableClientState (GL_VERTEX_ARRAY );
	 glEnableClientState (GL_TEXTURE_COORD_ARRAY );
	 glEnableClientState (GL_NORMAL_ARRAY );

	 glMatrixMode (GL_MODELVIEW );
	 glPushMatrix();
	 Matrix4x4 m(_AbsoluteRotation, _AbsoluteTranslation);
	 glMultMatrixd ( ( GLdouble * ) &m.m );

	 mesh->vertex_buffer.bind();
	 mesh->index_buffer.bind();
	 */
	/*
	 glMaterialfv (GL_FRONT, GL_SHININESS, &pass.Shininess);
	 glMaterialfv (GL_FRONT, GL_SPECULAR, ( GLfloat * ) &pass.Specular );

	 // setup shader
	 if (pass.Program && ShaderManager::getSingleton()->usingShaders() )
	 {
	 ShaderManager::getSingleton()->useShaderProgram(pass.Program);

	 int textureId = 0;
	 std::vector<Model::TextureUnit>::iterator titer;

	 for (titer = pass.Textures.begin(); titer != pass.Textures.end(); titer++)
	 {
	 Renderer::getSingleton()->bindTexture(( *titer ).mTexture, textureId );
	 ShaderManager::getSingleton()->useTexture(pass.Program, textureId, ( *titer ).mName);
	 textureId++;
	 }

	 if (pass.Tangents)
	 {
	 ShaderManager::getSingleton()->useTangentBuffer(pass.Program, &mesh->tangent_buffer, "tangent");
	 ShaderManager::getSingleton()->useTangentBuffer(pass.Program, &mesh->bitangent_buffer,
	 "bitangent");
	 }
	 }
	 else
	 {
	 glActiveTextureARB (GL_TEXTURE2_ARB );
	 glDisable (GL_TEXTURE_2D );
	 glActiveTextureARB (GL_TEXTURE1_ARB );
	 glDisable (GL_TEXTURE_2D );
	 int textureId = 0;

	 std::vector<Model::TextureUnit>::iterator titer;
	 for (titer = pass.Textures.begin(); titer != pass.Textures.end(); titer++)
	 {
	 Renderer::getSingleton()->bindTexture(( *titer ).mTexture, textureId );
	 ShaderManager::getSingleton()->useTexture(pass.Program, textureId, ( *titer ).mName);
	 textureId++;
	 break;
	 }
	 }

	 // render subsets
	 SubsetVector::iterator iter;
	 for (iter = mesh->surface_subsets.begin(); iter
	 != mesh->surface_subsets.end(); iter++)
	 {
	 Subset subset = *iter;
	 mesh->index_buffer.draw(subset.first, subset.count);
	 }

	 // cleanup
	 if (pass.Program && ShaderManager::getSingleton()->usingShaders() )
	 {
	 ShaderManager::getSingleton()->useShaderProgram( 0);
	 if (pass.Tangents)
	 {
	 ShaderManager::getSingleton()->disableTangentBuffer(pass.Program, "tangent");
	 ShaderManager::getSingleton()->disableTangentBuffer(pass.Program, "bitangent");
	 }
	 }

	 glPopMatrix();

	 glActiveTextureARB (GL_TEXTURE2_ARB );
	 glDisable (GL_TEXTURE_2D );
	 glActiveTextureARB (GL_TEXTURE1_ARB );
	 glDisable (GL_TEXTURE_2D );
	 glActiveTextureARB (GL_TEXTURE0_ARB );
	 glDisable (GL_TEXTURE_2D );
	 */
	/*
	 glDisableClientState (GL_VERTEX_ARRAY );
	 glDisableClientState (GL_TEXTURE_COORD_ARRAY );
	 glDisableClientState (GL_NORMAL_ARRAY );
	 */

}
//------------------------------------------------------------------------------
ModelManager::ModelManager(TextureManager *texturemanager,
		ShaderManager* shadermanager, MeshManager* meshmanager) :
	_TextureManager(texturemanager), _ShaderManager(shadermanager),
			_MeshManager(meshmanager)
{
}

//------------------------------------------------------------------------------
ModelManager::~ModelManager()
{
}

//------------------------------------------------------------------------------
void ModelManager::parseRenderPassDefinition(
		Model::RenderPassDefinition &Definition,
		const TiXmlElement* DefinitionElement)
{

	const TiXmlElement * TextureElement =
			DefinitionElement->FirstChildElement("Texture");

	while (TextureElement)
	{
		Model::TextureUnit unit;
		unit.mName = TextureElement->Attribute("name");

		const char *file = TextureElement->Attribute("file");

		if (file)
			unit.mTexture = _TextureManager->loadTexture(file);
		else
			unit.mTexture = 0;

		Definition.Textures.push_back(unit);

		TextureElement = TextureElement->NextSiblingElement("Texture");
	}

	const TiXmlElement * ShaderElement =
			DefinitionElement->FirstChildElement("Shader");

	if (ShaderElement)
	{
		Definition.Program
				= _ShaderManager->loadShaderProgram(ShaderElement->Attribute("name") );

		const char *t = ShaderElement->Attribute("tangents");
		if (t && string("true") == t)
			Definition.Tangents = true;
		else
			Definition.Tangents = false;
	}
	else
		Definition.Program = 0;

	const TiXmlElement * MaterialElement =
			DefinitionElement->FirstChildElement("Material");

	if (MaterialElement)
	{
		double s;
		MaterialElement->QueryDoubleAttribute("shininess", &s);
		Definition.Shininess = s;

		std::vector<std::string> components;
		explode(MaterialElement->Attribute("specular"), components);

		for (unsigned int i = 0; i < components.size(); i++)
		{
			Definition.Specular[i] = atof(components[i].c_str() );

			if (i == 3)
				break;
		}

	}

	Definition.Enabled = true;
}

//------------------------------------------------------------------------------
Model *ModelManager::loadModel(const string &name)
{
	// check if this model is already loaded
	ModelContainer::const_iterator result;
	result = _Models.find(name);

	if (result != _Models.end() )
	{
		return result->second.get();
	}

	string filename = name + ".model.xml";
	// load the document
	PHYSFS_file *file = PHYSFS_openRead(filename.c_str());

	if ( !file)
	{
		clog << "!!! XML-File '"<< name << "' not found"<< endline;
		return 0;
	}

	unsigned int fileSize = PHYSFS_fileLength(file);

	Buffer<char> buffer(fileSize);

	PHYSFS_read(file, buffer.data(), 1, buffer.size() );
	buffer[buffer.count() - 1] = 0;

	PHYSFS_close(file);

	TiXmlDocument *document = new TiXmlDocument();
	document->Parse(buffer.data() );

	if (document->Error() )
	{
		clog << "!!! Error loading XML-File'"<< name << "': ";
		clog << document->ErrorRow() << ","<< document->ErrorCol();
		clog << " "<< document->ErrorDesc() << endline;
		return 0;
	}

	Model *model = new Model();

	if (document == 0)
	{
		clog << "!!! Model '"<< name << "' not found!"<< endline;
		return 0;
	}

	const TiXmlElement *model_element = document->FirstChildElement("Model");

	if (model_element)
	{
		const TiXmlElement* MeshElement =
				model_element->FirstChildElement("Mesh");

		if (MeshElement)
		{
			model->ModelMesh
					= _MeshManager->loadMesh(MeshElement->Attribute("file") );
		}

		const TiXmlElement * ShadowMeshElement =
				model_element->FirstChildElement("ShadowMesh");

		if (ShadowMeshElement)
		{
			model->ShadowMesh
					= _MeshManager->loadMesh(ShadowMeshElement->Attribute("file") );
		}
		else
		{
			model->ShadowMesh = 0;
		}

		const TiXmlElement* definition_element;

		definition_element
				= model_element->FirstChildElement("AmbientRenderPass");
		if (definition_element)
			parseRenderPassDefinition(model->AmbientPass, definition_element);
		else
			model->AmbientPass.Enabled = false;

		definition_element = model_element->FirstChildElement("LitRenderPass");
		if (definition_element)
			parseRenderPassDefinition(model->LitPass, definition_element);
		else
			model->LitPass.Enabled = false;

		definition_element
				= model_element->FirstChildElement("DefaultRenderPass");
		if (definition_element)
			parseRenderPassDefinition(model->DefaultPass, definition_element);
		else
			model->DefaultPass.Enabled = false;

	}

	_Models[name] = model;

	return model;
}
#if 0
//------------------------------------------------------------------------------
void ModelManager::unload()
{
	ModelContainer::iterator iter;

	for (iter = _Models.begin(); iter != _Models.end(); iter++)
	{
		clog << ">>> ModelManager: unload " << iter->first << endlog;

		delete iter->second;
	}

	_Models.clear();
}
#endif
} // namespace BlueCore
