// __author__ = Juan Carlos De Abreu (jcabreur@gmail.com)

#include "SceneFile.h"

#include "../Scene/scene_nodes.h"
#include "../Scene/scene_inl.h"
#include "../Model/ModelManagement.h"
#include "../Util/Time/TimeManagement.h"

#include "DataTypes.h"
#include "DataStructures.h"

#include "DataMemoryManagement.h"

BEGIN_TEG_NAMESPACE();

void SceneFile::resolve() {
	// Do all the instrucctions.
	const int nInst = static_cast<int> (inst.size());
	for (int i = 0; i < nInst; ++i) { 
		const SmartPointer<IData> tmp = inst[i]->resolve(variables);
		if (tmp != NULL && i == nInst - 1) print(std::vector<SmartPointer<IData>> (1, tmp));
	}
}

void SceneFile::apply() {
	// Ejecutar las instrucciones globales ...
	resolve();
}

void SceneFileManagement::init() {
	// Math Functions.
	REGISTER_FUNCTION("sin", sin);
	REGISTER_FUNCTION("cos", cos);
	REGISTER_FUNCTION("tan", tan);
	REGISTER_FUNCTION("log", log);
	REGISTER_FUNCTION("exp", exp);
	REGISTER_FUNCTION("abs", abs);
	REGISTER_FUNCTION("sqrt", sqrt);
	REGISTER_FUNCTION("rand", rand);

	// Utility functions.
	REGISTER_FUNCTION("print", print);
	REGISTER_FUNCTION("exit", exit);
	REGISTER_FUNCTION("clone", clone);
	REGISTER_FUNCTION("include", include);
	REGISTER_FUNCTION("Timer", timer);

	REGISTER_FUNCTION("TYPE", TYPE);

	REGISTER_FUNCTION("stopAllTimers", stopAllTimers);
	REGISTER_FUNCTION("pauseAllTimers", pauseAllTimers);
	REGISTER_FUNCTION("startAllTimers", startAllTimers);

	REGISTER_FUNCTION("setKeyFunc", setKeyFunc);

	REGISTER_FUNCTION("InitializeRaster", InitializeRaster);
	REGISTER_FUNCTION("IsRasterButtonPressed", IsRasterButtonPressed);
	REGISTER_FUNCTION("SetRasterButtonFunc", SetRasterButtonFunc);
	REGISTER_FUNCTION("GetRasterPosition", GetRasterPosition);
	REGISTER_FUNCTION("GetRasterOrientation", GetRasterOrientation);

	REGISTER_FUNCTION("getMousePos", getMousePos);

	REGISTER_FUNCTION("reset", Reset);

	REGISTER_FUNCTION("help", help);
	REGISTER_FUNCTION("version", version);

	REGISTER_FUNCTION("collisionDetection", collisionDetection);

	// Scene tree functions.
	REGISTER_FUNCTION("translate", translate);
	REGISTER_FUNCTION("scale", scale);
	REGISTER_FUNCTION("rotate", rotate);
	REGISTER_FUNCTION("Group", group);
	REGISTER_FUNCTION("Light", Light);
	REGISTER_FUNCTION("Model", model);

	REGISTER_FUNCTION("Camera", camera);

	REGISTER_FUNCTION("Material", material);
	REGISTER_FUNCTION("getMaterial", getMaterial);

	// Functions to cast a value to a specific type.
	REGISTER_FUNCTION("INT", toInt);
	REGISTER_FUNCTION("BOOL", toBool);
	REGISTER_FUNCTION("FLOAT", toFloat);
	REGISTER_FUNCTION("STRING", toString);

	DataMemoryManagement::GetInstance().init();
}

void SceneFileManagement::release() {
  pathFiles.clear();
  sceneFiles.clear();
  DATAfunctions.clear();

  DataMemoryManagement::GetInstance().release();
}


SmartPointer<IData> SceneFileManagement::getId(const std::tstring& name) {
  std::list<SceneFile>::iterator it = sceneFiles.begin();
  const std::list<SceneFile>::iterator end = sceneFiles.end();
  for (; it != end; ++it) {
    if (it->variables.find(name) != it->variables.end()) {
      return it->variables[name];
    }
  }
  return NULL;
}

void SceneFileManagement::apply(void) {
	proccessingFile = 0;
	sceneFiles.begin()->apply();

	// verificar si hay alguna definicion del arbol inicial en algunos de los archivos.
	const int nFiles = static_cast<int> (sceneFiles.size());

	std::list<SceneFile>::iterator it = sceneFiles.begin();
	std::list<SceneFile>::iterator end = sceneFiles.end();
	for (int i = 0; i < nFiles; ++i, ++it) {
		if (it->variables.find("scene") != it->variables.end()) {
            Scene::GetInstance().set_root(castToSceneNode(it->variables["scene"]));
		}
	}

	PRINT_LOG(TYPE_MESSAGE, _T("BanFa Version 1.0 (%s UCV-LGG)"), __DATE__);

	proccessingFile = 0;
}

SmartPointer<IData> addDataId(const std::string& s) {
	SmartPointer<IData> tmp = new DataID(s);
	ADD_VAR(tmp);
	return tmp;
}

