#include "CGCloth.h"
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

//
// FUNCIÓN: CGCloth::CGCloth()
//
// PROPÓSITO: Constructor de la figura
//
CGCloth::CGCloth(GLfloat width, GLfloat height)
{
	K = 0.4f;                   // Wavenumber 
	Velocity = 4.0f;            // Wave's velocity 
	Amp = 5.0f;                 // Wave's amplitude

	int numRows = 101;
	int numColumns = 101;

	numVertices = numRows * numColumns;
	numFaces = 2 * (numRows - 1) * (numColumns - 1);

	indexes = new GLushort[numFaces * 3];  // Array of indexes 
	vertices = new GLfloat[3 * numVertices];  // Array of vertices

	for (int i = 0; i < numRows; i++)
		for (int j = 0; j < numColumns; j++)
		{
			vertices[3 * (i * numRows + j)] = 2 * width * i / (numRows - 1) - width;
			vertices[3 * (i * numRows + j) + 1] = 0.0f;
			vertices[3 * (i * numRows + j) + 2] = 2 * height * j / (numColumns - 1) - height;
		}
	for (int i = 0; i < numRows - 1; i++)
		for (int j = 0; j < numColumns - 1; j++)
		{
			indexes[6 * (i * (numColumns - 1) + j)] = i * numColumns + j;
			indexes[6 * (i * (numColumns - 1) + j) + 1] = i * numColumns + j + 1;
			indexes[6 * (i * (numColumns - 1) + j) + 2] = (i + 1) * numColumns + j;

			indexes[6 * (i * (numColumns - 1) + j) + 3] = (i + 1) * numColumns + j;
			indexes[6 * (i * (numColumns - 1) + j) + 4] = i * numColumns + j + 1;
			indexes[6 * (i * (numColumns - 1) + j) + 5] = (i + 1) * numColumns + j + 1;
		}

	InitBuffers();
}

//
// FUNCIÓN: CGCloth::~CGCloth()
//
// PROPÓSITO: Destructor de la figura
//
CGCloth::~CGCloth()
{
	if (vertices != NULL) delete[] vertices;
	if (indexes != NULL) delete[] indexes;

	// Delete vertex buffer objects
	glDeleteBuffers(2, VBO);
	glDeleteVertexArrays(1, &VAO);
}

//
// FUNCIÓN: CGCloth::InitBuffers()
//
// PROPÓSITO: Crea el VAO y los VBO y almacena todos los datos
//            en la GPU.
//
void CGCloth::InitBuffers()
{
	// Create the Vertex Array Object
	glGenVertexArrays(1, &VAO);
	glBindVertexArray(VAO);

	// Create the Vertex Buffer Objects
	glGenBuffers(2, VBO);

	// Copy data to video memory
	// Vertex data
	int buffsize = sizeof(GLfloat) * numVertices * 3;
	glBindBuffer(GL_ARRAY_BUFFER, VBO[VERTEX_DATA]);
	glBufferData(GL_ARRAY_BUFFER, buffsize, vertices, GL_STATIC_DRAW);

	// Indexes
	buffsize = sizeof(GLushort) * numFaces * 3;
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO[INDEX_DATA]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffsize, indexes, GL_STATIC_DRAW);

	delete[] vertices;
	delete[] indexes;

	vertices = NULL;
	indexes = NULL;

	glEnableVertexAttribArray(0); // Vertex position
	glBindBuffer(GL_ARRAY_BUFFER, VBO[VERTEX_DATA]);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

	location = glm::mat4(1.0f);
}

//
// FUNCIÓN: CGCloth::SetMaterial(CGMaterial* m)
//
// PROPÓSITO: Asigna el material de la figura
//
void CGCloth::SetMaterial(CGMaterial* mat)
{
	material = mat;
}

//
// FUNCIÓN: CGCloth::ResetLocation()
//
// PROPÓSITO: Asigna la posición inicial de la figura 
//
void CGCloth::ResetLocation()
{
	location = glm::mat4(1.0f);
}

//
// FUNCIÓN: CGCloth::Translate(glm::vec3 t)
//
// PROPÓSITO: Añade un desplazamiento a la matriz de posición de la figura 
//
void CGCloth::Translate(glm::vec3 t)
{
	location = glm::translate(location, t);
}

//
// FUNCIÓN: CGCloth::Rotate(GLfloat angle, glm::vec3 axis)
//
// PROPÓSITO: Añade una rotación a la matriz de posición de la figura 
//
void CGCloth::Rotate(GLfloat angle, glm::vec3 axis)
{
	location = glm::rotate(location, glm::radians(angle), axis);
}

//
// FUNCIÓN: CGCloth::Draw(CGShaderProgram * program, glm::mat4 projection, glm::mat4 view)
//
// PROPÓSITO: Dibuja la figura
//
void CGCloth::Draw(CGShaderProgram* program, glm::mat4 projection, glm::mat4 view)
{
	program->SetUniformF("K", K);
	program->SetUniformF("Velocity", Velocity);
	program->SetUniformF("Amp", Amp);

	glm::mat4 mvp = projection * view * location;
	program->SetUniformMatrix4("MVP", mvp);
	program->SetUniformMatrix4("ViewMatrix", view);
	program->SetUniformMatrix4("ModelViewMatrix", view * location);
	material->SetUniforms(program);

	glBindVertexArray(VAO);
	glDrawElements(GL_TRIANGLES, numFaces * 3, GL_UNSIGNED_SHORT, NULL);
}
