/*
 * normal mapping
 * from Nopper ogl 3.3 tutorial 06
*/

#include "demo_normal_mapping.h"


#include <glm/gtx/transform.hpp>
// glm::perspective
#include <glm/gtc/matrix_projection.hpp>
// glm::translate, glm::rotate, glm::scale
#include <glm/gtc/matrix_transform.hpp>
// glm::value_ptr
#include <glm/gtc/type_ptr.hpp> 


demo_normal_mapping::demo_normal_mapping()
{
	title = "normal mapping engine (based on Nopper Tutorial 06)";
}


demo_normal_mapping::~demo_normal_mapping()
{

}


bool demo_normal_mapping::init()
{
	if (!init_window())
	{
		return false;
	}

	//cam
	cam_eye = glm::vec3(0.0f, 0.0f, 5.f);
	cam_center = glm::vec3(0.0f, 0.0f, 0.0f);
	cam_up = glm::vec3(0.0f, 1.0f, 0.0f);

	angle = 0.0f;

	//do opengl initialization here
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
	glEnable(GL_DEPTH_TEST);
	glClearDepth(1.0f);
	glDepthFunc(GL_LEQUAL);
	glDisable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE);
	glEnable(GL_CULL_FACE);

	//set the different coordinates
	GLfloat points[] = {
		-1.0f, -1.0f, 0.0f, 1.0f,
		1.0f, -1.0f, 0.0f, 1.0f,
		-1.0f, 1.0f, 0.0f, 1.0f,
		1.0f, 1.0f, 0.0f, 1.0f
	};

	GLfloat normals[] = {
		0.0f, 0.0f, 1.0f,
		0.0f, 0.0f, 1.0f,
		0.0f, 0.0f, 1.0f,
		0.0f, 0.0f, 1.0f
	};

	GLfloat tangents[] = {
		1.0f, 0.0f, 0.0f,
		1.0f, 0.0f, 0.0f, 
		1.0f, 0.0f, 0.0f,
		1.0f, 0.0f, 0.0f
	};

	GLfloat texCoords[] = {
		0.0f, 0.0f,
		1.0f, 0.0f,
		0.0f, 1.0f,
		1.0f, 1.0f
	};

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

	GLuint numberVertices = 4;
	numberIndices = 6;

	//shader
	normmap = std::auto_ptr<Shader>(new Shader("../../shader/normmap"));
	if(!(*normmap))
	{
		cerr << "Could not compile shader program" << endl;
		return 1;
	}

	normmap->bind_frag_data_location("frag_color");

	//erzeugen der arrays
	glGenVertexArrays(1, &g_vao);
	glBindVertexArray(g_vao);

	//get the different locations
	g_projectionLocation = normmap->get_uniform_location("projectionMatrix");
	g_modelViewLocation = normmap->get_uniform_location("modelViewMatrix");
	g_lightDirLocation = normmap->get_uniform_location("lightDir");
	g_textureLocation = normmap->get_uniform_location("firstTexture");
	g_normalMapLocation = normmap->get_uniform_location("normalMap");
	g_vertexLocation = normmap->get_attrib_location("vertex");
	g_normalLocation = normmap->get_attrib_location("normal");
	g_tangentLocation = normmap->get_attrib_location("tangent");
	g_texCoordLocation = normmap->get_attrib_location("texCoord");

	glGenBuffers(1, &g_vertices);
	glBindBuffer(GL_ARRAY_BUFFER, g_vertices);
	glBufferData(GL_ARRAY_BUFFER, numberVertices * 4 * sizeof(GLfloat), (GLfloat*)points, GL_STATIC_DRAW);

	glGenBuffers(1, &g_normals);
	glBindBuffer(GL_ARRAY_BUFFER, g_normals);
	glBufferData(GL_ARRAY_BUFFER, numberVertices * 3 * sizeof(GLfloat), (GLfloat*)normals, GL_STATIC_DRAW);

	glGenBuffers(1, &g_tangents);
	glBindBuffer(GL_ARRAY_BUFFER, g_tangents);
	glBufferData(GL_ARRAY_BUFFER, numberVertices * 3 * sizeof(GLfloat), (GLfloat*)tangents, GL_STATIC_DRAW);

	glGenBuffers(1, &g_texCoords);
	glBindBuffer(GL_ARRAY_BUFFER, g_texCoords);
	glBufferData(GL_ARRAY_BUFFER, numberVertices * 2 * sizeof(GLfloat), (GLfloat*)texCoords, GL_STATIC_DRAW);

	glGenBuffers(1, &g_indices);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_indices);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, numberIndices * sizeof(GLuint), (GLuint*)indices, GL_STATIC_DRAW);


	//laden der texturen
	if (!load_tga_texture(&texture[TEX], "../../textures/rock_color.tga", GL_SRGB8_ALPHA8, true) 
	)
	{
		std::cout << "failed to load textures !\n";
		return false;
	}
	if (!load_tga_texture(&texture[NORM], "../../textures/rock_normal.tga", GL_SRGB8_ALPHA8, true) 
	)
	{
		std::cout << "failed to load textures !\n";
		return false;
	}

	normmap->bind();

	//load_identity(perspective_matrix);
	load_identity(modelview_matrix);  //muss matrix sein!!!!

	look_at(modelview_matrix, cam_eye, cam_center, cam_up);

	glUniformMatrix4fv(g_modelViewLocation, 1, GL_FALSE, glm::value_ptr(modelview_matrix));
	glUniformMatrix4fv(g_projectionLocation, 1, GL_FALSE, glm::value_ptr(perspective_matrix));

	glBindBuffer(GL_ARRAY_BUFFER, g_vertices);
	glVertexAttribPointer(g_vertexLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL);
	glEnableVertexAttribArray(g_vertexLocation);

	glBindBuffer(GL_ARRAY_BUFFER, g_normals);
	glVertexAttribPointer(g_normalLocation, 3, GL_FLOAT, GL_FALSE, 0, NULL);
	glEnableVertexAttribArray(g_normalLocation);

	glBindBuffer(GL_ARRAY_BUFFER, g_tangents);
	glVertexAttribPointer(g_tangentLocation, 3, GL_FLOAT, GL_FALSE, 0, NULL);
	glEnableVertexAttribArray(g_tangentLocation);

	glBindBuffer(GL_ARRAY_BUFFER, g_texCoords);
	glVertexAttribPointer(g_texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
	glEnableVertexAttribArray(g_texCoordLocation);


	//glUniform1i(g_textureLocation, 0);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, texture[TEX]);
	glUniform1i(g_textureLocation, 0);

	//glUniform1i(g_normalMapLocation, 1);
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, texture[NORM]);
	glUniform1i(g_normalMapLocation, 1);

	normmap->unbind();

	//opengl start up 
	//glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	//glClearDepth(1.0f);
	//glEnable(GL_DEPTH_TEST);
	//glEnable(GL_CULL_FACE);

	return true;
}


