#include "RenderDevice.h"

#include "Utilities/Log.h"

#include "GL/gl.h"
#include "GL/glu.h"

namespace BlueCore
{

//------------------------------------------------------------------------------
void RenderDevice::WindowResizeSlot(int width, int height)
{
	glViewport( 0, 0, width, height);
	_ViewportWidth = width;
	_ViewportHeight = height;
}

//------------------------------------------------------------------------------
RenderDevice::RenderDevice(RenderWindow* renderWindow) :
	_RenderWindow(renderWindow)
{
	if (_RenderWindow.valid())
	{
		_RenderWindow->WindowResizeSignal.connect(this,
				&RenderDevice::WindowResizeSlot);

		_RenderWindow->WindowCloseSignal.connect(this,
				&RenderDevice::WindowCloseSlot);

		_ViewportWidth = _RenderWindow->getWidth();
		_ViewportHeight = _RenderWindow->getHeight();
		glViewport( 0, 0, _ViewportWidth, _ViewportHeight);
	}

	clog << ">>> RenderDevice constructed..."<< endlog;
}

//------------------------------------------------------------------------------
RenderDevice::~RenderDevice()
{
	clog << ">>> RenderDevice destructed..."<< endlog;
}

//------------------------------------------------------------------------------
void RenderDevice::WindowCloseSlot()
{
	DeviceShutdownSignal();
}

//------------------------------------------------------------------------------
int RenderDevice::getViewportWidth()
{
	return _ViewportWidth;
}

//------------------------------------------------------------------------------
int RenderDevice::getViewportHeight()
{
	return _ViewportHeight;
}

//------------------------------------------------------------------------------
void RenderDevice::begin2D()
{
	// prepare state
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glNormal3f( 0.0, 0.0, 1.0);

	// set projection matrix
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	//int newheight = (_width / 16.0) * 9.0;
	gluOrtho2D( 0, _ViewportWidth-1, 0, _ViewportHeight-1);

	// prepare model matrix
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();
}

//------------------------------------------------------------------------------
void RenderDevice::end2D()
{
	// restore projection matrix
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();

	// restore model matrix
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	// restore old state
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);
}

//------------------------------------------------------------------------------
void RenderDevice::clear()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}

//------------------------------------------------------------------------------
void RenderDevice::setAmbientLight(float r, float g, float b)
{
	GLfloat lightAmbient[] =
	{ r, g, b, 1.0f };
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lightAmbient);
}

