// Glew always first
#define GLEW_STATIC  // Not in tutorial, but they built it differently; causes linker errors without
#include <GL/glew.h>
#include <GL/glfw.h>
#include <iostream>
#include "Input.h"
#include "Time.h"
#include "Hub.h"
#include "Game.h"
#include "Model_Data.h"
#include "Texture_Data.h"
#include "Utils.h"
using namespace GlobalProperties;

// For the debug window
#define WINDOWS

#ifdef WINDOWS
#include <Windows.h>
#endif

// Globals
PerspectiveCamera gCamera;
Shaders::AmbDifSpecShader gADSShader;

// Predeclare
void Update();
void HandleMouseCallback(int aX, int aY);

int InitGL()
{
	// Init glfw before using it
	if (!glfwInit())
	{
		std::cerr << "GLFWInit Failed!" << std::endl;
		return 0;
	}

	glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4); // 4x antialiasing
	glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 4); // Want version 3.3 but 4.2 is supported
	glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 2); 
	glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Not the old OpenGL

	// Now, open up a window
	if (!glfwOpenWindow(SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 0, 32, 0, GLFW_WINDOW))
	{
		std::cerr << "GLFWOpenWindow Failed!" << std::endl;
		glfwTerminate();
		return 0;
	}

	// Init GLEW
	glewExperimental = true; // Needed in the core profile
	if (glewInit() != GLEW_OK)
	{
		std::cerr << "GLEWInit Failed!" << std::endl;
		return 0;
	}

	return 1;
}

int Initialize()
{
	if (!InitGL())
		return -1;

#ifdef WINDOWS
	// Move console window
	HWND h = GetConsoleWindow();
	MoveWindow(h, SCREEN_WIDTH + 32, 0, SCREEN_WIDTH_HALF, SCREEN_HEIGHT, TRUE);
#endif

	
	// Init my stuff
	Geo::Models::InitModels();
	Textures::InitTextures();
	Input::InputManager::I();
	Hub::I().SetCamera(&gCamera);
	Hub::I().SetShader(&gADSShader);
	glfwSetMousePosCallback(HandleMouseCallback);
	gADSShader.Init();
	gADSShader.Activate();
	GLUtils::Time::Init();
	glLineWidth(GlobalProperties::LINE_DRAW_THICKNESS);

	return 0;
}

void HandleMouseCallback(int aX, int aY)
{
	Input::InputManager::I().UpdateMousePos(aX, aY);
	gCamera.UpdateMouseRot(aX, aY);
}

void Update()
{
	// Get delta time
	GLUtils::Time::Update();
	double deltaTime = GLUtils::Time::GetDeltaTime();
	if (LOG_FPS) GLUtils::CalculateFrameRate();

	// Update shader
	gADSShader.Activate();
	gADSShader.UpdateLights();
	gADSShader.UpdateCamData();

	// Update Inputs
	Input::InputManager& im = Input::InputManager::I();
	if (im.IsMouseEnabled())
		im.UpdateMouseButtons();
	if (im.IsKeyboardEnabled())
		im.UpdateKeyboardButtons();

	// Update Camera
	gCamera.UpdateKeyboardCameraMovement(deltaTime);
	gCamera.RecalculateView();
	gCamera.UpdateVP();
}

void CleanUp()
{
	Geo::Models::CleanUpModels();
	Textures::CleanUpTextures();
	gADSShader.CleanUp();
}

void Run(std::string aWindowTitle)
{
	glfwSetWindowTitle(aWindowTitle.c_str());
	glfwEnable(GLFW_STICKY_KEYS); // Held down buttons should still trigger envents
	glClearColor(0.3f, 0.3f, 0.3f, 0.0f); // Background color when color buffer is cleared

	// Z buffer
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS); // The closer gets the render priority

	if (BACKFACE_CULLING)
		glEnable(GL_CULL_FACE);

	Game game;

	do
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		Update();
		game.Update();

		glfwSwapBuffers();
	} 
	while (glfwGetKey(GLFW_KEY_ESC) != GLFW_PRESS && glfwGetWindowParam(GLFW_OPENED));

	game.CleanUp();
	CleanUp();

	glfwTerminate();
}

int main()
{
	if (Initialize())
		return -1;

	Run("The Wall");

	return 0;
}