#include "Mix/Tool/Win32/Core/Graphics/PolygonDivider.h"

namespace Mix{ namespace Tool{ namespace Win32{ namespace Graphics{

const float PolygonDivider::DEF_PLANETHICKNESS = FLT_EPSILON;//0.000001f;

PolygonDivider::PolygonDivider( void )
{
	m_PlaneThickness = PolygonDivider::DEF_PLANETHICKNESS;
}

void PolygonDivider::SetPlaneThickness( float value )
{
	m_PlaneThickness = value;
}

float PolygonDivider::GetPlaneThickness( void ) const
{
	return m_PlaneThickness;
}

void PolygonDivider::Process(	const Mix::Tool::Win32::Graphics::POLYGON& polygon,
								const Mix::Tool::Win32::Graphics::PLANE& plane,
								Mix::Tool::Win32::Graphics::POLYGON& frontPolygon,
								Mix::Tool::Win32::Graphics::POLYGON& backPolygon )
{
	unsigned int numVertex;

	frontPolygon.vertices.clear();
	backPolygon.vertices.clear();

	numVertex = polygon.vertices.size();
	if( numVertex == 0 )
	{
		return;
	}

	Mix::Tool::Win32::Graphics::VERTEX a = polygon.vertices[numVertex - 1];
	int aSide = ClassifyPointToPlane( a.pos, plane );

	unsigned int n;

	D3DXVECTOR3 v0;
	D3DXVECTOR3 v1;
	float l0;
	float l1;
	float ratio;

	for( n = 0; n < numVertex; n++ )
	{
		Mix::Tool::Win32::Graphics::VERTEX b = polygon.vertices[n];
		int bSide = ClassifyPointToPlane( b.pos, plane );

		if( bSide == PolygonDivider::CPP_FRONT )
		{
			if( aSide == PolygonDivider::CPP_BACK )
			{
				Mix::Tool::Win32::Graphics::VERTEX i;
				
				IntersectEdgeAgainstPlane( a.pos, b.pos, plane, i.pos );

				v0 = i.pos - a.pos;
				v1 = b.pos - a.pos;
				l0 = D3DXVec3Length( &v0 );
				l1 = D3DXVec3Length( &v1 );
				ratio = MIX_FLOAT_DIV( l0, l1 );

				::D3DXVec4Lerp( &( i.color ), &( a.color ), &( b.color ), ratio );
				::D3DXVec3Lerp( &( i.normal ), &( a.normal ), &( b.normal ), ratio );
				D3DXVec3Normalize( &( i.normal ), &( i.normal ) );
				::D3DXVec2Lerp( &( i.tex ), &( a.tex ), &( b.tex ), ratio );

				frontPolygon.vertices.push_back( i );
				backPolygon.vertices.push_back( i );
			}

			frontPolygon.vertices.push_back( b );
		}
		else if( bSide == PolygonDivider::CPP_BACK )
		{
			if( aSide == PolygonDivider::CPP_FRONT )
			{
				Mix::Tool::Win32::Graphics::VERTEX i;
				
				IntersectEdgeAgainstPlane( a.pos, b.pos, plane, i.pos );

				v0 = i.pos - a.pos;
				v1 = b.pos - a.pos;
				l0 = D3DXVec3Length( &v0 );
				l1 = D3DXVec3Length( &v1 );
				ratio = MIX_FLOAT_DIV( l0, l1 );

				::D3DXVec4Lerp( &( i.color ), &( a.color ), &( b.color ), ratio );
				::D3DXVec3Lerp( &( i.normal ), &( a.normal ), &( b.normal ), ratio );
				D3DXVec3Normalize( &( i.normal ), &( i.normal ) );
				::D3DXVec2Lerp( &( i.tex ), &( a.tex ), &( b.tex ), ratio );

				frontPolygon.vertices.push_back( i );
				backPolygon.vertices.push_back( i );
			}
			else if( aSide == PolygonDivider::CPP_ON )
			{
				backPolygon.vertices.push_back( a );
			}

			backPolygon.vertices.push_back( b );
		}
		else
		{
			frontPolygon.vertices.push_back( b );

			if( aSide == PolygonDivider::CPP_BACK )
			{
				backPolygon.vertices.push_back( b );
			}
		}

		a = b;
		aSide = bSide;
	}

	//頂点座標が平面と完全に重なっていると不完全なポリゴン( 頂点数が1以上、3未満 )ができあがるので、
	//ここで対処する
	if( ( frontPolygon.vertices.size() > 0 ) &&
		( frontPolygon.vertices.size() < 3 ) )
	{
		frontPolygon.vertices.clear();
		backPolygon.vertices = polygon.vertices;
	}
	else if(	( backPolygon.vertices.size() > 0 ) &&
				( backPolygon.vertices.size() < 3 ) )
	{
		backPolygon.vertices.clear();
		frontPolygon.vertices = polygon.vertices;
	}

	//マテリアル番号を付与
	frontPolygon.materialNo = polygon.materialNo;
	backPolygon.materialNo = polygon.materialNo;
}

int PolygonDivider::ClassifyPointToPlane( const D3DXVECTOR3& p, const Mix::Tool::Win32::Graphics::PLANE& plane )
{
	float dist = ( D3DXVec3Dot( &( plane.normal ), &p ) + plane.d );

	if( dist > m_PlaneThickness )
	{
		return PolygonDivider::CPP_FRONT;
	}
	else if( dist < -m_PlaneThickness )
	{
		return PolygonDivider::CPP_BACK;
	}

	return PolygonDivider::CPP_ON;
}

void PolygonDivider::IntersectEdgeAgainstPlane( const D3DXVECTOR3& a,
												const D3DXVECTOR3& b,
												const Mix::Tool::Win32::Graphics::PLANE& plane,
												D3DXVECTOR3& cp )
{
	D3DXVECTOR3 v = ( a - b );
	float w = ( D3DXVec3Dot( &( plane.normal ), &b ) + plane.d ) / D3DXVec3Dot( &( plane.normal ), &v );

	cp = ( b - ( v * w ) );
}

Mix::Tool::Win32::Object::TYPE PolygonDivider::GetType( void ) const
{
	return Mix::Tool::Win32::Object::GRAPHICS__POLYGON_DIVIDER;
}

}}}}
