#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <shader.h>

#include <iostream>
#include <fstream>


void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

int SCR_WIDTH = 800;
int SCR_HEIGHT = 800;

float triangleVertices[] = {
	-0.5f,-0.5f,0.0f,	1.0f,0.0f,0.0f,
	 0.5f,-0.5f,0.0f,	0.0f,1.0f,0.0f,
	 0.0f, 0.5f,0.0f,	0.0f,0.0f,1.0f
};

float squareVertices[] = {
	 -0.8f, 1.0f, 0.0f,	1.0f, 0.0f, 0.0f,
	 -0.8f, 0.8f, 0.0f,	1.0f, 0.0f, 0.0f,
	 -1.0f, 0.8f, 0.0f,	0.0f, 1.0f, 0.0f,
	 -1.0f, 1.0f, 0.0f,	0.0f, 1.0f, 0.0f
};

unsigned int indices[] = {
	0,1,3,
	1,2,3
};

int main() {
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearningOpenGl", NULL, NULL);
	if (window == NULL) {
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	Shader ourShader("vertex.vert", "fragment.frag");

	glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);

	unsigned int VAO_1, VAO_2, VBO_1, VBO_2, EBO;
	glGenVertexArrays(1, &VAO_1);
	glGenVertexArrays(1, &VAO_2);
	glGenBuffers(1, &VBO_1);
	glGenBuffers(1, &VBO_2);
	glGenBuffers(1, &EBO);

	glBindVertexArray(VAO_1);

	glBindBuffer(GL_ARRAY_BUFFER, VBO_1);
	glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);

	glBindVertexArray(VAO_2);

	glBindBuffer(GL_ARRAY_BUFFER, VBO_2);
	glBufferData(GL_ARRAY_BUFFER, sizeof(squareVertices), squareVertices, GL_STATIC_DRAW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
	
	float r = 0.0f, g = 0.0f, b = 0.0f;
	while (!glfwWindowShouldClose(window)) {
		//input
		processInput(window);

		//rendering
		if (r < 1) {
			r = r + 0.01f;
		}
		else if (g < 1) {
			g = g + 0.01f;
		}
		else if (b < 1) {
			b = b + 0.01f;
		}
		else {
			r = 0;
			g = 0;
			b = 0;
		}

		glClearColor(r, g, b, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);

		//transforms
		glm::mat4 defaultTransform = glm::mat4(1.0f);
		glm::mat4 rotatedTransform = glm::rotate(defaultTransform, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));

		//draw
		ourShader.use();
		unsigned int transformLoc = glGetUniformLocation(ourShader.ID, "transform");
		glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(rotatedTransform));
		glBindVertexArray(VAO_1);
		glDrawArrays(GL_TRIANGLES, 0, 3);

		glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(defaultTransform));
		glBindVertexArray(VAO_2);
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

		//check and call
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glfwTerminate();
	return 0;
}

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

void processInput(GLFWwindow* window) {
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}
}