#include "GL/glew.h"

#include "RenderWindow.h"
#include "RenderDevice.h"
#include "FontManager.h"
#include "MeshManager.h"
#include "TextureManager.h"
#include "ShaderManager.h"
#include "ModelManager.h"
#include "TextureImage.h"
#include "ScriptSystem.h"
#include "ScriptSystem_Font.h"
#include "ScriptSystem_Image.h"
#include "ScriptSystem_Math.h"
#include "ScriptSystem_RigidBody.h"
#include "RigidBodySimulation.h"

#include "Camera.h"
#include "SceneNode.h"

#include "Utilities/CfgParser.h"
#include "Utilities/Log.h"
#include "Utilities/Kernel.h"

#include "physfs.h"

using namespace BlueCore;

void initializePhysfs(char* program)
{
	// setup physfs
	PHYSFS_init(program);

	std::string appdir = PHYSFS_getUserDir();
	appdir += ".bluecore";

	if ( !PHYSFS_setWriteDir(appdir.c_str()) )
	{
		if ( (PHYSFS_setWriteDir(PHYSFS_getUserDir()))
				&& (PHYSFS_mkdir(".bluecore")))
			PHYSFS_setWriteDir(appdir.c_str() );
	}

	PHYSFS_addToSearchPath(appdir.c_str(), 0);
	PHYSFS_addToSearchPath("data", 1);

	char **rc = PHYSFS_enumerateFiles("");

	for (char **i = rc; *i != 0; i++)
	{
		std::string filename( *i);

		if (filename.substr(filename.size() - 4, 4) == ".zip")
		{
			PHYSFS_addToSearchPath(( "data/" + filename ).c_str(), 1);
			clog << ">>> Using addon: "<< filename << endlog;
		}
	}

	PHYSFS_freeList(rc);
}

void shutdownPhysfs()
{
	PHYSFS_deinit();
}

int main(int argc, char **argv)
{
	initializePhysfs(argv[0]);

	CfgParser cfg;
	cfg.parseFile("options.cfg");

	int width = cfg.get("width", 640);
	int height = cfg.get("height", 480);
	bool fullscreen = cfg.get("fullscreen", false);
	ref_ptr<RenderWindow> window = new RenderWindow();
	window->create(width, height, 0, 0, 0, fullscreen);

	ref_ptr<RenderDevice> device = new RenderDevice(window);
	ref_ptr<FontManager> fontmanager = new FontManager(device);
	ref_ptr<MeshManager> meshmanager = new MeshManager(device);
	ref_ptr<TextureManager> texturemanager = new TextureManager();
	ref_ptr<ScriptSystem> scriptsystem = new ScriptSystem();
	ref_ptr<ShaderManager> shadermanager = new ShaderManager(window);
	ref_ptr<RigidBodySimulation> simulation = new RigidBodySimulation(scriptsystem);
	ref_ptr<ModelManager> modelmanager = new ModelManager (texturemanager, shadermanager, meshmanager);
	ref_ptr<Camera> camera = new Camera();

	/*
	 ShaderProgram prog =
	 shadermanager->loadShaderProgram("ambient_color_emissive");
	 */
	setupScriptSystem_Font(scriptsystem, fontmanager);
	setupScriptSystem_Image(scriptsystem, texturemanager, device);
	setupScriptSystem_Math(scriptsystem);
	setupScriptSystem_RigidBody(scriptsystem, simulation);

	GLfloat mat_specular[] =
	{ 1.0, 1.0, 1.0, 1.0 };
	GLfloat mat_shininess[] =
	{ 2.0 };
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_specular);
	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_specular);
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);

	if (window.valid() && device.valid())
	{
		scriptsystem->loadScript("main");

		camera->setFoV(90.0);
		camera->setAspectRatio((double)width/(double)height);
		camera->setNearPlane(1.0);
		camera->setFarPlane(100.0);
		camera->setPosition(Vector3(0.0, 0.0, 20.0));

		device->setAmbientLight(1.0, 1.0, 1.0);

		//ref_ptr<SceneNode> rootnode(new SceneNode("root node"));

		double _DeltaTime = 0;
		double _LastTime = glfwGetTime();

		ref_ptr<Model> model = modelmanager->loadModel("combat");
		scriptsystem->callFunction("Initialize");

		clog << "--- starting main loop..."<< endlog;

		while (window->isOpen())
		{
			device->clear();
			double time = glfwGetTime();
			_DeltaTime = time - _LastTime;
			_LastTime = time;

			// device->setViewMatrix();
			// device->setProjectionMatrix();
			camera->setupProjectionMatrix();
			camera->setupViewMatrix();

			// device->useShader (program);
			// device->setTexture (stage, name, texture)
			// device->clearTextures (stage+1);
			//glEnable (GL_TEXTURE_2D);
			//glEnable (GL_LIGHTING);
			//glBindTexture (GL_TEXTURE_2D, texture->getId() );

			// device->
			model->render();

			simulation->saveStates();
			simulation->updateSteps(_DeltaTime);
			while (simulation->step())
				;
			scriptsystem->callFunction("OnFrame", _DeltaTime);

			window->swap();
		}

		scriptsystem->callFunction("Shutdown");

		clog << "--- main loop finished..."<< endlog;
	}

	shutdownPhysfs();
}
