#include "CKernel.hpp"
#include <chrono>
#include <memory>
#include <string>
#include "../../Configurations/Configuration/CConfiguration.hpp"
#include "../../Modules/Module/CModuleLoader.hpp"
#include "../Component/Graphics/CTestRenderer.hpp"

//CKernel* CKernel::m_kernel;
//CShader* CKernel::m_mainShader;

// #define NAME "Cosmic Engine"
// #define LOGO "../assets/spaceEngineIcon.png"
// #define SPLASH "../assets/shrekt.png"
// #define SPLASH_HEIGHT 512
// #define SPLASH_WIDTH 512
// #define GAME_HEIGHT 360
// #define GAME_WIDTH 640

namespace CosmicCore {

	CKernel::CKernel(std::string name, std::string gameName) :
		m_gameName(gameName)
	{
		//m_kernel = this;
		//CContext::init(SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC,IMG_INIT_PNG | IMG_INIT_JPG);
		m_activeScene = std::make_unique<CScene>("Scene1");
	}

	CKernel::~CKernel()
	{
		/*for(std::pair<const std::string, CScene*>& scenePair : m_sceneList)
		{
			delete scenePair.second;
		}*/
	}

	/*void CKernel::setContext(SDL_GLContext context)
	{
		m_GLcontext = context;
	}*/

	/*CScene* CKernel::getActiveScene()
	{
		return m_activeScene;
	}*/

	/*void CKernel::initGL()
	{
		m_window.setCursorGrabbed(false);
		m_window.setAntiAliasing(m_config.getMsaa());
		m_window.initialization();
		m_GLcontext = m_window.getOpenGLContext();
		SDL_GL_MakeCurrent(m_window.getWindow(), m_GLcontext);
		m_window.setVisible(true);
		SDL_GL_SetSwapInterval(1);
	}*/

	void CKernel::start(bool isPreview) {

		//DEBUG_LOG(trace, "Kernel", "Context", "Starting " + m_gameName + "...")
		CConfiguration::init();
		//CComponentFactory::registerStandardProvidedAbstractComponents();
		std::cout << CModuleLoader::loadAll() << std::endl;
		if(!isPreview)
		{
			// Open the splash window.
			//DEBUG_LOG(trace, "Kernel", "Context", "Loading splash window...")
			//m_loadingWindow.setVisible(true);
			//m_loadingWindow.initialization();

			// Load the config file.
			//m_engineConfig.load();
			//m_config.load();

			// Load the main window.
			/*DEBUG_LOG(trace, "Kernel", "Context", "Loading main window...")
			m_window.setAntiAliasing(m_config.getMsaa());
			m_window.initialization();
			m_window.setCursorGrabbed(m_config.getFullscreen());
			m_GLcontext = m_window.getOpenGLContext();
			SDL_GL_SetSwapInterval(1);
			m_window.setFullscreen(m_config.getFullscreen());
			if (m_config.getFullscreen()) {
				m_window.setSize(m_config.getFullScreenSize());
				glViewport(0, 0, m_config.getFullScreenSize().first, m_config.getFullScreenSize().second);
			}
			else {
				m_window.setSize(m_config.getWindowSize());
				glViewport(0, 0, m_config.getWindowSize().first, m_config.getWindowSize().second);
			}*/
		}

		// On initialise GLEW.
		/*DEBUG_LOG(trace, "Kernel", "Context", "GLEW initialization...")
		GLenum initialisationGLEW(glewInit());

		if (initialisationGLEW != GLEW_OK) {
			// On affiche l'erreur grace a la fonction : glewGetErrorString(GLenum code)
			HandleException(CLibException(std::string("Erreur d'initialisation de GLEW : ") + (char*)glewGetErrorString(initialisationGLEW)), true);
		}
		DEBUG_LOG(trace, "Kernel", "Context", "GLEW initialized!")

		try {
			m_shaderManager.create();
			m_shaderManager.compile();
		}
		catch (const CException& exception) {
			HandleException(exception, true);
		}

		m_mainShader = m_shaderManager.get("basicColor");
		
		//double ratio;
		//if (isFullscreen()) {
		//	ratio = ((double)m_fullscreenSize.first) / ((double)m_fullscreenSize.second);
		//}
		//else {
		//	ratio = ((double)m_windowSize.first) / ((double)m_windowSize.second);
		//}
		// m_projection = glm::perspective(m_fov, ratio, 1.0, 100.0);
		// m_modelView = glm::mat4(1.0);

		if(!isPreview)
		{
			m_loadingWindow.setVisible(false);
			m_window.setVisible(true);
		}

		DEBUG_LOG(trace, "Kernel", "Context", m_gameName + " Started !")*/
	}

