#include "Game.h"
#include "SSAO.h"

const bool halfRes = false;
const GLfloat SSAO::pSphere[] = {0.53812504, 0.18565957, -0.43192,
0.13790712, 0.24864247, 0.44301823,
0.33715037, 0.56794053, -0.005789503,
-0.6999805, -0.04511441, -0.0019965635,
0.06896307, -0.15983082, -0.85477847,
0.056099437, 0.006954967, -0.1843352,
-0.014653638, 0.14027752, 0.0762037,
0.010019933, -0.1924225, -0.034443386,
-0.35775623, -0.5301969, -0.43581226,
-0.3169221, 0.106360726, 0.015860917,
0.010350345, -0.58698344, 0.0046293875,
-0.08972908, -0.49408212, 0.3287904,
0.7119986, -0.0154690035, -0.09183723,
-0.053382345, 0.059675813, -0.5411899,
0.035267662, -0.063188605, 0.54602677,
-0.47761092, 0.2847911, -0.0271716
};

SSAO::SSAO(void)
{
	//mrt shader for gbuffer
	_mrtShader = new Shader(Game::getInstance().getConfig().log);
	_mrtShader->InitShaders("Shader/mrt.vert", "Shader/mrt.frag");
	_nearPlane = _mrtShader->GetVariable("near");
	_farPlane = _mrtShader->GetVariable("far");

	//actual ssao shader
	_shader = new Shader(Game::getInstance().getConfig().log);
	_shader->InitShaders("Shader/ssao.vert", "Shader/ssao.frag");
	_varRandomTexture = _shader->GetVariable("randomMap");
	_varPosTexture = _shader->GetVariable("posMap"); 
	_varNormalTexture = _shader->GetVariable("normalMap");
	_varPSphere = _shader->GetVariable("pSphere");
	//_varColorTexture = _shader->GetVariable("colorMap");

	_varMatrix = _shader->GetVariable("projectionMatrix");

	//ssao combine shader
	_shader_combine = addShader("ssao_combine");
	_varColorTexture2 = _shader_combine->GetVariable("colorMap");
	_varPosTexture2 = _shader_combine->GetVariable("posMap"); 
	_varNormalTexture2 = _shader_combine->GetVariable("normalMap");

	_varXDirection = _shader_combine->GetVariable("xDirection"); 
	_varSSAOTexture = _shader_combine->GetVariable("SSAOMap"); 
	_varonlySSAO = _shader_combine->GetVariable("onlySSAO"); 
	_varMapResX = _shader_combine->GetVariable("mapResX"); 
	_varMapResY = _shader_combine->GetVariable("mapResY"); 

	_active = false;
	_onlySSAO = false;

}

SSAO::~SSAO(void)
{
	delete _shader;
}

void SSAO::saveMatrix(){
	glGetFloatv(GL_PROJECTION_MATRIX, _perspProjMatrix);
}

void SSAO::initSSAO(int resX, int resY){
	_gBuffer = new FBO(2, Game::getInstance().getConfig().log, resX, resY, true);

	//generate random normals for _texRandom
	srand(timeGetTime());
	std::vector<GLfloat> data; 
	float a,b,c;
	float length;

	for(int i=0; i<resX*resY; i++){
		a = rand() % 100 / 10 -5; //r
		b = rand() % 100 / 10 -5; //g
		c = rand() % 100 / 10 -5; //b
		length = sqrt(a*a + b*b + c*c);
		data.push_back(a/length); 
		data.push_back(b/length); 
		data.push_back(c/length);
		data.push_back(0.8f + (rand() / float(RAND_MAX)*0.4f)); // random scale factor
	}

	_texRandom = Game::getInstance().getConfig().texLoader->CreateTexture(
		GL_TEXTURE_2D, GL_RGBA32F_ARB, resX, resY, GL_RGBA, GL_FLOAT, GL_LINEAR, true, false, true, &data[0]);
	_texPos = Game::getInstance().getConfig().texLoader->CreateTexture(
		GL_TEXTURE_2D, GL_RGBA32F_ARB, resX, resY, GL_RGBA, GL_FLOAT, GL_LINEAR, true);
	_texNormal = Game::getInstance().getConfig().texLoader->CreateTexture(
		GL_TEXTURE_2D, GL_RGBA32F_ARB, resX, resY, GL_RGBA, GL_FLOAT, GL_LINEAR, true);
	_texColor = Game::getInstance().getConfig().texLoader->CreateTexture(
		GL_TEXTURE_2D, GL_RGBA32F_ARB, resX, resY, GL_RGBA, GL_FLOAT, GL_LINEAR, true);
   _texBlurredX = Game::getInstance().getConfig().texLoader->CreateTexture(
		GL_TEXTURE_2D, GL_RGBA32F_ARB, resX, resY, GL_RGBA, GL_FLOAT, GL_LINEAR, true);

	//start rendering into FBO
	_gBuffer->Bind();

	//attach (already filled) texture(s)
	_gBuffer->AttachFBOTexture(GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _texPos->GetID());
	_gBuffer->AttachFBOTexture(GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, _texNormal->GetID());
	_gBuffer->AttachFBOTexture(GL_COLOR_ATTACHMENT2_EXT, GL_TEXTURE_2D, _texColor->GetID());

	//Renderbuffer (Depth)
	_gBuffer->AttachRBO();

	//stop rendering into FBO
	_gBuffer->CheckFBO();
	_gBuffer->DisableFBO();

	//save texture adresses for later usage
	_varBuffers[0] = GL_COLOR_ATTACHMENT0_EXT;
	_varBuffers[1] = GL_COLOR_ATTACHMENT1_EXT;
	_varBuffers[2] = GL_COLOR_ATTACHMENT2_EXT;

	// ***********************************************
	//Buffer 2: input: color and ssaomap, output: combined image
	if(halfRes)
		_gBuffer2 = new FBO(3, Game::getInstance().getConfig().log, resX/2, resY/2, true);
	else
		_gBuffer2 = new FBO(3, Game::getInstance().getConfig().log, resX, resY, true);

	_texSSAO = Game::getInstance().getConfig().texLoader->CreateTexture(
		GL_TEXTURE_2D, GL_RGBA32F_ARB, resX, resY, GL_RGBA, GL_FLOAT, GL_LINEAR, true);
	_texSSAOHalf = Game::getInstance().getConfig().texLoader->CreateTexture(
		GL_TEXTURE_2D, GL_RGBA32F_ARB, resX/2, resY/2, GL_RGBA, GL_FLOAT, GL_LINEAR, true);

	_gBuffer2->Bind();
	if(halfRes)
		_gBuffer2->AttachFBOTexture(GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _texSSAOHalf->GetID());
	else
		_gBuffer2->AttachFBOTexture(GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _texSSAO->GetID());
	glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
	//_gBuffer2->AttachRBO();
	_gBuffer2->CheckFBO();
	_gBuffer2->DisableFBO();

	_varBuffers2[0] = GL_COLOR_ATTACHMENT0_EXT;
	_varBuffers2[1] = GL_COLOR_ATTACHMENT1_EXT;
}

