#include "utils/logger.h"
#include "utils/maths.h"
#include "utils/timer.h"
#include "filesystem/filemanager.h"
#include "game/game_app.h"
#include "game/game_object_factory.h"
#include "game/level.h"
#include "game/player.h"
#include "game/main_menu.h"
#include "sound/sound_system.h"
#include "render/render.h"
#include "render/modelmanager.h"
#include "render/render_shared.h"
#include "render/ui.h"
#include "physics/physics_world.h"

GameApp* g_gameApp = nullptr;
GameState g_gameState = GAME_STATE_MAIN_MENU;

#define REGISTER_OBJECT( className ) \
	g_objectFactory->Register( RUNTIME_CLASS( className ), &className::_Construct  )	

void RegisterObjects()
{
	REGISTER_OBJECT( GameObject );
	REGISTER_OBJECT( Player );
	//g_objectFactory->Register( RUNTIME_CLASS( GameObject ), &GameObject::_Construct );
	//g_objectFactory->Register( RUNTIME_CLASS( Player ), &Player::_Construct );
}

GameApp::GameApp() :
	m_bExitRequested(false)
{
}

GameApp::~GameApp()
{
}

void GameApp::Init()
{
	g_systemTimer->Init();

	Logger::init();

	g_fileManager = new FileManager();

	RegisterObjects();

	g_inputSystem = CreateInputSystem(nullptr);

	g_render = new Render();
	g_render->Init();

	g_soundSystem = CreateSoundSystem_DirectSound(g_render->GetRenderWindow());
	g_soundSystem->SetMasterVolume(1.0f);

	glm::vec3 eye = glm::vec3(1.0f);
	glm::vec3 look = glm::vec3(0.0f);
	glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
	g_renderView.view = glm::lookAt(eye, look, up);
	g_renderView.fov = 70.0f;

	for (int i = 0; i < __argc; i++)
	{
		if ( __argv[i] && strcmp( __argv[i], "-skipMenu" ) == 0 )
			g_gameState = GAME_STATE_RUNNING;
	}

	PhysicsInit();

	g_level = new Level();
	g_level->LoadText("content/levels/playertest.txt");

	//g_level = new Level();
	//g_level->LoadText("content/levels/test.txt");
}

void GameApp::Shutdown()
{
	PhysicsShutdown();

	delete g_soundSystem;
	g_soundSystem = nullptr;

	g_render->Shutdown();

	delete g_render;
	g_render = nullptr;

	DestroyInputSystem(g_inputSystem);
	g_inputSystem = nullptr;

	delete g_fileManager;
	g_fileManager = nullptr;

	Logger::shutdown();

	g_systemTimer->Shutdown();
}

const float kDuration = 2.2f;

struct NoScoreGlobals
{
	float time = 0.0f;
	float x = 0.0f;
	float y = 0.0f;
} g_msg_noscore;

void UpdateNoScoreAnimation()
{
	const Vec2 size = { 256.0f, 128.0f };
	
	float alpha = lerp(g_msg_noscore.time, 0.0f, 1.0f);

	uiBeginRender();
	uiSetTextureByName("content/textures/ui/ui_msg_nopoints.png");
	uiDrawRect(Vec2{ g_msg_noscore.x,g_msg_noscore.y }, size, Vec4{ 1.0f, 1.0f, 1.0f, alpha });
	uiEndRender();

	const float speed = 170.0f;
	g_msg_noscore.y += speed * g_systemTimer->GetDelta();
	g_msg_noscore.time += g_systemTimer->GetDelta();
}

void Reset()
{
	g_msg_noscore.x = 0.0f;
	g_msg_noscore.y = 0.0f;
	g_msg_noscore.time = 0.0f;
}

const float kFadeDuration = 2.0f;

struct Fade
{
	float time = 0.0f;
} g_fade;

bool showFade = true;
bool g_hideCursor = false;

