#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "include/ShaderProgram.h"
#include "stb_image/stb_image.h"
#include "include/GLTexture.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#define WINDOW_HEIGHT 600
#define WINDOW_WIDTH 800

void resizeWindowCallback(GLFWwindow*, int, int);
void DrawTriangle(uint32_t, uint32_t);
void DrawComplex(uint32_t, uint32_t, uint32_t);
void SetWindowColor(float, float, float, float);

const glm::mat4 identity(1.f);

int main() {
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGl 3.3
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Opengl 3.3
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Hello Window!", nullptr, nullptr);

	if (window == nullptr) {
		std::cerr << "Failed to create window!!";
		glfwTerminate();
		return 1;
	}

	glfwMakeContextCurrent(window);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cerr << "Failed to init glad !!";
		return 1;
	}

	glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
	glfwSetFramebufferSizeCallback(window, resizeWindowCallback);

	ShaderProgram shaderProgram("./res/shaders/vs.shader", "./res/shaders/fs.shader");
	
	const float rectangleVertecies[] = {
		 // Coordinates    // Colors        // Text coordinates
		 0.5f, 0.5f, 0.f,  1.f, 0.f, 0.f,   1.f, 1.f,
		 0.5f,-0.5f, 0.f,  0.f, 1.f, 0.f,   1.f, 0.f,
		-0.5f,-0.5f, 0.f,  0.f, 0.f, 1.f,   0.f, 0.f,
		-0.5f, 0.5f, 0.f,  1.f, 1.f, 0.f,   0.f, 1.f,
	};

	const uint32_t rectangleIndexes[] = {
		0, 1, 3,
		1, 2, 3
	};

	uint32_t VAO;
	glGenVertexArrays(1, &VAO);
	glBindVertexArray(VAO);

	uint32_t VBO;
	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(rectangleVertecies), rectangleVertecies, GL_STATIC_DRAW);
	//glBindBuffer(GL_ARRAY_BUFFER, 0);

	uint32_t EBO;
	glGenBuffers(1, &EBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(rectangleIndexes), rectangleIndexes, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);
	glEnableVertexAttribArray(2);

	// Unbind
	//glBindVertexArray(0);
	//glBindBuffer(GL_ARRAY_BUFFER, 0);
	//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	GLTexture wood("./res/textures/container.jpg", GL_TEXTURE_2D, GL_TEXTURE0, GL_RGB, GL_RGB);
	
	stbi_set_flip_vertically_on_load(true);
	GLTexture happyFace("./res/textures/awesomeface.png", GL_TEXTURE_2D, GL_TEXTURE1, GL_RGB, GL_RGBA);
	stbi_set_flip_vertically_on_load(false);


	shaderProgram.Use();
	shaderProgram.SetInt("texture0", 0);
	shaderProgram.SetInt("texture1", 1);

	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	float currMixPercent = 0.f;

	// Render Loop
	while (!glfwWindowShouldClose(window)) {
		// Key inputs
		if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
			glfwSetWindowShouldClose(window, true);
		}

		if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
			currMixPercent += 0.01f;
			shaderProgram.SetFloat("mixPercent", currMixPercent);
		}

		if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
			currMixPercent -= 0.01f;
			shaderProgram.SetFloat("mixPercent", currMixPercent);
		}

		// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		
		glm::mat4 containerTranslate = identity;
		containerTranslate = glm::translate(containerTranslate, glm::vec3(0.5f, -0.5f, 0.f));
		containerTranslate = glm::rotate(containerTranslate, (float)glfwGetTime(), glm::vec3(0.f, 0.f, 1.f));

		shaderProgram.SetMat4("transform", containerTranslate);

		DrawComplex(VAO, VBO, EBO);
		
		glfwPollEvents();
		glfwSwapBuffers(window);
	}

	glfwTerminate();
	return 0;
}

void resizeWindowCallback(GLFWwindow* window, int width, int height) {
	glViewport(0, 0, width, height);
}

void DrawTriangle(uint32_t VAO, uint32_t VBO) {
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBindVertexArray(VAO);
	glDrawArrays(GL_TRIANGLES, 0, 3);
	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void DrawComplex(uint32_t VAO, uint32_t VBO, uint32_t EBO) {
	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

void SetWindowColor(float r, float g, float b, float a){
	glClearColor(r, g, b, a);
	glClear(GL_COLOR_BUFFER_BIT);
}