void SSAO::setClippingPlane(float nearPlane, float farPlane)
{
	_mrtShader->SwitchOn();
	_mrtShader->SetFloat(_nearPlane, nearPlane);
	_mrtShader->SetFloat(_farPlane, farPlane);
	_mrtShader->SwitchOff();
}

void SSAO::BeginRenderToGBuffer(void)
{
	_gBuffer->Bind();

	_mrtShader->SwitchOn();
	//render to 3 buffers
	glDrawBuffers(3, _varBuffers);

}

void SSAO::EndRenderToGBuffer(void)
{
	_mrtShader->SwitchOff();
	_gBuffer->DisableFBO();
}

void SSAO::deactivate(){
	_active = false;
}

void SSAO::renderSSAOToGBuffer2(){
	_gBuffer2->Bind();
   if(halfRes || !_onlySSAO)
		_gBuffer2->AttachFBOTexture(GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _texSSAOHalf->GetID());
	else
		_gBuffer2->AttachFBOTexture(GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _texSSAO->GetID());
	
	_shader->SwitchOn();
	if (Game::getInstance().getState() == PLAYING)
	{
		//ModelViewProjectionMatrix
		//glUniformMatrix4fv(getUniformLocation(getUniformLocation("perspProjMatrix"), 1 GL_FALSE);
		_shader->SetMatrix4(_varMatrix, _perspProjMatrix);

		//Textures
		//glClearColor(0, 0, 0, 1);
		//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glDisable(GL_LIGHTING);
		glDisable(GL_DEPTH_BUFFER);
		//glDisable(GL_BLEND);
		
		glLoadIdentity();


		glActiveTextureARB(GL_TEXTURE0_ARB);
		glDisable(GL_TEXTURE_2D);
		glActiveTextureARB(GL_TEXTURE1_ARB);
		glDisable(GL_TEXTURE_2D);
		glActiveTextureARB(GL_TEXTURE2_ARB);
		glDisable(GL_TEXTURE_2D);
		glActiveTextureARB(GL_TEXTURE3_ARB);
		glDisable(GL_TEXTURE_2D);
		glActiveTextureARB(GL_TEXTURE4_ARB);
		glDisable(GL_TEXTURE_2D);
	
		_texRandom->Bind(GL_TEXTURE_2D, 0);
		_texPos->Bind(GL_TEXTURE_2D, 1);
		_texNormal->Bind(GL_TEXTURE_2D, 2);
		//_texColor->Bind(GL_TEXTURE_2D, 3);

		_shader->SetInt(_varRandomTexture, 0);
		_shader->SetInt(_varPosTexture, 1);
		_shader->SetInt(_varNormalTexture, 2);
      glUniform3fv(_varPSphere, 16, pSphere);
		//_shader->SetInt(_varColorTexture, 3);
		
		if(halfRes || !_onlySSAO)
		{
			glPushAttrib(GL_VIEWPORT_BIT);
			glViewport(0, 0, Game::getInstance().getConfig().screenWidth/2,
				Game::getInstance().getConfig().screenHeight/2);
		}
		//FullScreenQuad
		glBegin(GL_QUADS);
			glTexCoord2f(1, 1);
			glVertex2f(1, 1);
			glTexCoord2f(0, 1);
			glVertex2f(-1, 1);
			glTexCoord2f(0, 0);
			glVertex2f(-1, -1);
			glTexCoord2f(1, 0);
			glVertex2f(1, -1);
		glEnd();		

		if(halfRes || !_onlySSAO)
			glPopAttrib();

		_shader->SwitchOff();

		glActiveTextureARB(GL_TEXTURE0_ARB);
		glDisable(GL_TEXTURE_2D);
		glActiveTextureARB(GL_TEXTURE1_ARB);
		glDisable(GL_TEXTURE_2D);
		glActiveTextureARB(GL_TEXTURE2_ARB);
		glDisable(GL_TEXTURE_2D);
		glActiveTextureARB(GL_TEXTURE3_ARB);
		glDisable(GL_TEXTURE_2D);
		glActiveTextureARB(GL_TEXTURE4_ARB);
		glDisable(GL_TEXTURE_2D);

	} //end if
	//glDrawBuffers(2, _varBuffers2);
	_gBuffer2->DisableFBO();
}

