#include <iostream>
#include <opengl-playground/sdlmainwindow.hpp>
#include <opengl-playground/openglshader.hpp>
#include <opengl-playground/textured-rectangle.hpp>
#include <opengl-playground/globalcanvassettings.hpp>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <opengl-playground/model.hpp>

#include <imgui.h>
#include <imgui_impl_sdl.h>
#include <imgui_impl_opengl3.h>

#define WINDOW_WIDTH 1200
#define WINDOW_HEIGHT 800

int main(int argc, char **argv)
{
	(void)argc;
	(void)argv;
	SdlMainWindow *window;
	bool run = true;
	glm::mat4 projection_matrix;
	glm::vec4 color = glm::vec4(1.0f);
	float fov_angle = 45.0f;


	window = new SdlMainWindow(WINDOW_HEIGHT, WINDOW_WIDTH);

	window->show();
	window->activateGlContext();

	/* Clear the image and set the background to a nice gray */
	glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
	glClearColor(0.1f, 0.1f, 0.1f, 0.f);
	glClear(GL_COLOR_BUFFER_BIT);

	projection_matrix = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, -10.0f, 10.0f);
	GlobalCanvasSettings::setProjectionMatrix(projection_matrix);
	GlobalCanvasSettings::setViewMatrix(glm::mat4(1.0f));

	// Setup Dear ImGui context
	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO(); (void)io;
	//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
	//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls

	// Setup Dear ImGui style
	ImGui::StyleColorsDark();
	//ImGui::StyleColorsClassic();

	// Setup Platform/Renderer bindings
	ImGui_ImplSDL2_InitForOpenGL(window->getWindow(), window->getContext());
	ImGui_ImplOpenGL3_Init("#version 330");

	auto shader = std::make_shared<OpenGlShaderProgram>("shaders/mesh-vertex.glsl", "", "shaders/mesh-fragment.glsl");
	shader->compile();
	Model m("nanosuit/nanosuit.obj", glm::vec4(1.0f, 0.0f, 0.0f, 1.0f), shader);

	Model coords("coordinate-system/coordsys.obj", glm::vec4(1.0f, 0.0f, 0.0f, 1.0f), shader);

	coords.realize();
	m.realize();
	m.setModelMatrix(glm::scale(glm::mat4(1.0f), glm::vec3(0.2f, 0.2f, 0.2f)));

	glEnable(GL_DEPTH_TEST);

	while (run) {

		SDL_Event event;
		while (SDL_PollEvent(&event))
		{
			ImGui_ImplSDL2_ProcessEvent(&event);
			if (event.type == SDL_QUIT)
				run = false;
			if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window->getWindow()))
				run = false;

			if (event.type == SDL_MOUSEWHEEL) {
				if (event.wheel.y > 0) {
					fov_angle -= 0.1;
					if (fov_angle <= 1.0f)
						fov_angle = 1.0f;
				} else if (event.wheel.y < 0) {
					fov_angle += 0.1;
					if (fov_angle >= 60.0f)
						fov_angle = 60.0f;
				}
			}
		}


		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


		// Start the Dear ImGui frame
		ImGui_ImplOpenGL3_NewFrame();
		ImGui_ImplSDL2_NewFrame(window->getWindow());
		ImGui::NewFrame();


		ImGui::Begin("Camera");

		static int e = 1;
		ImGui::RadioButton("Orthographic", &e, 0); ImGui::SameLine();
		ImGui::RadioButton("Perspective", &e, 1);

		static float view_z = 10.0f, view_x = 0.0f, view_y = 0.0f;
		ImGui::SliderFloat("Camera x", &view_x, -20.0f, 20.0f);
		ImGui::SliderFloat("Camera y", &view_y, -20.0f, 20.0f);
		ImGui::SliderFloat("Camera z", &view_z, 0.0f, 20.0f);
		ImGui::SliderFloat("Camera FOV", &fov_angle, 1.0f, 60.0f);
		ImGui::Text("Framerate: %.01f Hz", io.Framerate);

		GlobalCanvasSettings::setCameraPosition(glm::vec3(view_x, view_y, view_z));
		const glm::mat4 view = glm::lookAt(GlobalCanvasSettings::getCameraPosition(),
				   glm::vec3(view_x, view_y, 0.0f),
				   glm::vec3(0.0f, 1.0f, 0.0f));

		GlobalCanvasSettings::setViewMatrix(view);

		if(!e) {
			//GlobalCanvasSettings::setProjectionMatrix(glm::mat4(1.0f));
			GlobalCanvasSettings::setProjectionMatrix(glm::ortho(-5.0f, 5.0f, -5.0f, 5.0f, -5.0f, 5.0f));
		} else {

			GlobalCanvasSettings::setProjectionMatrix(glm::perspective(glm::radians(fov_angle), (float)1200/(float)800, 0.2f, 30.0f));
		}
		ImGui::End();

		{
		ImGui::Begin("Model Matrix");
		glm::mat4 mat = m.getModelMatrix();
		static float x_pos = 0.0f;
		static float y_rot = 0.0f, z_rot =0.0f, x_rot = 0.0f;
		ImGui::Text("%.2f %.2f %.2f %.2f", mat[0][0], mat[1][0], mat[2][0], mat[3][0]);
		ImGui::Text("%.2f %.2f %.2f %.2f", mat[0][1], mat[1][1], mat[2][1], mat[3][1]);
		ImGui::Text("%.2f %.2f %.2f %.2f", mat[0][2], mat[1][2], mat[2][2], mat[3][2]);
		ImGui::Text("%.2f %.2f %.2f %.2f", mat[0][3], mat[1][3], mat[2][3], mat[3][3]);
		ImGui::SliderFloat("X Pos", &x_pos, -5, 5);
		ImGui::SliderFloat("X Rotation", &x_rot, -180.0f, 180.0f);
		ImGui::SliderFloat("Y Rotation", &y_rot, -180.0f, 180.0f);
		ImGui::SliderFloat("Z Rotation", &z_rot, -180.0f, 180.0f);
		mat = glm::scale(glm::mat4(1.0f), glm::vec3(0.2f, 0.2f, 0.2f));
		mat = glm::rotate(mat, glm::radians(x_rot), glm::vec3(1.0f, 0.0f, 0.0f));
		mat = glm::rotate(mat, glm::radians(y_rot), glm::vec3(0.0f, 1.0f, 0.0f));
		mat = glm::rotate(mat, glm::radians(z_rot), glm::vec3(0.0f, 0.0f, 1.0f));
		mat = glm::translate(glm::mat4(1.0), glm::vec3(x_pos, 0.0f, 0.0f)) * mat;
		m.setModelMatrix(mat);
		ImGui::End();
		}
		{
		ImGui::Begin("Normal Matrix");
		glm::mat4 mat = m.getNormalMatrix();
		ImGui::Text("%.2f %.2f %.2f %.2f", mat[0][0], mat[1][0], mat[2][0], mat[3][0]);
		ImGui::Text("%.2f %.2f %.2f %.2f", mat[0][1], mat[1][1], mat[2][1], mat[3][1]);
		ImGui::Text("%.2f %.2f %.2f %.2f", mat[0][2], mat[1][2], mat[2][2], mat[3][2]);
		ImGui::Text("%.2f %.2f %.2f %.2f", mat[0][3], mat[1][3], mat[2][3], mat[3][3]);
		ImGui::End();
		}

		m.render();
		coords.render();

		glm::mat4 current_model_matrix = m.getModelMatrix();
		current_model_matrix = glm::translate(glm::mat4(1.0f), glm::vec3(3.0f, 0.0f, 0.0f)) * current_model_matrix;
		m.setModelMatrix(current_model_matrix);

		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		m.render();
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

		ImGui::Render();
		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

		window->swapBuffer();

	}

	ImGui_ImplOpenGL3_Shutdown();
	ImGui_ImplSDL2_Shutdown();
	ImGui::DestroyContext();

	shader->unload();

	delete window;
}
