#include "Mesh.h"
#include <glm/gtc/type_ptr.hpp>
#include <glm/vec3.hpp>
#include <iostream>

Mesh::Mesh(ShaderProgram* shader, std::size_t size) : GLObject(shader), _size(size) {
	_vertexCount = 2*3*(_size + 1)*(_size + 1);
	_indexCount = _vertexCount;
	_vertices = std::make_unique<Vertex[]>(_vertexCount);
	_indices = std::make_unique<GLuint[]>(_indexCount);
}

Mesh::~Mesh() {

}

/*
VertexArray Mesh::generateVertices() const {
	std::size_t u = _size, v = _size;
	auto retVal = std::make_unique<Vertex[]>(u*v);
	const float step = (exp(4) - 1)/_size;

	for (std::size_t i = 0; i < u; i++)
		for (std::size_t j = 0; j < v; j++)
		{
//			Vertex vertex;
 			//float x = (float)i/(float)u
			float x = logf(i*step + 1)/4.0f - 0.5f;
			float y = (float)j/(float)v - 0.5f;
			retVal[j*u+i].coords[0] = x*3;
			retVal[j*u+i].coords[1] = y*3;
			retVal[j*u+i].coords[2] = heightMapFunc(j, i, x, y);
			retVal[j*u+i].normal[0] = 1.0f;
			retVal[j*u+i].normal[1] = 1.0f;
			retVal[j*u+i].normal[2] = 1.0f;
		}

	return std::make_tuple(std::move(retVal), u*v*sizeof(Vertex));
}
*/

void Mesh::generateVertices() const {
	const float step = 1.0f/_size;
	const float expStep = (exp(4) - 1)/(_size - 2);

	for(std::size_t j = 0; j < _size; ++j)
		for(std::size_t i = 0; i <= _size; ++i) {
			float x = (float)i/(_size - 0) - 0.5f;
			float xStep = step;
			float y = (float)j/(_size - 0) - 0.5f;
			//logf(j*expStep + 1)/4.0f - 0.5f;
			glm::vec3 p1(x, y, heightMapFunc(i, j, x, y));
			glm::vec3 p2(p1.x + xStep, p1.y, heightMapFunc(i + 1, j, x + xStep, y));
			glm::vec3 p3(p1.x, p1.y + step, heightMapFunc(i, j + 1, x, y + step));
			glm::vec3 p4(p1.x + xStep, p1.y + step, heightMapFunc(i + 1, j + 1, x + xStep, y + step));
			glm::vec3 norm1 = glm::cross(p2 - p1, p3 - p1);
			glm::vec3 norm2 = glm::cross(p3 - p4, p2 - p4);

            std::cout << "p1 = [" << p1.x << "," << p1.y << "]" << std::endl;

			std::size_t idx = (j*(_size - 1) + i)*2*3;

			_vertices[idx].x = p1.x;
			_vertices[idx].y = p1.y;
			_vertices[idx].z = p1.z;
			_vertices[idx].nx = norm1.x;
			_vertices[idx].ny = norm1.y;
			_vertices[idx].nz = norm1.z;

			idx += 1;
			_vertices[idx].x = p2.x;
			_vertices[idx].y = p2.y;
			_vertices[idx].z = p2.z;
			_vertices[idx].nx = norm1.x;
			_vertices[idx].ny = norm1.y;
			_vertices[idx].nz = norm1.z;

			idx += 1;
			_vertices[idx].x = p3.x;
			_vertices[idx].y = p3.y;
			_vertices[idx].z = p3.z;
			_vertices[idx].nx = norm1.x;
			_vertices[idx].ny = norm1.y;
			_vertices[idx].nz = norm1.z;

			idx += 1;
			_vertices[idx].x = p3.x;
			_vertices[idx].y = p3.y;
			_vertices[idx].z = p3.z;
			_vertices[idx].nx = norm2.x;
			_vertices[idx].ny = norm2.y;
			_vertices[idx].nz = norm2.z;

			idx += 1;
			_vertices[idx].x = p2.x;
			_vertices[idx].y = p2.y;
			_vertices[idx].z = p2.z;
			_vertices[idx].nx = norm2.x;
			_vertices[idx].ny = norm2.y;
			_vertices[idx].nz = norm2.z;

			idx += 1;
			_vertices[idx].x = p4.x;
			_vertices[idx].y = p4.y;
			_vertices[idx].z = p4.z;
			_vertices[idx].nx = norm2.x;
			_vertices[idx].ny = norm2.y;
			_vertices[idx].nz = norm2.z;
		}
}

/*
IndexArray Mesh::generateIndices() const {
	unsigned u = _size, v = _size;
	auto indices = std::make_unique<GLuint[]>((u-1)*(v-1)*6);
	for (unsigned i = 0; i < (u - 1); i++)
		for (unsigned j = 0; j < (v - 1); j++)
		{
			unsigned int indexa=j*(u-1)+i;
			unsigned int indexb=j*u+i;
			indices[indexa*6+0]=indexb;
			indices[indexa*6+1]=indexb+1+u;
			indices[indexa*6+2]=indexb+1;

			indices[indexa*6+3]=indexb;
			indices[indexa*6+4]=indexb+u;
			indices[indexa*6+5]=indexb+u+1;
		}

	return std::make_tuple(std::move(indices), (u-1)*(v-1)*6*sizeof(GLuint));
}*/

void Mesh::generateIndices() const {
	for(std::size_t i = 0; i < _size - 1; ++i)
		for(std::size_t j = 0; j < _size - 1; ++j) {
			std::size_t idx = (i*(_size - 1) + j)*6;
			_indices[idx] = (GLuint)idx;
			_indices[idx + 1] = (GLuint)idx + 1;
			_indices[idx + 2] = (GLuint)idx + 2;
			_indices[idx + 3] = (GLuint)idx + 3;
			_indices[idx + 4] = (GLuint)idx + 4;
			_indices[idx + 5] = (GLuint)idx + 5;
		}
}