void SSAO::activate()
{
	_active = true;

	renderSSAOToGBuffer2();

	//combine color with ssao map
	_shader_combine->SwitchOn();
	glActiveTextureARB(GL_TEXTURE0_ARB);
	glDisable(GL_TEXTURE_2D);
	glActiveTextureARB(GL_TEXTURE1_ARB);
	glDisable(GL_TEXTURE_2D);

	_texColor->Bind(GL_TEXTURE_2D, 0);
	if(halfRes || !_onlySSAO)
		_texSSAOHalf->Bind(GL_TEXTURE_2D, 3);
	else
		_texSSAO->Bind(GL_TEXTURE_2D, 3);

	if(halfRes || !_onlySSAO)
	{
		_shader_combine->SetFloat(_varMapResX, Game::getInstance().getConfig().screenWidth / 2);
		_shader_combine->SetFloat(_varMapResY, Game::getInstance().getConfig().screenHeight / 2);
	}
	else
	{
		_shader_combine->SetFloat(_varMapResX, Game::getInstance().getConfig().screenWidth);
		_shader_combine->SetFloat(_varMapResY, Game::getInstance().getConfig().screenHeight);
	}

	_shader_combine->SetInt(_varColorTexture2, 0);
	_shader_combine->SetInt(_varSSAOTexture, 3);
	_shader_combine->SetInt(_varPosTexture2, 1);
	_shader_combine->SetInt(_varNormalTexture2, 2);
	if (_onlySSAO) //show only ao map
		_shader_combine->SetFloat(_varonlySSAO, 1.0);
	else //combine color with ao
		_shader_combine->SetFloat(_varonlySSAO, 0.0);

   if(_onlySSAO)
   {
      //FullScreenQuad
      glBegin(GL_QUADS);
      glTexCoord2f(1, 1);
      glVertex2f(1, 1);
      glTexCoord2f(0, 1);
      glVertex2f(-1, 1);
      glTexCoord2f(0, 0);
      glVertex2f(-1, -1);
      glTexCoord2f(1, 0);
      glVertex2f(1, -1);
      glEnd();

   }

   else
   { // two pass blur

      _gBuffer2->Bind();
      // first pass:
      _gBuffer2->AttachFBOTexture(GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, _texBlurredX->GetID());

      // x pass
      _shader_combine->SetFloat(_varXDirection, 1.0);

      //FullScreenQuad
      glBegin(GL_QUADS);
      glTexCoord2f(1, 1);
      glVertex2f(1, 1);
      glTexCoord2f(0, 1);
      glVertex2f(-1, 1);
      glTexCoord2f(0, 0);
      glVertex2f(-1, -1);
      glTexCoord2f(1, 0);
      glVertex2f(1, -1);
      glEnd();

      // y pass
      _shader_combine->SetFloat(_varXDirection, 0.0);
      _gBuffer2->DisableFBO();
      _texBlurredX->Bind(GL_TEXTURE_2D, 3);

      //FullScreenQuad
      glBegin(GL_QUADS);
      glTexCoord2f(1, 1);
      glVertex2f(1, 1);
      glTexCoord2f(0, 1);
      glVertex2f(-1, 1);
      glTexCoord2f(0, 0);
      glVertex2f(-1, -1);
      glTexCoord2f(1, 0);
      glVertex2f(1, -1);
      glEnd();

   }

	_shader_combine->SwitchOff();

	glActiveTextureARB(GL_TEXTURE0_ARB);
	glDisable(GL_TEXTURE_2D);
	glActiveTextureARB(GL_TEXTURE1_ARB);
	glDisable(GL_TEXTURE_2D);
	glActiveTextureARB(GL_TEXTURE2_ARB);
	glDisable(GL_TEXTURE_2D);
	glActiveTextureARB(GL_TEXTURE3_ARB);
	glDisable(GL_TEXTURE_2D);

	//Effect::activate();
}
