#include "common_header.h"
#include "win_OpenGLApp.h"
#include "shaders.h"
#include "texture.h"
#include "vertexBufferObject.h"
#include "flyingCamera.h"
#include "skybox.h"
#include "static_geometry.h"

CVertexBufferObject vboSceneObjects;
UINT uiVAOs[1];

CShader shShaders[2];
CShaderProgram spDirectionalLight;

#define NUMTEXTURES 3

CTexture tTextures[NUMTEXTURES];

CFlyingCamera cCamera;

CSkybox sbMainSkybox;

int iTorusFaces;

bool initScene()
{
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

	vboSceneObjects.createVBO();
	glGenVertexArrays(1, uiVAOs);
	glBindVertexArray(uiVAOs[0]);

	vboSceneObjects.bindVBO();

	FOR(i, 36)
	{
		vboSceneObjects.addData(&vCubeVertices[i], sizeof(vec3));
		vboSceneObjects.addData(&vCubeTexCoords[i%6], sizeof(vec2));
		vboSceneObjects.addData(&vCubeNormals[i/6], sizeof(vec3));
	}

	FOR(i, 6)
	{
		vboSceneObjects.addData(&vGround[i], sizeof(vec3));
		vCubeTexCoords[i] *= 10.0f;
		vboSceneObjects.addData(&vCubeTexCoords[i%6], sizeof(vec2));
		vec3 vGroundNormal(0.0f, 1.0f, 0.0f);
		vboSceneObjects.addData(&vGroundNormal, sizeof(vec3));
	}

	iTorusFaces = generateTorus(vboSceneObjects, 7.0f, 2.0f, 20, 20);
	vboSceneObjects.uploadDataToGPU(GL_STATIC_DRAW);

	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 2*sizeof(vec3)+sizeof(vec2), 0);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(vec3)+sizeof(vec2), (void*)sizeof(vec3));
	glEnableVertexAttribArray(2);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 2*sizeof(vec3)+sizeof(vec2), (void*)(sizeof(vec3)+sizeof(vec2)));

	shShaders[0].loadShader("release\\shaders\\shader.vert", GL_VERTEX_SHADER);
	shShaders[1].loadShader("release\\shaders\\shader.frag", GL_FRAGMENT_SHADER);

	spDirectionalLight.createProgram();
	spDirectionalLight.addShaderToProgram(&shShaders[0]);
	spDirectionalLight.addShaderToProgram(&shShaders[1]);
	spDirectionalLight.linkProgram();

	string sTextureNames[] = {"grass.jpg", "crate.jpg", "metalplate.jpg"};

	FOR(i, NUMTEXTURES)
	{
		tTextures[i].loadTexture2D("..\\10.) Skybox - V6\\release\\textures\\"+sTextureNames[i], true);
		tTextures[i].setFiltering(TEXTURE_FILTER_MAG_BILINEAR, TEXTURE_FILTER_MIN_BILINEAR_MIPMAP);
	}

	glEnable(GL_DEPTH_TEST);
	glClearDepth(1.0);
	glClearColor(0.0f, 0.26f, 0.48f, 1.0f);

	cCamera = CFlyingCamera(vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, -1.0f), vec3(0.0f, 1.0f, 0.0f), 25.0f, 0.1f);
	cCamera.setMovingKeys('W', 'S', 'A', 'D');

	sbMainSkybox.loadSkybox("..\\10.) Skybox - V6\\release\\textures\\", "jajlands1_ft.jpg", "jajlands1_bk.jpg", "jajlands1_lf.jpg", "jajlands1_rt.jpg", "jajlands1_up.jpg", "jajlands1_dn.jpg");

	return true;
}

float fGlobalAngle;
mat4 ProjectionMatrix;

