#include "Renderer.h"

PFNGLACTIVETEXTUREARBPROC				glActiveTextureARB;
PFNGLCLIENTACTIVETEXTUREARBPROC			glClientActiveTextureARB;
PFNGLMULTITEXCOORD2FARBPROC				glMultiTexCoord2fARB;

PFNGLDELETEBUFFERSARBPROC				glDeleteBuffersARB;
PFNGLGENBUFFERSARBPROC					glGenBuffersARB;
PFNGLBINDBUFFERARBPROC					glBindBufferARB;
PFNGLBUFFERDATAARBPROC					glBufferDataARB;

PFNGLSHADERSOURCEARBPROC				glShaderSourceARB;
PFNGLCREATESHADEROBJECTARBPROC			glCreateShaderObjectARB;
PFNGLCREATEPROGRAMOBJECTARBPROC			glCreateProgramObjectARB;
PFNGLDELETEOBJECTARBPROC				glDeleteObjectARB;
PFNGLCOMPILESHADERARBPROC				glCompileShaderARB;
PFNGLATTACHOBJECTARBPROC				glAttachObjectARB;
PFNGLLINKPROGRAMARBPROC					glLinkProgramARB;
PFNGLVALIDATEPROGRAMARBPROC				glValidateProgramARB;
PFNGLUSEPROGRAMOBJECTARBPROC			glUseProgramObjectARB;
PFNGLGETOBJECTPARAMETERIVARBPROC		glGetObjectParameterivARB;
PFNGLGETUNIFORMLOCATIONARBPROC			glGetUniformLocationARB;
PFNGLGETINFOLOGARBPROC					glGetInfoLogARB;
PFNGLUNIFORM1IARBPROC					glUniform1iARB;
PFNGLUNIFORM1FARBPROC					glUniform1fARB;

const double CRenderer::ClipReflect[] = {0,1,0,0.01};
const double CRenderer::ClipRefract[] = {0,-1,0,0.3};

void	CCamera::Apply(){
	gluLookAt( 
		Pos.x, Pos.y, Pos.z, 
		Target.x, Target.y, Target.z,
		0,1,0 );
}

void	CCamera::ApplyReflected(){
	glScalef( 1,-1,1 );
	gluLookAt( 
		Pos.x, -Pos.y, Pos.z, 
		Target.x, Target.y, Target.z,
		0,-1,0 );
}

bool	CRenderer::ExistsExtension( const char* Ext ){
	const char* extAll	= (const char *)glGetString(GL_EXTENSIONS);
	uint	size	= strlen( Ext );
	while ( extAll ){
		if ( !strncmp( extAll, Ext, size ) && (*(extAll+size)==0 || *(extAll+size)==' ') ){
			return true;
		}
		extAll = strchr( extAll, ' ' );
		if ( extAll ) extAll++;
	}
	return false;
}

