#include "DynamicCubeMap.hpp"
#include <iostream>

#include "glm/gtc/type_ptr.hpp"
#include "glm/gtc/matrix_transform.hpp"


DynamicCubeMap::DynamicCubeMap(GLsizei p_width, GLsizei p_height, GLdouble near, GLdouble far):
width(p_width),
height(p_height),
nearClippingPlane(near),
farClippingPlane(far)
{
	// create an empty texture
	glGenTextures(1, &textureName);
	glBindTexture(GL_TEXTURE_CUBE_MAP, textureName);
	
	// Sets the texture's behavior for wrapping (optional)
	glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_S,GL_CLAMP);
	glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_WRAP_T,GL_CLAMP);
	// Sets the texture's max/min filters (mandatory) 
	glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_CUBE_MAP,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

	//create a depth buffer for our framebuffer
	glGenRenderbuffers(1, &renderBuffer);
	glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
	//glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB, width, height);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);	
	glBindRenderbuffer(GL_RENDERBUFFER, 0);
	
	//attach the texture and depth buffer to the framebuffer
	glGenFramebuffers(1, &frameBuffer);
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer);
	//glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP, textureName, 0);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, textureName, 0);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, textureName, 0);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, textureName, 0);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, textureName, 0);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, textureName, 0);
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, textureName, 0);
	glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderBuffer);	
	

	glEnable(GL_DEPTH_TEST);

	GLenum fbstatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
	switch (fbstatus)
	{
		//case GL_FRAMEBUFFER_COMPLETE:	
		//				std::cout << "GL_FRAMEBUFFER_COMPLETE" << std::endl; break;
		case GL_FRAMEBUFFER_UNDEFINED:	
						std::cout << "GL_FRAMEBUFFER_UNDEFINED" << std::endl; break;
		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:	
						std::cout << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT" << std::endl; break;
		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:	
						std::cout << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT" << std::endl; break;
		case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:	
						std::cout << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER" << std::endl; break;
		case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:	
						std::cout << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER" << std::endl; break;
		case GL_FRAMEBUFFER_UNSUPPORTED:	
						std::cout << "GL_FRAMEBUFFER_UNSUPPORTED" << std::endl; break;
		case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:	
						std::cout << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" << std::endl; break;
	}
	
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}


DynamicCubeMap::~DynamicCubeMap()
{	
	glDeleteFramebuffers(1, &frameBuffer);
}


void DynamicCubeMap::setGameReference(Game* p_game)
{
	game = p_game;
}


void DynamicCubeMap::render(Vector3f viewPoint)
{
	GLfloat currentClearColor[4];
	glGetFloatv(GL_COLOR_CLEAR_VALUE, currentClearColor);
	
	//update Texture
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glTranslatef(viewPoint.x, viewPoint.y, 0.0f);
 
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer);
	glPushAttrib(GL_VIEWPORT_BIT);	
	glViewport(0,0,width,height);
	glClearColor(1.0f,1.0f,1.0f,1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	//negative_X
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glRotatef(90.0f, 0.0f, 1.0f, 0.0f);	
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	//gluPerspective(90.0, 1.0, nearClippingPlane, farClippingPlane);
	glLoadMatrixf(glm::value_ptr(glm::perspective<float>(0.5f*glm::pi<float>(), 1.0f, nearClippingPlane, farClippingPlane)));
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, textureName, 0);
	game->DrawScene();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();	
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	//positive_X
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glRotatef(-90.0f, 0.0f, 1.0f, 0.0f);	
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadMatrixf(glm::value_ptr(glm::perspective<float>(0.5f*glm::pi<float>(), 1.0f, nearClippingPlane, farClippingPlane)));
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, textureName, 0);
	game->DrawScene();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();	
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	//negative_Y
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);	
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadMatrixf(glm::value_ptr(glm::perspective<float>(0.5f*glm::pi<float>(), 1.0f, nearClippingPlane, farClippingPlane)));
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, textureName, 0);
	game->DrawScene();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();	
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	//positive_Y
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glRotatef(90.0f, 1.0f, 0.0f, 0.0f);	
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadMatrixf(glm::value_ptr(glm::perspective<float>(0.5f*glm::pi<float>(), 1.0f, nearClippingPlane, farClippingPlane)));
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, textureName, 0);
	game->DrawScene();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();	
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	//negative_Z
	//glMatrixMode(GL_MODELVIEW);
	//glPushMatrix();
	//glRotatef(0.0f, 0.0f, 1.0f, 0.0f);	
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadMatrixf(glm::value_ptr(glm::perspective<float>(0.5f*glm::pi<float>(), 1.0f, nearClippingPlane, farClippingPlane)));
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, textureName, 0);
	game->DrawScene();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();	
	//glMatrixMode(GL_MODELVIEW);
	//glPopMatrix();

	//positive_Z
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glRotatef(180.0f, 0.0f, 1.0f, 0.0f);	
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadMatrixf(glm::value_ptr(glm::perspective<float>(0.5f*glm::pi<float>(), 1.0f, nearClippingPlane, farClippingPlane)));
	glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, textureName, 0);
	game->DrawScene();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();	
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	glPopMatrix(); // glTranslatef()
	glPopAttrib();
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
		 
	glClearColor(currentClearColor[0], currentClearColor[1], currentClearColor[2], currentClearColor[3]);		
}


GLuint DynamicCubeMap::getTexName()
{
	return textureName;
}


void DynamicCubeMap::enable()
{	
	glActiveTexture(GL_TEXTURE0);
	glEnable(GL_TEXTURE_CUBE_MAP);
	glBindTexture(GL_TEXTURE_CUBE_MAP, textureName);

	// Enable texture generation for S,T,R coords
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);
	glEnable(GL_TEXTURE_GEN_R);
	// Sets the coordinates to be generated and how
	glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP);
	glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP);
	glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_REFLECTION_MAP);
	
}


void DynamicCubeMap::disable()
{	
	glDisable(GL_TEXTURE_GEN_S);
	glDisable(GL_TEXTURE_GEN_T);
	glDisable(GL_TEXTURE_GEN_R);
	glDisable(GL_TEXTURE_CUBE_MAP);
	glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
	
}


void DynamicCubeMap::display()
{
	GLboolean lightingWasEnabled[1];	
	glGetBooleanv(GL_LIGHTING, lightingWasEnabled);
	glDisable(GL_LIGHTING);
	
	glActiveTexture(GL_TEXTURE0);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, textureName);
		
	glBegin(GL_QUADS);
		glNormal3f(0.0f,-1.0f,0.0f);
		glMultiTexCoord2f(GL_TEXTURE0, 0, 0);	glVertex3f(-2.0f,0.0f,2.0f);
		glMultiTexCoord2f(GL_TEXTURE0, 1, 0);	glVertex3f(2.0f,0.0f,2.0f);
		glMultiTexCoord2f(GL_TEXTURE0, 1, 1);	glVertex3f(2.0f,0.0f,6.0f);
		glMultiTexCoord2f(GL_TEXTURE0, 0, 1);	glVertex3f(-2.0f,0.0f,6.0f);
	glEnd();	

	glBindTexture(GL_TEXTURE_2D, 0);
	glDisable(GL_TEXTURE_2D);

	if (lightingWasEnabled)
		glEnable(GL_LIGHTING);
}










