#include <MeshRenderer.h>

#include <iostream>

// when creating a meshrenderer the important question is "do all these vertices share the same shader?"

MeshRenderer::MeshRenderer()
{
}

MeshRenderer::~MeshRenderer()
{
	glDeleteVertexArrays(1, &this->VAO);
}

void MeshRenderer::Setup
(
	std::vector<float> vertices,
	std::vector<float> normals,
	std::vector<float> colors,
	std::vector<unsigned int> indices,
	Shader* lit_shader
)
{
	this->vertices = vertices;
	this->normals = normals;
	this->colors = colors;
	this->indices = indices;
	this->lit_shader = lit_shader;
	this->shadow_shader = shadow_shader;

	this->SetupBuffers();

	//if (shadow_shader != nullptr)
	//{
	//	//std::cout << "Setup lighting" << std::endl;
	//	this->SetupLighting();
	//}

}

void MeshRenderer::Render(Shader &shader)
{
	//std::cout << "Rendering with Shader: " << shader.ID << std::endl;
	shader.Use();
	glm::mat4 model = glm::mat4(1.0f);
	shader.SetMatrix4("model", model);

	glBindVertexArray(this->VAO);

	//std::cout << this->indices.size() << std::endl;
	glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);

	glBindVertexArray(0);
}

void MeshRenderer::Render
(
	glm::mat4 projection,
	glm::mat4 view,
	glm::vec3 light_position,
	glm::vec3 light_color,
	glm::vec3 view_position
)
{
	glm::mat4 model = glm::mat4(1.0f);

	//glEnable(GL_CULL_FACE);
	//glCullFace(GL_BACK);

	//if (shadow_shader != nullptr)
	//{
	//	this->shader = shadow_shader;
	//	RenderShadows(projection, view, model, light_position);
	//}

	this->shader = lit_shader;
	RenderObject(projection, view, model, light_position, light_color, view_position);
}

void MeshRenderer::Render
(
	glm::mat4 projection,
	glm::mat4 view, 
	glm::vec3 light_position,
	glm::vec3 light_color,
	glm::vec3 view_position,
	glm::vec3 position,
	glm::vec3 scale,
	glm::vec3 rotation
)
{
	glm::mat4 model = glm::mat4(1.0f);
	model = glm::translate(model, position);
	model = glm::rotate(model, glm::radians(rotation.x), glm::vec3(1, 0, 0));
	model = glm::rotate(model, glm::radians(rotation.y), glm::vec3(0, 1, 0));
	model = glm::rotate(model, glm::radians(rotation.z), glm::vec3(0, 0, 1));
	model = glm::scale(model, scale);

	if (shadow_shader != nullptr)
	{
		this->lit_shader = shadow_shader;
		RenderShadows(projection, view, model, light_position);
	}
	this->shader = lit_shader;

	RenderObject(projection, view, model, light_position, light_color, view_position);
}

void MeshRenderer::SetupBuffers()
{
	/*for (int i = 0; i < this->normals.size(); i += 3)
	{
		std::cout << this->normals[i] << " " << this->normals[i + 1] << " " << this->normals[i + 2] << std::endl;
	}*/
	glGenVertexArrays(1, &this->VAO);

	unsigned int VBO, NBO, CBO, EBO;

	glGenBuffers(1, &VBO);
	glGenBuffers(1, &NBO);
	glGenBuffers(1, &CBO);
	glGenBuffers(1, &EBO);

	glBindVertexArray(this->VAO);

	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(float), this->vertices.data(), GL_STATIC_DRAW);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	glBindBuffer(GL_ARRAY_BUFFER, NBO);
	glBufferData(GL_ARRAY_BUFFER, this->normals.size() * sizeof(float), this->normals.data(), GL_STATIC_DRAW);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(1);

	glBindBuffer(GL_ARRAY_BUFFER, CBO);
	glBufferData(GL_ARRAY_BUFFER, this->colors.size() * sizeof(float), this->colors.data(), GL_STATIC_DRAW);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(2);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->indices.size() * sizeof(unsigned int), this->indices.data(), GL_STATIC_DRAW);

	glBindVertexArray(0);
}

void MeshRenderer::SetupLighting()
{
	/*glGenFramebuffers(1, &depth_map_FBO);
	glGenTextures(1, &depth_map);

	for (unsigned int i = 0; i < 6; i++)
	{
		glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
	}
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

	glBindFramebuffer(GL_FRAMEBUFFER, depth_map_FBO);
	glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_map, 0);
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);
	glBindFramebuffer(GL_FRAMEBUFFER, 0);*/
}

void MeshRenderer::RenderObject
(
	glm::mat4 projection, 
	glm::mat4 view, 
	glm::mat4 model, 
	glm::vec3 light_position, 
	glm::vec3 light_color,
	glm::vec3 view_position
)
{
	this->shader->Use();
	this->shader->SetMatrix4("projection", projection);
	this->shader->SetMatrix4("view", view);
	this->shader->SetMatrix4("model", model);
	this->shader->SetVector3f("lightPos", light_position);
	this->shader->SetVector3f("lightColor", light_color);
	this->shader->SetVector3f("viewPos", view_position);
	this->shader->SetFloat("far_plane", 25.0f);

	glBindVertexArray(this->VAO);

	glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);

	glBindVertexArray(0);
}

void MeshRenderer::RenderShadows(glm::mat4 projection, glm::mat4 view, glm::mat4 model, glm::vec3 light_position)
{
	this->shader->Use();
	this->shader->SetMatrix4("projection", projection);
	this->shader->SetMatrix4("view", view);
	this->shader->SetMatrix4("model", model);
	float near_plane = 1.0f;
	float far_plane = 50.0f;

	glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), (float)SHADOW_WIDTH / (float)SHADOW_HEIGHT, near_plane, far_plane);
	std::vector<glm::mat4> shadowTransforms;
	shadowTransforms.push_back(shadowProj * glm::lookAt(light_position, light_position + glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
	shadowTransforms.push_back(shadowProj * glm::lookAt(light_position, light_position + glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
	shadowTransforms.push_back(shadowProj * glm::lookAt(light_position, light_position + glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)));
	shadowTransforms.push_back(shadowProj * glm::lookAt(light_position, light_position + glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)));
	shadowTransforms.push_back(shadowProj * glm::lookAt(light_position, light_position + glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)));
	shadowTransforms.push_back(shadowProj * glm::lookAt(light_position, light_position + glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)));

	glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
	glBindFramebuffer(GL_FRAMEBUFFER, depth_map_FBO);
	//glClear(GL_DEPTH_BUFFER_BIT);

	for (unsigned int i = 0; i < 6; ++i)
	{
		std::string matrix_name = "shadowMatrices[" + std::to_string(i) + "]";
		this->shader->SetMatrix4(matrix_name.c_str(), shadowTransforms[i]);
	}
	this->shader->SetFloat("far_plane", far_plane);
	this->shader->SetVector3f("lightPos", light_position);

	glBindVertexArray(this->VAO);

	glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);

	glBindVertexArray(0);

	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
	//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glBindTexture(GL_TEXTURE_CUBE_MAP, depth_map);
}