void GameApp::UpdateFrame()
{
	// #TODO: Refactor loop
	//  in progress ...

	// Update the timer
	g_systemTimer->Update();

	float deltaTime = g_systemTimer->GetDelta();

	// Update the Input System
	g_inputSystem->Update();

	// Update sound system
	g_soundSystem->Update( deltaTime );

	// Request exit on Q button
	if (g_inputSystem->IsKeyPressed(KEY_Q))
		RequestExit();

	//if (g_inputSystem->IsKeyPressed(KEY_ESCAPE) && g_gameState == GAME_STATE_RUNNING)
	//	g_gameState = GAME_STATE_MAIN_MENU;
	//else if (g_inputSystem->IsKeyPressed(KEY_ESCAPE) && g_gameState == GAME_STATE_MAIN_MENU)
	//	g_gameState = GAME_STATE_RUNNING;

	// Update level if we in running state
//	if ( g_level && g_gameState == GAME_STATE_RUNNING )
		 g_level->Update(deltaTime);

	// Begin of frame
	g_render->BeginFrame();

	// Draw main menu
	//if ( g_gameState == GAME_STATE_MAIN_MENU )
	//{
	//	// Draw BG
	//	uiBeginRender();
	//	uiSetTextureByName("content/textures/ui/ui_main_bg.png");
	//	uiDrawRect(Vec2{ 0.0f, 0.0f }, Vec2{ (float)g_renderView.width,(float)g_renderView.height }, Vec4{1.f, 1.f,1.f,1.f});
	//	uiEndRender();

	//	// Draw new game button
	//	{
	//		// Check for selection
	//		bool selected = buttonIsSelected(&g_startGameButton);
	//		if (selected && g_inputSystem->IsMouseButtonPressed(mouseButton_Left))
	//		{
	//			Msg("GameApp: NEW GAME !!!");
	//			
	//			if (g_level)
	//				delete g_level;

	//			g_level = new Level();
	//			g_level->LoadText("content/levels/playertest.txt");

	//			g_gameState = GAME_STATE_RUNNING;
	//			g_hideCursor = true;
	//		}

	//		float color = lerp((float)selected, 0.8f, 1.0f);

	//		uiBeginRender();
	//		uiSetTextureByName("content/textures/ui/ui_btn_newgame.png");
	//		uiDrawRect(g_startGameButton.pos, g_startGameButton.size, Vec4{ color,color,color,1.f });
	//		uiEndRender();
	//	}

	//	// Draw quit button
	//	
	//	{
	//		// Check for selection
	//		bool selected = buttonIsSelected(&g_quitButton);
	//		if (selected && g_inputSystem->IsMouseButtonPressed(mouseButton_Left))
	//		{
	//			RequestExit();
	//			Msg("GameApp: Requested exit");
	//		}

	//		float color = lerp((float)selected, 0.8f, 1.0f);

	//		uiBeginRender();
	//		uiSetTextureByName("content/textures/ui/ui_btn_quit.png");
	//		uiDrawRect(g_quitButton.pos, g_quitButton.size, Vec4{ color,color,color,1.f });
	//		uiEndRender();
	//	}
	//}
	//else if (g_gameState == GAME_STATE_RUNNING)
	{
		// Render level

		if (g_level)
			g_level->Render();
	}

	// Draw cursor
	if (!g_hideCursor)
	{
		int32_t x = 0, y = 0;
		g_inputSystem->GetMousePosition(&x, &y);

		uiBeginRender();
		uiSetTextureByName("content/textures/ui/ui_cursor.png");
		uiDrawRect(Vec2{ (float)x,(float)y }, Vec2{ 16.0f,35.0f }, Vec4{ 1.f, 1.f,1.f,1.f });
		uiEndRender();
	}

	// End of frame
	g_render->EndFrame();
#if 0
	//glm::vec3 eye = glm::vec3(5.0f);
	//glm::vec3 look = glm::vec3(0.0f);
	//glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
	//g_renderView.view = glm::lookAt(eye, look, up);

	g_systemTimer->Update();

//	g_camera.look = glm::vec3(0.0f, 0.0f, 10.0f);

	const float cameraSpeed = 4.0f;

	glm::vec3 dir = glm::normalize( g_camera.look );
//	dir.y = 0.0f;

	if (g_inputSystem->IsKeyPressed(KEY_W))
		g_camera.eye += cameraSpeed * dir * g_systemTimer->GetDelta();
	if (g_inputSystem->IsKeyPressed(KEY_S))
		g_camera.eye -= cameraSpeed * dir * g_systemTimer->GetDelta();
	if (g_inputSystem->IsKeyPressed(KEY_A))
		g_camera.eye -= cameraSpeed * g_systemTimer->GetDelta() * glm::normalize(glm::cross(dir, glm::vec3(0.0f, 1.0f, 0.0f)));
	if (g_inputSystem->IsKeyPressed(KEY_D))
		g_camera.eye += cameraSpeed * g_systemTimer->GetDelta() * glm::normalize(glm::cross(dir, glm::vec3(0.0f, 1.0f, 0.0f)));


	if (g_inputSystem->IsMouseButtonPressed(mouseButton_Right))
	{
		int32_t x = 0, y = 0;
		g_inputSystem->GetMousePosition(&x, &y);

		float yaw = (float)x;
		float pitch = (float)-y;
		
		yaw *= 0.2f;
		pitch *= 0.2f;

		glm::vec3 direction = glm::vec3(0.0f);
		direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
		direction.y = sin(glm::radians(pitch));
		direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));

		g_camera.look = direction;
	}

	//Msg("%f %f %f", g_camera.eye.x, g_camera.eye.y, g_camera.eye.z);

	g_renderView.view = glm::lookAt(g_camera.eye, g_camera.eye + g_camera.look, glm::vec3(0.0f, 1.0f, 0.0f));

	if (g_inputSystem->IsKeyPressed(KEY_Q))
		RequestExit();

	if (g_inputSystem->IsKeyPressed(KEY_E))
		g_renderView.fov -= 0.5f;
	else if (g_inputSystem->IsKeyPressed(KEY_R))
		g_renderView.fov += 0.5f;

	g_render->BeginFrame();

