#include "../Framework/Framework.h"
#include "Chess.h"
#include <time.h>

class MainApp : public OpenGLApp {
protected:
	Chess chess;

	Vertex lightPos;

	TextureID shadowMap;

	TextureID chessBoard;
	TextureID lightTex;
	ShaderID chessBoardVS, chessBoardFS;

	void initContext(Matrix &mat);
public:
	void selectPixelFormat(PixelFormat &pixelFormat);

	bool init();
	bool exit();
	bool load();
	bool unload();
	bool drawFrame();
	
	void resetCamera();
};

void MainApp::resetCamera(){
	position = Vertex(270, 170, 65);
	Wx = 0.64f;
	Wy = 1.83f;
	Wz = 0;
}

void MainApp::selectPixelFormat(PixelFormat &pixelFormat){
	pixelFormat.redBits   = 8;
	pixelFormat.greenBits = 8;
	pixelFormat.blueBits  = 8;
	pixelFormat.alphaBits = 8;

	pixelFormat.depthBits = 24;
}

bool MainApp::init(){
	if (!chess.load()) return false;

	srand(::time(NULL));

	return true;
}

bool MainApp::exit(){
	return true;
}

bool MainApp::load(){

	String supported, missing;

	if (GL_ARB_vertex_program)   supported += "GL_ARB_vertex_program\n";   else missing += "GL_ARB_vertex_program\n";
	if (GL_ARB_fragment_program) supported += "GL_ARB_fragment_program\n"; else missing += "GL_ARB_fragment_program\n";
	if (GL_EXT_texture3D)        supported += "GL_EXT_texture3D\n";        else missing += "GL_EXT_texture3D\n";
#ifdef _WIN32	
	if (WGL_ARB_render_texture)  supported += "WGL_ARB_render_texture\n";  else missing += "WGL_ARB_render_texture\n";
#else
	if (GLX_ATI_render_texture)  supported += "GLX_ATI_render_texture\n";  else missing += "GLX_ATI_render_texture\n";
#endif

    if (missing.getLength() > 0){
        ALERT("The following required OpenGL extensions are supported:\n\n" + supported +
			  "\nToo bad, these required OpenGL extensions aren't supported by the graphic card and driver you're using:\n\n" + missing +
              "\nThis application will now exit.", "Missing extensions");
        return false;
    }

	shadowMap = renderer->addRenderTexture(1024, 1024, false, false, RPF_RGBA8);


	if (!chess.initialize(renderer)) return false;

	lightTex = renderer->addParticleTexture(64);

	chessBoard  = renderer->addTexture(BASE_PATH"Textures/ChessBoard4.dds");
	textTexture = renderer->addTexture(BASE_PATH"Textures/font2.dds");

	projection.loadProjectionMatrix(90 * PI / 180, 0.75f, 1, 3000);

	chessBoardVS = renderer->addVertexShader(BASE_PATH"Chess/ChessBoard.vsh");
	if (glGetError() != GL_NO_ERROR){
		char *str = (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB);
		ALERT(str, "Error");
		return false;
	}

	chessBoardFS = renderer->addFragmentShader(GL_FRAGMENT_PROGRAM_ARB, BASE_PATH"Chess/ChessBoard.fsh");
	if (glGetError() != GL_NO_ERROR){
		char *str = (char *) glGetString(GL_PROGRAM_ERROR_STRING_ARB);
		ALERT(str, "Error");
		return false;
	}

	return true;
}

bool MainApp::unload(){
	return true;
}

void MainApp::initContext(Matrix &mat){
	//glClearColor(0.2f, 0.2f, 0.2f, 0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf(projection);

	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	glFrontFace(GL_CW);

	glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, 0, lightPos.x, lightPos.y, lightPos.z, 0);
	glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, 1, position.x, position.y, position.z, 0);

	glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, 2, ((float *) mat));
	glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, 3, ((float *) mat) + 4);
	glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, 4, ((float *) mat) + 8);
	glProgramEnvParameter4fvARB(GL_VERTEX_PROGRAM_ARB, 5, ((float *) mat) + 12);
}

bool MainApp::drawFrame(){
	float ct = fmodf(0.015f * currTime, 1);
	ct *= 4 * (1 - ct);
	ct *= ct;
	ct *= ct;
	ct *= ct;
	ct *= 3 * PI;

	lightPos = Vertex(315 * cosf(ct), 160, 315 * sinf(ct));

	Matrix lightMat;
	lightMat.loadRotateXY(-0.7f, 1.5f * PI - ct);
	lightMat.translate(-lightPos);
	Matrix tMat = transpose(projection * lightMat);

	chess.update(currTime);

	initContext(tMat);

	modelView.loadRotateZXY(-Wx, -Wy, -Wz);
	modelView.translate(-position);

    glMatrixMode(GL_MODELVIEW);
	glLoadMatrixf(modelView);


	renderer->changeRenderTarget(shadowMap);
	initContext(tMat);
	glClearColor(1, 1, 1, 1);
	glMatrixMode(GL_MODELVIEW);
	glLoadMatrixf(lightMat);
	chess.draw(renderer, DEPTH);
	renderer->changeRenderTarget();



	renderer->setVertexShader(chessBoardVS);
	renderer->setFragmentShader(chessBoardFS);
	renderer->setTextures(chessBoard);
	renderer->apply();

	glBegin(GL_QUADS);
		glTexCoord2f(0, 0);
		glVertex3f(-256, 0,  256);
		glTexCoord2f(1, 0);
		glVertex3f( 256, 0,  256);
		glTexCoord2f(1, 1);
		glVertex3f( 256, 0, -256);
		glTexCoord2f(0, 1);
		glVertex3f(-256, 0, -256);
	glEnd();

	glDisable(GL_DEPTH_TEST);
	Matrix shadowMat;
	shadowMat.loadShadowMatrix(Plane(0,1,0,0), lightPos);
    glMatrixMode(GL_MODELVIEW);
	glLoadMatrixf(modelView * shadowMat);
	chess.draw(renderer, BLANK);
	glEnable(GL_DEPTH_TEST);



	glLoadMatrixf(modelView);
	renderer->setTextures(chessBoard);
	renderer->setBlending(GL_ONE, GL_ONE);
	renderer->apply();

	glColor3f(0.2f, 0.2f, 0.2f);
	glBegin(GL_QUADS);
		glTexCoord2f(0, 0);
		glVertex3f(-256, 0,  256);
		glTexCoord2f(1, 0);
		glVertex3f( 256, 0,  256);
		glTexCoord2f(1, 1);
		glVertex3f( 256, 0, -256);
		glTexCoord2f(0, 1);
		glVertex3f(-256, 0, -256);
	glEnd();


	chess.draw(renderer, NORMAL, shadowMap);


	glColor3f(1, 1, 1);
	renderer->setTextures(lightTex);
	renderer->setBlending(GL_ONE, GL_ONE);
	renderer->apply();

	Vector dirX(modelView[0], modelView[4], modelView[8]);
	Vector dirY(modelView[1], modelView[5], modelView[9]);

	glBegin(GL_QUADS);
		glTexCoord2f(0, 1);
		glVertex3fv(lightPos + 20 * (-dirX + dirY));
		glTexCoord2f(1, 1);
		glVertex3fv(lightPos + 20 * ( dirX + dirY));
		glTexCoord2f(1, 0);
		glVertex3fv(lightPos + 20 * ( dirX - dirY));
		glTexCoord2f(0, 0);
		glVertex3fv(lightPos + 20 * (-dirX - dirY));
	glEnd();

	return true;
}

DECLARE_MAIN_APP(MainApp)
