#include "EdgeLightBuffer.h"
#include "ShaderManager.h"
#include "MatrixManager.h"
#include "DrawFunc.h"

EdgeLightBuffer::EdgeLightBuffer(int nWidth, int nHeight)
{
	this->m_nWidth=nWidth;
	this->m_nHeight=nHeight;

	glEnable(GL_TEXTURE_2D);

	glGenFramebuffers(1,&m_nFrameBuffer);
	glBindFramebuffer(GL_FRAMEBUFFER, m_nFrameBuffer);
	
	glGenTextures(1, &m_nLightTex);
	glBindTexture(GL_TEXTURE_2D, m_nLightTex);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_nWidth, m_nHeight, 0, GL_RGBA, GL_FLOAT, 0);
	glBindTexture(GL_TEXTURE_2D, 0);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_nLightTex, 0);

	// check FbO status
	GLenum FBOstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	if(FBOstatus != GL_FRAMEBUFFER_COMPLETE)
	{
		glBindFramebuffer(GL_FRAMEBUFFER, 0); // error
	}

	glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

EdgeLightBuffer::~EdgeLightBuffer()
{
	glDeleteFramebuffers(1,&m_nFrameBuffer);
}


void EdgeLightBuffer::drawToBuffer(GLuint depthTex, View *view, Camera *camera, vector<Vector4 *> edgeLights)
{

	GLSLProgram *glslProgram = ShaderManager::getInstance()->getShader("Light");
	
	bind();
		glClearColor(0,0,0,0);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	unbind();

	for (int i=0; i<edgeLights.size(); i++)
	{
		bind();
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA,GL_ONE);
			glslProgram->use();
			GLenum mrt[] = {GL_COLOR_ATTACHMENT0_EXT};
			glDrawBuffers(1, mrt);
			glClear(GL_DEPTH_BUFFER_BIT);
			glPushAttrib( GL_VIEWPORT_BIT );
			glViewport( 0, 0, getWidth(), getHeight());

			MatrixManager::getInstance()->putMatrix4(MODELVIEW, glm::mat4(1.0f));
			MatrixManager::getInstance()->putMatrix4(PROJECTION, glm::mat4(1.0f));
			camera->transform();
			view->use3D(true);
			glm::mat4 m4InvMVP = MatrixManager::getInstance()->getMatrix4(PROJECTION) * MatrixManager::getInstance()->getMatrix4(MODELVIEW);
			m4InvMVP = glm::inverse(m4InvMVP);
			MatrixManager::getInstance()->putMatrix4(MODELVIEW, glm::mat4(1.0f));
			MatrixManager::getInstance()->putMatrix4(PROJECTION, glm::mat4(1.0f));

			view->use3D(false);

			glBindAttribLocation(glslProgram->getHandle(), 0, "v_vertex");
			glBindAttribLocation(glslProgram->getHandle(), 1, "v_texture");

			glslProgram->sendUniform("projectionMatrix", &MatrixManager::getInstance()->getMatrix4(PROJECTION)[0][0]);
			glslProgram->sendUniform("inverseMVPMatrix", &m4InvMVP[0][0]);
			glslProgram->sendUniform("lightPos", edgeLights[i][0][0], edgeLights[i][0][1], edgeLights[i][0][2], edgeLights[i][0][3]);
			glslProgram->sendUniform("lightRadius", 7.0f);
			glslProgram->sendUniform("near", (float)view->getNear());
			glslProgram->sendUniform("far", (float)view->getFar());

			glActiveTexture(GL_TEXTURE0); 
			glBindTexture(GL_TEXTURE_2D, depthTex);
			glslProgram->sendUniform("depthTex",0);

			drawScreenShader(0,0,1.0f,1.0f);

			glPopAttrib();
			glslProgram->disable();
		unbind();
	}

	glDisable(GL_BLEND);
}

void EdgeLightBuffer::bind() 
{
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_nFrameBuffer);
}

void EdgeLightBuffer::unbind() 
{
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

void EdgeLightBuffer::bindLightTex() 
{
	glBindTexture(GL_TEXTURE_2D, m_nLightTex);
}

GLuint EdgeLightBuffer::getLightTex() 
{
	return m_nLightTex;
}

int EdgeLightBuffer::getWidth() 
{
	return m_nWidth;
}

int EdgeLightBuffer::getHeight() 
{
	return m_nHeight;
}