bool	CRenderer::InitExtensions(){
	if ( !ExistsExtension( "GL_ARB_multitexture" ) ) return false;
	glActiveTextureARB			= ( PFNGLACTIVETEXTUREARBPROC )wglGetProcAddress( "glActiveTextureARB" );
	glClientActiveTextureARB	= ( PFNGLCLIENTACTIVETEXTUREARBPROC )wglGetProcAddress( "glClientActiveTextureARB" );
	glMultiTexCoord2fARB		= ( PFNGLMULTITEXCOORD2FARBPROC )wglGetProcAddress( "glMultiTexCoord2fARB" );

	if ( !ExistsExtension( "GL_ARB_vertex_buffer_object" ) ) return false;
	glDeleteBuffersARB			= ( PFNGLDELETEBUFFERSARBPROC )wglGetProcAddress( "glDeleteBuffersARB" );
	glGenBuffersARB				= ( PFNGLGENBUFFERSARBPROC )wglGetProcAddress( "glGenBuffersARB" );
	glBindBufferARB				= ( PFNGLBINDBUFFERARBPROC )wglGetProcAddress( "glBindBufferARB" );
	glBufferDataARB				= ( PFNGLBUFFERDATAARBPROC )wglGetProcAddress( "glBufferDataARB" );

	if ( !ExistsExtension( "GL_ARB_shading_language_100" ) ) return false;
	glShaderSourceARB			= ( PFNGLSHADERSOURCEARBPROC )wglGetProcAddress( "glShaderSourceARB" );
	glCreateShaderObjectARB		= ( PFNGLCREATESHADEROBJECTARBPROC )wglGetProcAddress( "glCreateShaderObjectARB" );
	glCreateProgramObjectARB	= ( PFNGLCREATEPROGRAMOBJECTARBPROC )wglGetProcAddress( "glCreateProgramObjectARB" );
	glDeleteObjectARB			= ( PFNGLDELETEOBJECTARBPROC )wglGetProcAddress( "glDeleteObjectARB" );
	glCompileShaderARB			= ( PFNGLCOMPILESHADERARBPROC )wglGetProcAddress( "glCompileShaderARB" );
	glAttachObjectARB			= ( PFNGLATTACHOBJECTARBPROC )wglGetProcAddress( "glAttachObjectARB" );
	glLinkProgramARB			= ( PFNGLLINKPROGRAMARBPROC )wglGetProcAddress( "glLinkProgramARB" );
	glValidateProgramARB		= ( PFNGLVALIDATEPROGRAMARBPROC )wglGetProcAddress( "glValidateProgramARB" );
	glUseProgramObjectARB		= ( PFNGLUSEPROGRAMOBJECTARBPROC )wglGetProcAddress( "glUseProgramObjectARB" );
	glGetObjectParameterivARB	= ( PFNGLGETOBJECTPARAMETERIVARBPROC )wglGetProcAddress( "glGetObjectParameterivARB" );
	glGetUniformLocationARB		= ( PFNGLGETUNIFORMLOCATIONARBPROC )wglGetProcAddress( "glGetUniformLocationARB" );		
	glGetInfoLogARB				= ( PFNGLGETINFOLOGARBPROC )wglGetProcAddress( "glGetInfoLogARB" );
	glUniform1iARB				= ( PFNGLUNIFORM1IARBPROC )wglGetProcAddress( "glUniform1iARB" );
	glUniform1fARB				= ( PFNGLUNIFORM1FARBPROC )wglGetProcAddress( "glUniform1fARB" );
	return true;
}

bool	CRenderer::Init( HWND hwnd ){
	hWND = hwnd;
	GLuint pixel_format;
	PIXELFORMATDESCRIPTOR pfd = { sizeof( PIXELFORMATDESCRIPTOR ), 1,
				PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA,
				32 , 0,0, 0,0, 0,0, 0,0, 0,0,0,0,0,
				24 , 0,0, PFD_MAIN_PLANE,0,0,0,0 };
	if ( !( hDC = GetDC( hWND ) ) ) return false;
	if ( !( pixel_format = ChoosePixelFormat( hDC, &pfd ) ) )	return false;
	if ( !SetPixelFormat( hDC, pixel_format, &pfd ) )			return false;
	if ( !( hRC	= wglCreateContext( hDC ) ) )					return false;
	if ( !wglMakeCurrent( hDC, hRC ) )							return false;
	glClearColor( 0.23f,0.23f,0.23f,1 );
	glLightModeli( GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT );
	glHint( GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST );
	glHint( GL_FOG_HINT,GL_NICEST );
	glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
	glDepthFunc( GL_LEQUAL );
	glFrontFace( GL_CCW );
	glCullFace( GL_FRONT );
	if ( !InitExtensions() ) return false;
	ilInit();
	return true;
}

void	CRenderer::Deinit(){
	ReflectMap.Free();
	RefractMap.Free();
	Island.Free();
	Bottom.Free();
	Sky.Free();
	if ( hWND ){
		wglMakeCurrent( hDC, hRC );
		wglDeleteContext( hRC );
		ReleaseDC( hWND, hDC );
		hWND = NULL;
	}
}