//	if (g_model)
//		g_model->Draw(g_identityMatrix);

	g_level->Render();

	Vec2 buttonSize = { 100.0f,100.0f };

	int32_t x = 0, y = 0;
	g_inputSystem->GetMousePosition(&x, &y);

	Vec2 pos = { 100.0f, 100.0f };
	bool inFocus = GetBoxIntersection2D(Vec2{ (float)x + 15.0f, (float)y + 35.0f }, Vec2{ (float)x,(float)y },
		Vec2{ pos.x + buttonSize.x, pos.y + buttonSize.y }, pos);

	static bool updateAni = false;
	static float updateAniTime = 0.0f;

	//stopRotate = inFocus && g_inputSystem->IsMouseButtonPressed(mouseButton_Left);

	if (inFocus && g_inputSystem->IsMouseButtonPressed(mouseButton_Left))
	{
		updateAniTime = 0.0f;
		Reset();
		updateAni = true;
	}

	// Draw button

	float color = lerp((float)inFocus, 0.8f, 1.0f);

	uiBeginRender();
	uiSetTextureByName("content/textures/ui/ui_new_unit.png");
	uiDrawRect(pos, buttonSize, Vec4{ color, color, color, 1.f });
	uiEndRender();

	if ( updateAni && updateAniTime <= kDuration )
	{
		UpdateNoScoreAnimation();
		updateAniTime += g_systemTimer->GetDelta();
	}
	else if (updateAni && updateAniTime >= kDuration)
	{
		updateAniTime = 0.0f;
		updateAni = false;
		g_msg_noscore.x = 0.0f;
		g_msg_noscore.y = 0.0f;
		g_msg_noscore.time = 0.0f;
	}

	if (showFade && g_fade.time <= kFadeDuration)
	{
		uiBeginRender();
		uiSetTexture(nullptr);
		uiDrawRect(Vec2{ 0.f, 0.f }, Vec2{ (float)g_renderView.width, (float)g_renderView.height }, Vec4{ 0.0f, 0.0f, 0.0f, lerp(g_fade.time, 1.0f, 0.0f) });
		uiEndRender();

		g_fade.time += g_systemTimer->GetDelta();
	}

	// draw cursor

	uiBeginRender();
	uiSetTextureByName("content/textures/ui/ui_cursor.png");
	uiDrawRect(Vec2{ (float)x,(float)y }, Vec2{ 16.0f,35.0f }, Vec4{ 1.f, 1.f,1.f,1.f });
	uiEndRender();

	g_render->EndFrame();
#endif
}
