#include "ShadowRenderer.h"
#include "OpenGLMatrix.h"
#include "OpenGLUtils.h"

#include <Utility/Log.h>

//////////////////////////////////////////////////////////////
namespace Dubious
{
namespace Renderer
{

//////////////////////////////////////////////////////////////
ShadowRenderer::ShadowRenderer()
{
}

//////////////////////////////////////////////////////////////
ShadowRenderer::~ShadowRenderer()
{
}

namespace
{
struct BufferData
{
	BufferData( const Math::LocalVector& V )
	{
		x = V.X();
		y = V.Y();
		z = V.Z();
	}
	float x, y, z;
};
typedef std::vector<BufferData> BufferDataVect;
const int SIZEOF_BUFFER_DATA = 3*sizeof(float);

void BuildShadowVolume( const Math::LocalVector& LightPosition, const Math::LocalVector& Offset, const VisibleObject::Silhouette& Sil, BufferDataVect& Points )
{
	Math::LocalVector Position = Offset + Sil.Position;
	Math::LocalVector EndPoint = LightPosition * -1;
	EndPoint.SetLength( 100.0f );
	for( size_t i=0; i<Sil.Edges.size(); ++i ){
		const Math::LocalVector& p1 = Sil.Edges[i].first;
		const Math::LocalVector& p2 = Sil.Edges[i].second;
		// T1
		Points.push_back( Position );
		Points.push_back( Position+p2 );
		Points.push_back( Position+p1 );
		// T2
		Points.push_back( Position+p1 );
		Points.push_back( Position+p2 );
		Points.push_back( EndPoint );
	}
	for( size_t i=0; i<Sil.Kids.size(); ++i ){
		BuildShadowVolume( LightPosition, Position, Sil.Kids[i], Points );
	}
}
}

//////////////////////////////////////////////////////////////
void ShadowRenderer::RenderShadowVolume( VisibleObjectPtr pObject, const Math::LocalVector& LightPosition )
{
	VisibleObject::Silhouette Sil;
	pObject->BuildSilhouette( LightPosition, Sil );
	
	OpenGLMatrix Matrix;
	float m[16];
	pObject->CoordinateSpace().GLMatrix( m );
	Matrix.MultMatrix( m );

	BufferDataVect Data;
	BuildShadowVolume( LightPosition, Math::LocalVector(), Sil, Data );

	std::vector<unsigned short> Indices;
	for (unsigned short i=0; i<Data.size(); ++i) {
		Indices.push_back( i );
	}

	// Unbind any data from the GL_ARRAY_BUFFER so we can
	// just use the vertices in local memory
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, SIZEOF_BUFFER_DATA, &(Data[0]));

	// The actual shadow volume drawing
	glCullFace( GL_FRONT );
	glStencilOp( GL_KEEP, GL_INCR, GL_KEEP );
	glDrawElements(GL_TRIANGLES, Indices.size(), GL_UNSIGNED_SHORT, &(Indices[0]));

	glCullFace( GL_BACK );
	glStencilOp( GL_KEEP, GL_DECR, GL_KEEP );
	glDrawElements(GL_TRIANGLES, Indices.size(), GL_UNSIGNED_SHORT, &(Indices[0]));

}

}
}