SmartPointer<IData> addFunction(std::string name, IData* d1, IData *d2) {
	SmartPointer<Function> function = new Function();
	
	// add the instruccions only if is not null
	SmartPointer<DataVector> tmp = castToDataVector(d2);
	if(tmp != NULL) function->inst = tmp->value;

	if (d1 != NULL) {
		SmartPointer<DataVector> params = castToDataVector(d1);
		for (int i = 0; i < (int)params->value.size(); ++i) {
			if (params->value[i]->type != IData::DATA_ID) {
				PRINT_LOG(TYPE_ERROR, "error: \'%s\' : function-style initializer appears to be a function definition", name.c_str());
				return NULL;
			}
			function->param.push_back(castToDataID(params->value[i])->value);
		}
	}

	const std::pair<std::string, int> p(name, function->param.size());

	// Search if the function was previews defined..
	const int nFiles = static_cast<int> (SceneFileManagement::GetInstance().sceneFiles.size());
	std::list<SceneFile>::iterator it = SceneFileManagement::GetInstance().sceneFiles.begin();
	std::list<SceneFile>::iterator end = SceneFileManagement::GetInstance().sceneFiles.end();
	
	bool foundDefined = false;
	for (; it != end; ++it) {
		if (it->functions.find(p) != it->functions.end()) {
			it->functions[p] = function;
			foundDefined = true;
			PRINT_LOG(TYPE_WARNING, _T("warning: function \'%s\' with %d parameters redefinition"), name.c_str(), function->param.size());
		}
	}

	if (!foundDefined) {
		it = SceneFileManagement::GetInstance().sceneFiles.begin();
		for (int i = 0; i < nFiles - 1; ++i) ++it; 
		it->functions[p] = function;
	}

	SmartPointer<IData> tmp2 = castToDATA(function);
	ADD_VAR(tmp2);
	return tmp2;
}

SmartPointer<IData> addVector(IData* d) {
	SmartPointer<DataVector> tmp = new DataVector(); tmp->value.push_back(d);
	SmartPointer<IData> tmp2 = castToDATA(tmp);
	ADD_VAR(tmp2);
	return tmp2;
}

SmartPointer<IData> addVector(IData *d1, IData *d2) {
	SmartPointer<DataVector> tmp = castToDataVector(d1); tmp->value.push_back(d2);
	SmartPointer<IData> tmp2 = castToDATA(tmp);
	ADD_VAR(tmp2);
	return tmp2;
}

SmartPointer<IData> addCallFunction(std::string name, IData* d) {
	SmartPointer<DataCallFunction> callFunction = new DataCallFunction(name);

	// Check if the function have parameters
	if (d != NULL) {
		SmartPointer<DataVector> params = castToDataVector(d);
		callFunction->param = params->value;
	}

	SmartPointer<IData> tmp = castToDATA(callFunction);
	ADD_VAR(tmp);
	return tmp;
}

SmartPointer<IData> addIf(IData* d1, IData* d2, IData* d3) {
	// Check if the if have instruccions
	std::vector<SmartPointer<IData>> inst;
	if (d2 != NULL) inst = castToDataVector(d2)->value;

	// Create a DataIf ...
	SmartPointer<DataIf> ifCond = new DataIf(d1, inst, d3);
	SmartPointer<IData> tmp = castToDATA(ifCond);
	ADD_VAR(tmp);
	return tmp;
}

SmartPointer<IData> addFor(IData* d1, IData* d2, IData* d3, IData* d4) {
	// check if the while have instructions
	std::vector<SmartPointer<IData>> inst;
	if (d4 != NULL) inst = castToDataVector(d4)->value;
	
	SmartPointer<DataFor> forStatement = new DataFor(d1, d2, d3, inst);
	SmartPointer<IData> tmp = castToDATA(forStatement);
	ADD_VAR(tmp);
	return tmp;
}

SmartPointer<IData> addWhile(IData* d1, IData* d2) {
	// check if the while have instructions
	std::vector<SmartPointer<IData>> inst;
	if (d2 != NULL) inst = castToDataVector(d2)->value;
	
	// Create a DataWhile ....
	SmartPointer<DataWhile> whileStatement = new DataWhile(d1, inst);
	SmartPointer<IData> tmp = castToDATA(whileStatement);
	ADD_VAR(tmp);
	return tmp;
}

SmartPointer<IData> addOperation(IData* d1, IData* d2, std::string op) {
	SmartPointer<IData> tmp = new DataOperation(d1, d2, op);

	// Se verifica si los tipos son basicpos para realizar la operacion de una vez
	// Y asi optimizar un poco el codigo...
	// TODO: for this optimizations add a flag in the compilation ...
	if (d1->type < 4 && d2->type < 4)
		tmp = tmp->resolve(std::map<std::string, SmartPointer<IData>>());

	ADD_VAR(tmp);
	return tmp;
}

// For operators like - !
SmartPointer<IData> addOperation(IData* d1, std::string op) {
	SmartPointer<IData> tmp = new DataOperation(d1, NULL, op);

	// Check if I can do a optimization here
	if (d1->type < 4) tmp = tmp->resolve(std::map<std::string, SmartPointer<IData>>());

	ADD_VAR(tmp);
	return tmp;
}

SmartPointer<IData> addReturn(IData* d1) {
  SmartPointer<IData> tmp = new DataReturn(d1);
  ADD_VAR(tmp);
  return tmp;
}

SmartPointer<IData> addGlobalInst(IData* d) {
	const int pos = SceneFileManagement::GetInstance().sceneFiles.size() - 1;
	std::list<SceneFile>::iterator it = SceneFileManagement::GetInstance().sceneFiles.begin();
	std::list<SceneFile>::iterator end = SceneFileManagement::GetInstance().sceneFiles.end();
	for (int i = 0; i < pos; ++i) ++it;
	it->inst.push_back(d);

	return NULL;
}

END_TEG_NAMESPACE();