	void CKernel::loop() {
		while(1)
		{
			int addOrNot = 0;
			std::cout << "Create test renderer to add to the scene via an entity ?" << std::endl;
			std::cin >> addOrNot;
			if(addOrNot == 1)
			{
				auto entity = m_activeScene->addEntity("test1", "testDesc");
				entity.addComponent<CTestRenderer>();
				std::cout << "nb Entities : " << m_activeScene->getNumEntity() << std::endl;
			}
			else if(addOrNot == 2)
			{
				//m_activeScene.get()->getEntityComponentManager().getEntities().clear();
				//m_activeScene.get()->getEntityComponentManager().addComponent<CTestRenderer>(m_activeScene.get()->getEntityComponentManager().getEntities().size()-1, EComponentType::COMPONENT_RENDERER);
			}
			m_activeScene->render();
		}
		/*unsigned int frameRate(1000 / m_config.getTargetedFramerate());
		unsigned int beginIterationTime(0);
		unsigned int endIterationTime(0);
		unsigned int spendedIterationTime(0);
		unsigned int lastFrame = 0;

		// Activation du Depth Buffer
		glEnable(GL_DEPTH_TEST);

		//lancement des scripts
		unsigned int numEntities = m_activeScene->getNumEntity();
		for (unsigned int i = 0; i < numEntities; i++)
		{
			unsigned int numComponents = m_activeScene->getEntity(i)->getComponents(EComponentType::COMPONENT_SCRIPT).size();
			for (unsigned int j = 0; j < numComponents; j++)
			{
				((CAbstractScript*)(m_activeScene->getEntity(i)->getComponents(EComponentType::COMPONENT_SCRIPT)[j]))->start();
			}

		}
		while (!m_finished) {
			// Define the starting time.
			beginIterationTime = SDL_GetTicks();

			// Compute the delta time.
			unsigned int currentFrame = beginIterationTime;
			m_deltaTime = currentFrame - lastFrame;
			lastFrame = currentFrame;

			// Update event system.
			m_inputHandler.updateEvent();
			// ####### start render #######

			// Nettoyage de l'�cran.
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			// Update des scripts.
			m_activeScene->updateScript();

			// Actualisation du monde physique.
			//todo fonction wrap => pour rendre valide même si c'est pas ODE
			dSpaceCollide(m_activeScene->getTangibleWorld()->getSpace(), m_activeScene->getTangibleWorld(), &(m_activeScene->getTangibleWorld())->callback);
			m_activeScene->getTangibleWorld()->updateWorld(0.12f);


			// Rendu.
			m_activeScene->render();

			// Actualisation de la fen�tre.
			SDL_GL_SwapWindow(m_window.getWindow());

			// ####### end render #######

			// Compute the end and spended time .
			endIterationTime = SDL_GetTicks();
			spendedIterationTime = endIterationTime - beginIterationTime;

			// If need, we pause the programm to have the right fps amount.
			if (spendedIterationTime < frameRate) {
				SDL_Delay(frameRate - spendedIterationTime);
			}
		}
		if(m_isOriginOfContext)
			CContext::quit();*/
	}

	void CKernel::quit() {
		m_finished = true;
	}


	void CKernel::fullscreen() {
		/*if (!m_config.getFullscreen()) {
			m_window.setSize(m_config.getFullScreenSize());
			m_window.setCursorGrabbed(true);
			m_window.setFullscreen(true);
			m_config.setFullscreen(true);
			glViewport(0, 0, m_config.getFullScreenSize().first, m_config.getFullScreenSize().second);
		}
		else {
			m_window.setSize(m_config.getWindowSize());
			m_window.setCursorGrabbed(true);
			m_window.setFullscreen(false);
			m_config.setFullscreen(false);
			glViewport(0, 0, m_config.getWindowSize().first, m_config.getWindowSize().second);
		}
		// Sauvegarde la nouvelle conf.
		m_config.save();*/
	}

	void CKernel::desktop() {
		/*if (m_config.getFullscreen()) {
			m_window.setSize(m_config.getWindowSize());
			m_window.setCursorGrabbed(false);
			m_window.setFullscreen(false);
			m_config.setFullscreen(false);
			glViewport(0, 0, m_config.getWindowSize().first, m_config.getWindowSize().second);
		}
		m_window.setCursorGrabbed(false);*/
	}

	void CKernel::focus() {
		//m_window.setCursorGrabbed(true);
	}

}