//------------------------------------------------------------------------------
/*
 void RenderDevice::renderShadowVolume(Mesh *mesh,
 const Vector3Float &direction, const Vector3Float &extrude)
 {
 // Calculate visibility
 unsigned int i, frontface_count = 0;
 for (i=0; i < mesh->shadowfaces.count(); i++)
 {
 if (mesh->shadowfaces[i].plane.distance(extrude) < 0)
 {
 shadow_faces[i].backFace = false;
 frontface_count++;
 }
 else
 {
 shadow_faces[i].backFace = true;
 }
 }

 unsigned int max_edges = (frontface_count) * 3;
 if (_extrudeVertexBuffer.count() < (max_edges * 4))
 {
 clog << ">>> increase shadow buffers to "<< max_edges << " Edges"<< endline;
 _extrudeVertexBuffer.create( (max_edges + 100) * 4);
 }

 if (_extrudeIndexBuffer.count() < (max_edges * 6))
 _extrudeIndexBuffer.create( (max_edges + 100) * 6);

 // fill the buffer
 unsigned int j, k;
 extrude_quad_count = 0;
 Vertex *face_vertices[3];
 for (i=0; i<shadow_faces.count(); i++)
 {
 if (shadow_faces[i].backFace == false)
 {
 for (j=0; j<3; j++)
 {
 k = shadow_faces[i].neighbours[j];
 if ( (k == 0) || (shadow_faces[k-1].backFace == true))
 {
 unsigned int src_index_offset = i * 3;
 face_vertices[0]
 = &mesh->vertex_buffer[ mesh->index_buffer[ src_index_offset ] ];
 face_vertices[1]
 = &mesh->vertex_buffer[ mesh->index_buffer[ src_index_offset + 1] ];
 face_vertices[2]
 = &mesh->vertex_buffer[ mesh->index_buffer[ src_index_offset + 2] ];

 #ifdef DEBUG_SILHOUETTE
 glBegin( GL_LINES );
 glVertex3f( face_vertices[j]->point.x, face_vertices[j]->point.y, face_vertices[j]->point.z );
 glVertex3f( face_vertices[(j+1)%3]->point.x, face_vertices[(j+1)%3]->point.y, face_vertices[(j+1)%3]->point.z );
 glEnd();
 #endif

 unsigned int vertex_offset = extrude_quad_count*4;
 _extrudeVertexBuffer[vertex_offset] = face_vertices[j]->point;
 _extrudeVertexBuffer[vertex_offset+1] = face_vertices[(j+1)%3]->point;
 _extrudeVertexBuffer[vertex_offset+2] = face_vertices[(j+1)%3]->point + extrude_vector;
 _extrudeVertexBuffer[vertex_offset+3] = face_vertices[j]->point
 + extrude_vector;

 unsigned int index_offset = extrude_quad_count*6;
 _extrudeIndexBuffer[index_offset] = vertex_offset;
 _extrudeIndexBuffer[index_offset+1] = vertex_offset + 3;
 _extrudeIndexBuffer[index_offset+2] = vertex_offset + 1;
 _extrudeIndexBuffer[index_offset+3] = vertex_offset + 1;
 _extrudeIndexBuffer[index_offset+4] = vertex_offset + 3;
 _extrudeIndexBuffer[index_offset+5] = vertex_offset + 2;

 extrude_quad_count++;
 }
 }
 }

 }

 if (_capIndexBuffer.count() < mesh->index_buffer.count() )
 _capIndexBuffer.create(mesh->index_buffer.count() );

 bf = 0;
 ff = 0;

 for (i=0; i<shadow_faces.count(); i++)
 {
 unsigned int dst_offset, src_offset = i*3;

 if (shadow_faces[i].backFace == false)
 {
 dst_offset = ff*3;
 ff++;
 }
 else
 {
 dst_offset = _capIndexBuffer.count() - (bf + 1)*3;
 bf++;
 }

 _capIndexBuffer[dst_offset] = mesh->index_buffer[src_offset];
 _capIndexBuffer[dst_offset+1] = mesh->index_buffer[src_offset+1];
 _capIndexBuffer[dst_offset+2] = mesh->index_buffer[src_offset+2];
 }

 _lastOrientation = _AbsoluteRotation;
 }

 if (_extrudeVertexBuffer.count() > 0)
 {
 // draw the volume
 glPushAttrib( GL_ALL_ATTRIB_BITS);

 glDisable(GL_TEXTURE_2D);
 glDisable(GL_LIGHTING);

 glEnable( GL_DEPTH_TEST);
 glDepthMask(GL_FALSE);

 #if defined(DEBUG_SILHOUETTE) || defined(DEBUG_CAPS) || defined(DEBUG_VOLUME)
 glColorMask( 1, 1, 1, 1 );
 glDepthFunc(GL_LEQUAL);
 #else
 glColorMask(0, 0, 0, 0);
 glDepthFunc(GL_LESS);
 #endif

 glEnable(GL_STENCIL_TEST);
 glStencilFunc(GL_ALWAYS, 0, ~0);
 glStencilMask( ~0);

 glEnable(GL_CULL_FACE);

 glMatrixMode ( GL_MODELVIEW);
 glPushMatrix();
 Matrix4x4 m(_AbsoluteRotation, _AbsoluteTranslation);
 glMultMatrixd ( ( GLdouble * ) &m.m );

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

 ShaderManager::getSingleton()->useShaderProgram( 0);

 #ifndef DEBUG_SILHOUETTE

 glEnableClientState( GL_VERTEX_ARRAY);
 glDisableClientState( GL_TEXTURE_COORD_ARRAY);
 glDisableClientState( GL_NORMAL_ARRAY);
 glDisableClientState( GL_ARRAY_BUFFER_ARB);
 glDisableClientState( GL_ELEMENT_ARRAY_BUFFER_ARB);

 for (int p=0; p<2; p++)
 {
 if (p==0)
 {
 glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
 glCullFace(GL_FRONT);
 //glColorMask(0, 1, 0, 0);
 }
 else
 {
 glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
 glCullFace(GL_BACK);
 //glColorMask(1, 0, 0, 0);
 }

 if (GLEW_ARB_vertex_buffer_object )
 {
 glBindBufferARB ( GL_ARRAY_BUFFER_ARB, 0);
 glBindBufferARB ( GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
 }
 //    #if !defined(DEBUG_CAPS) || defined(DEBUG_VOLUME)
 glVertexPointer ( 3, GL_FLOAT, 0, _extrudeVertexBuffer.data() );
 glDrawElements (
 GL_TRIANGLES,
 extrude_quad_count * 6,
 GL_UNSIGNED_SHORT,
 _extrudeIndexBuffer.data());
 //    #endif

 //    #if !defined(DEBUG_VOLUME) || defined(DEBUG_CAPS)
 // draw caps
 mesh->vertex_buffer.bind();
 //glVertexPointer ( 3, GL_FLOAT, sizeof(Vertex), mesh->vertex_buffer.data() );
 glDrawElements (
 GL_TRIANGLES,
 ff*3,
 GL_UNSIGNED_SHORT,
 _capIndexBuffer.data());
 //&_capIndexBuffer[_capIndexBuffer.count() - bf*3]);

 glPushMatrix();
 glTranslatef(extrude_vector.x, extrude_vector.y, extrude_vector.z);
 glDrawElements (
 GL_TRIANGLES,
 bf*3,
 GL_UNSIGNED_SHORT,
 &_capIndexBuffer[_capIndexBuffer.count() - bf*3]);
 //_capIndexBuffer.data() );
 glPopMatrix();
 //         #endif  
 }

 #endif

 glPopAttrib();

 glPopMatrix();
 }
 */
} // namespace BlueCore