void demo_normal_mapping::process_keys()
{

	//factor determines the speed of the camera movement
	float factor = static_cast<float>(delta()) * 5;

	update_camera_movement(factor);

}


void demo_normal_mapping::cleanup()
{
	if(g_vertices)
	{
		glDeleteBuffers(1, &g_vertices);
		g_vertices = 0;
	}

	if(g_normals)
	{
		glDeleteBuffers(1, &g_normals);
		g_normals = 0;
	}

	if(g_tangents)
	{
		glDeleteBuffers(1, &g_tangents);
		g_tangents = 0;
	}

	if(g_texCoords)
	{
		glDeleteBuffers(1, &g_texCoords);
		g_texCoords = 0;
	}

	if(g_indices)
	{
		glDeleteBuffers(1, &g_indices);
		g_indices = 0;
	}

	if(g_texture)
	{
		glDeleteTextures(1, &g_texture);
		g_texture = 0;
	}

	if(g_normalMap)
	{
		glDeleteTextures(1, &g_normalMap);
		g_normalMap = 0;
	}

	glDeleteVertexArrays(1, &g_vao);

}


void demo_normal_mapping::draw()
{

	update();

}


void demo_normal_mapping::update()
{
	normmap->bind();
	GLfloat lightDir[] = {2.0f * glm::cos(angle), 0.0f, 1.0f};
	angle += 0.005f;
	
	glUniform3fv(g_lightDirLocation, 1, (GLfloat*)lightDir);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	//load_identity(perspective_matrix);
	load_identity(modelview_matrix);  //muss matrix sein!!!!

	look_at(modelview_matrix, cam_eye, cam_center, cam_up);
	
	g_modelViewLocation = normmap->get_uniform_location("modelViewMatrix");

	glUniformMatrix4fv(g_modelViewLocation, 1, GL_FALSE, glm::value_ptr(modelview_matrix));

	glDrawElements(GL_TRIANGLES, numberIndices, GL_UNSIGNED_INT, 0);

	normmap->unbind();
}