void	CRenderer::RefreshViewport(){
	glViewport( 0, 0, ScrWidth, ScrHeight );
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	gluPerspective( 40.0f, (float)ScrWidth / (float)ScrHeight, 0.01f, 1000.0f );
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
}

void	CRenderer::OnResize( int Width, int Height ){
	ScrWidth	= Width;
	ScrHeight	= Height;
	RefreshViewport();
}

void	CRenderer::RenderToReflectMap(){
	glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
	glFrontFace( GL_CW );
	ReflectMap.SetViewport();
	Camera.ApplyReflected();
	glGetFloatv( GL_PROJECTION_MATRIX, Water.MtxProjectionReflect );
	glGetFloatv( GL_MODELVIEW_MATRIX, Water.MtxModelReflect );
	glClipPlane( GL_CLIP_PLANE0, ClipReflect );
	glEnable( GL_CLIP_PLANE0 );
	glEnable( GL_CULL_FACE );
	RenderObjects();
	glDisable( GL_CLIP_PLANE0 );
	ReflectMap.RenderTo();
	glFrontFace( GL_CCW );
}

void	CRenderer::RenderToRefractMap(){
	glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
	RefractMap.SetViewport();
	Camera.Apply();
	glGetFloatv( GL_PROJECTION_MATRIX, Water.MtxProjectionRefract );
	glGetFloatv( GL_MODELVIEW_MATRIX, Water.MtxModelRefract );
	glClipPlane( GL_CLIP_PLANE0, ClipRefract );
	glEnable( GL_CLIP_PLANE0 );
	glDisable( GL_CULL_FACE );
	RenderObjects();
	glDisable( GL_CLIP_PLANE0 );
	RefractMap.RenderTo();
}

void	CRenderer::RenderObjects(){
	IslandMap.Bind();
	Island.Render();

	SkyMap.Bind();
	Sky.Render();

	static float a = 0;
	a+=0.4f * DeltaTime;
	glPushMatrix();
	glTranslatef( 0,0.23f+sinf(a*0.7f)*0.2f,2);
	glRotatef( a*50.0f, 0,1,0 );
	glTranslatef( 0,0,0.4f );
	glRotatef( 90, 0,1,0 );
	glRotatef( -20.0f + cosf(a*4.0f)*10, 0,0,1 );
	glScalef( 0.04f, 0.04f, 0.04f );
	BiplaneMap.Bind();
	Biplane.Render();
	glPopMatrix();
}

void	CRenderer::RenderScene(){
	DeltaTime = SyncTimer.Elapsed();
	SyncTimer.Start();

	glEnable( GL_DEPTH_TEST );
	glLoadIdentity();

	RenderToReflectMap();
	RenderToRefractMap();

	glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT );
	RefreshViewport();
	Camera.Apply();

	RenderObjects();
	Water.SetDelta( DeltaTime );
	Water.Render( ReflectMap, RefractMap, NormalMap );

	glFlush();
	SwapBuffers( hDC );
}

bool	CRenderer::LoadResources(){
	bool Status = true;
	ReflectMap.CreateVoid( 512, 512 );
	RefractMap.CreateVoid( 512, 512 );
	if ( !SkyMap.LoadImage( "Sky.jpg" ) ) Status = false;
	if ( !IslandMap.LoadImage( "Island.jpg" ) ) Status = false;
	if ( !NormalMap.LoadImage( "Waves.jpg" ) ) Status = false;
	if ( !BiplaneMap.LoadImage( "Biplane.jpg" ) ) Status = false;
	if ( !Island.Load3ds( "Island.3ds" ) ) Status = false;
	if ( !Sky.Load3ds( "Sky.3ds" ) ) Status = false;
	if ( !Biplane.Load3ds( "Biplane.3ds" ) ) Status = false;
	if ( !Water.Generate() ) Status = false;
	return Status;
}

