#include "VisibleObject.h"
#include "Model.h"

#include "Utility/Log.h"

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

//////////////////////////////////////////////////////////////
VisibleObject::VisibleObject( ModelPtr pModel, ModelPtr pShadowModel )
	: m_pModel( pModel )
	, m_pShadowModel( pShadowModel )
	, m_BaseColor( pModel->MaterialColor() )
{
	BuildVisibleEdges( pModel );
}

//////////////////////////////////////////////////////////////
VisibleObject::~VisibleObject()
{
}

//////////////////////////////////////////////////////////////
void VisibleObject::BuildVisibleEdges( ModelPtr pModel )
{
	const Math::LocalVector& Offset = pModel->Offset();
	const LocalVectorVector& Points = pModel->Points();
	const Model::SurfaceVector& Surfaces = m_pModel->Surfaces();
	const Model::EdgeVector& Edges = pModel->Edges();
	for (size_t i=0; i<Edges.size(); ++i) {
		if (Surfaces[Edges[i].s0].Normal.Dot( Surfaces[Edges[i].s1].Normal ) > 0.9) {
			continue;
		}
		Edge NewEdge;
		NewEdge.first = Offset + Points[Edges[i].p0];
		NewEdge.second = Offset + Points[Edges[i].p1];
		m_VisibleEdges.push_back( NewEdge );
	}

	// have you checked the children?
	const ModelPtrVector& ModelKids = pModel->Kids();
	for (size_t i=0; i<ModelKids.size(); ++i) {
		BuildVisibleEdges( ModelKids[i] );
	}
}

//////////////////////////////////////////////////////////////
void VisibleObject::BuildSilhouette(const Math::LocalVector &LightPos, Silhouette &Sil) const
{
	Sil.Edges.clear();
	Sil.Kids.clear();
	if( !m_pShadowModel )
		return;

	BuildSilhouette( m_pShadowModel, LightPos, Sil );
}

//////////////////////////////////////////////////////////////
void VisibleObject::BuildSilhouette( ModelPtr pModel, const Math::LocalVector &LightPos, Silhouette &Sil) const
{
	Sil.Position = pModel->Offset();
	const Model::EdgeVector& Edges = pModel->Edges();
	if( !Edges.empty() ){
		const LocalVectorVector& Points = pModel->Points();
		const Model::SurfaceVector& Surfaces = pModel->Surfaces();
		const Math::LocalVector* pVertices = &(Points[0]);
		const Model::Surface* pSurfaces = &(Surfaces[0]);

		// precompute dot products so as to do less multiplies
		std::vector<float> DotProducts( Surfaces.size() );
		for( size_t i=0; i<Surfaces.size(); ++i )
			DotProducts[i] = LightPos.Dot( pSurfaces[i].Normal );

		Sil.Edges.reserve( 20 );
		// This is sort of a hack, but the STL is a bit too slow to pull this off
		// in debug mode, so dropping down to raw arrays.
		const float* pDots = &(DotProducts[0]);
		const Model::Edge* pEdges = &(Edges[0]);
		for( size_t i=0; i<Edges.size(); ++i ){
			float Dot1 = pDots[pEdges[i].s0];
			float Dot2 = pDots[pEdges[i].s1];
			Edge NewEdge;
			if( Dot1 <= 0 && Dot2 > 0 ) {
				NewEdge.first = pVertices[pEdges[i].p0];
				NewEdge.second = pVertices[pEdges[i].p1];
				Sil.Edges.push_back( NewEdge );
			}
			else if( Dot1 > 0 && Dot2 <= 0 ){
				NewEdge.first = pVertices[pEdges[i].p1];
				NewEdge.second = pVertices[pEdges[i].p0];
				Sil.Edges.push_back( NewEdge );
			}
		}
	}
	// have you checked the children?
	const ModelPtrVector& ModelKids = pModel->Kids();
	Sil.Kids.reserve( ModelKids.size() );
	auto itKids( ModelKids.cbegin() );
	auto itKidEnd( ModelKids.cend() );
	for( ; itKids!=itKidEnd; ++itKids ){
		Sil.Kids.push_back( Silhouette() );
		BuildSilhouette( *itKids, LightPos, Sil.Kids[Sil.Kids.size()-1] );
	}
}

}
}