void renderScene()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glEnable(GL_TEXTURE_2D);
	spDirectionalLight.useProgram();

	spDirectionalLight.setUniform("sunLight.vColor", vec3(1.0f, 1.0f, 1.0f));
	spDirectionalLight.setUniform("sunLight.fAmbientIntensity", 1.0f); // Full light for skybox
	spDirectionalLight.setUniform("sunLight.vDirection", vec3(0, -1, 0));

	spDirectionalLight.setUniform("projectionMatrix", ProjectionMatrix);
	spDirectionalLight.setUniform("gSampler", 0);

	mat4 mModelView = cCamera.look();
	mat4 mModelToCamera;

	spDirectionalLight.setUniform("modelViewMatrix", translate(mModelView, cCamera.vEye));
	sbMainSkybox.renderSkybox();

	glBindVertexArray(uiVAOs[0]);
	spDirectionalLight.setUniform("sunLight.fAmbientIntensity", 0.55f);
	spDirectionalLight.setUniform("modelViewMatrix", &mModelView);
	
	tTextures[0].bindTexture();
	glDrawArrays(GL_TRIANGLES, 36, 6);

	tTextures[1].bindTexture();

	SFOR(nb, 1, 9)
	{
		int iCnt = nb > 5 ? 10-nb : nb;
		FOR(i, iCnt)
		{
			vec3 vPos = vec3(-20.0f+nb*8.02f, -6.0f+i*8.02f, -50.0f);
			mModelToCamera = translate(mat4(1.0), vPos);
			mModelToCamera = scale(mModelToCamera, vec3(8.0f, 8.0f, 8.0f));
			spDirectionalLight.setUniform("normalMatrix", transpose(inverse(mModelToCamera)));
			spDirectionalLight.setUniform("modelViewMatrix", mModelView*mModelToCamera);
			glDrawArrays(GL_TRIANGLES, 0, 36);
		}
	}

	tTextures[2].bindTexture();
	spDirectionalLight.setUniform("vColor", vec4(1.0f, 1.0f, 1.0f, 1.0f));
	
	vec3 vPos = vec3(0.0f, 0.0, 40.0f);
	mModelToCamera = translate(mat4(1.0), vPos);
	mModelToCamera = rotate(mModelToCamera, fGlobalAngle, vec3(0.0f, 1.0f, 0.0f));
	spDirectionalLight.setUniform("normalMatrix", transpose(inverse(mModelToCamera)));
	spDirectionalLight.setUniform("modelViewMatrix", mModelView*mModelToCamera);
	glDrawArrays(GL_TRIANGLES, 42, iTorusFaces*3);

	mModelToCamera = translate(mat4(1.0), vPos);
	mModelToCamera = rotate(mModelToCamera, fGlobalAngle, vec3(0.0f, 1.0f, 0.0f));
	mModelToCamera = rotate(mModelToCamera, 90.0f, vec3(1.0f, 0.0f, 0.0f));
	spDirectionalLight.setUniform("normalMatrix", transpose(inverse(mModelToCamera)));
	spDirectionalLight.setUniform("modelViewMatrix", mModelView*mModelToCamera);
	glDrawArrays(GL_TRIANGLES, 42, iTorusFaces*3);

	mModelToCamera = translate(mat4(1.0), vPos);
	mModelToCamera = rotate(mModelToCamera, fGlobalAngle+90.0f, vec3(0.0f, 1.0f, 0.0f));
	spDirectionalLight.setUniform("normalMatrix", transpose(inverse(mModelToCamera)));
	spDirectionalLight.setUniform("modelViewMatrix", mModelView*mModelToCamera);
	glDrawArrays(GL_TRIANGLES, 42, iTorusFaces*3);


	fGlobalAngle += appMain.sof(100.0f);
	cCamera.update();

	if(GetKeyState(VK_ESCAPE) & 0x80) PostQuitMessage(0);
}

void resizeScene(int Width, int Height)
{
	glViewport(0, 0, Width, Height);

	ProjectionMatrix = perspective(45.0f, (float)Width / (float)Height, 0.125f, 512.0f);
}

void releaseScene()
{
	FOR(i, NUMTEXTURES)tTextures[i].releaseTexture();
	sbMainSkybox.releaseSkybox();

	spDirectionalLight.deleteProgram();
	FOR(i, 2)shShaders[i].deleteShader();

	glDeleteVertexArrays(1, uiVAOs);
	vboSceneObjects.releaseVBO();
}