#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"

CFlyingCamera Camera;

mat4x4 ModelMatrix, ViewMatrix, ProjectionMatrix;

CSkybox Skybox;

CTexture Textures[3];
CShaderProgram Lighting;

CVertexBufferObject VBO;
GLuint VAO;
int TorusFaces;

bool initScene()
{
	bool Error = false;

	Error |= !Skybox.Init();

	char *sTextureNames[] = {
		"..\\10.) Skybox - V6\\release\\textures\\grass.jpg",
		"..\\10.) Skybox - V6\\release\\textures\\crate.jpg",
		"..\\10.) Skybox - V6\\release\\textures\\metalplate.jpg"
	};

	for(int i = 0; i < 3; i++)
	{
		Error |= !Textures[i].LoadTexture2D(sTextureNames[i]);
	}

	Error |= !Lighting.Load("release\\shaders\\lighting.vert", "release\\shaders\\lighting.frag");

	if(Error)
	{
		return false;
	}

	glUseProgram(Lighting);
	glUniform1f(glGetUniformLocation(Lighting, "Light.Ambient"), 0.333333f);
	glUniform1f(glGetUniformLocation(Lighting, "Light.Diffuse"), 0.666666f);
	glUniform3fv(glGetUniformLocation(Lighting, "Light.Direction"), 1, (float*)&vec3(0.467757f, 0.424200f, -0.775409f));

	VBO.createVBO();
	glGenVertexArrays(1, &VAO);
	glBindVertexArray(VAO);

	VBO.bindVBO();

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

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

	TorusFaces = generateTorus(VBO, 7.0f, 2.0f, 20, 20);
	VBO.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)));

	Camera = 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);
	
	Camera.setMovingKeys('W', 'S', 'A', 'D');
	
	ViewMatrix = Camera.look();

	return true;
}

void renderScene()
{
	static float Angle = 0.0f;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	Skybox.Render(ProjectionMatrix * translate(ViewMatrix, Camera.vEye));
	
	glEnable(GL_DEPTH_TEST);

	glUseProgram(Lighting);

	glBindVertexArray(VAO);
	
	glUniformMatrix4fv(glGetUniformLocation(Lighting, "ModelViewMatrix"), 1, GL_FALSE, (float*)&ViewMatrix);
	glBindTexture(GL_TEXTURE_2D, Textures[0]);
	glDrawArrays(GL_TRIANGLES, 36, 6);

	glBindTexture(GL_TEXTURE_2D, Textures[1]);

	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);
			ModelMatrix = translate(mat4(1.0), vPos);
			ModelMatrix = scale(ModelMatrix, vec3(8.0f, 8.0f, 8.0f));
			glUniformMatrix4fv(glGetUniformLocation(Lighting, "NormalMatrix"), 1, GL_FALSE, (float*)&transpose(inverse(ModelMatrix)));
			glUniformMatrix4fv(glGetUniformLocation(Lighting, "ModelViewMatrix"), 1, GL_FALSE, (float*)&(ViewMatrix*ModelMatrix));
			glDrawArrays(GL_TRIANGLES, 0, 36);
		}
	}

	glBindTexture(GL_TEXTURE_2D, Textures[2]);
	
	vec3 vPos = vec3(0.0f, 0.0, 40.0f);
	ModelMatrix = translate(mat4(1.0), vPos);
	ModelMatrix = rotate(ModelMatrix, Angle, vec3(0.0f, 1.0f, 0.0f));
	glUniformMatrix4fv(glGetUniformLocation(Lighting, "NormalMatrix"), 1, GL_FALSE, (float*)&transpose(inverse(ModelMatrix)));
	glUniformMatrix4fv(glGetUniformLocation(Lighting, "ModelViewMatrix"), 1, GL_FALSE, (float*)&(ViewMatrix*ModelMatrix));
	glDrawArrays(GL_TRIANGLES, 42, TorusFaces*3);

	ModelMatrix = translate(mat4(1.0), vPos);
	ModelMatrix = rotate(ModelMatrix, Angle, vec3(0.0f, 1.0f, 0.0f));
	ModelMatrix = rotate(ModelMatrix, 90.0f, vec3(1.0f, 0.0f, 0.0f));
	glUniformMatrix4fv(glGetUniformLocation(Lighting, "NormalMatrix"), 1, GL_FALSE, (float*)&transpose(inverse(ModelMatrix)));
	glUniformMatrix4fv(glGetUniformLocation(Lighting, "ModelViewMatrix"), 1, GL_FALSE, (float*)&(ViewMatrix*ModelMatrix));
	glDrawArrays(GL_TRIANGLES, 42, TorusFaces*3);

	ModelMatrix = translate(mat4(1.0), vPos);
	ModelMatrix = rotate(ModelMatrix, Angle+90.0f, vec3(0.0f, 1.0f, 0.0f));
	glUniformMatrix4fv(glGetUniformLocation(Lighting, "NormalMatrix"), 1, GL_FALSE, (float*)&transpose(inverse(ModelMatrix)));
	glUniformMatrix4fv(glGetUniformLocation(Lighting, "ModelViewMatrix"), 1, GL_FALSE, (float*)&(ViewMatrix*ModelMatrix));
	glDrawArrays(GL_TRIANGLES, 42, TorusFaces*3);

	glBindTexture(GL_TEXTURE_2D, 0);
	glBindVertexArray(0);
	glUseProgram(0);
	glDisable(GL_DEPTH_TEST);

	Angle += appMain.sof(100.0f);

	Camera.update();
	ViewMatrix = Camera.look();
}

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

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

	glUseProgram(Lighting);
	glUniformMatrix4fv(glGetUniformLocation(Lighting, "ProjectionMatrix"), 1, GL_FALSE, (float*)&ProjectionMatrix);
	glUseProgram(0);
}

void releaseScene()
{
	Skybox.Destroy();

	for(int i = 0; i < 3; i++)
	{
		Textures[i].Destroy();
	}

	Lighting.Destroy();

	glDeleteVertexArrays(1, &VAO);
	VBO.releaseVBO();
}