#include "ProxyHeaders.hpp"

struct AbstractVertex
{
	Point3D position;
	Normal3D normal;
	Vector2DOf<Scalar> uv;
	RGBAColor color;
};

//
//############################################################################
//#########################    PolygonMeshProxy    ###########################
//############################################################################
//

PolygonMeshProxy::ClassData*
	PolygonMeshProxy::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonMeshProxy::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			PolygonMeshProxyClassID,
			"PolygonMeshProxy",
			ChildProxy::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonMeshProxy::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
PolygonMeshProxy::PolygonMeshProxy(
	ClassData *class_data,
	SceneProxy *scene,
	GroupProxy *parent
):
	ChildProxy(class_data, scene, parent),
	activePolygonProxies(NULL),
	activeVertexProxies(NULL)
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
PolygonMeshProxy::~PolygonMeshProxy()
{
	Check_Object(this);
	Verify(activePolygonProxies.IsEmpty());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonMeshProxy::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonMeshProxy::GetCentroid(Point3D *centroid)
{
	Check_Object(this);
	Check_Object(centroid);

	//
	//-------------------------------------------------------------------
	// If we have no polygons, just return our local to world translation
	//-------------------------------------------------------------------
	//
	DynamicArrayOf<PolygonProxy*> polygons;
	unsigned polygon_count = UsePolygonArray(&polygons);
	Verify(polygon_count == polygons.GetLength());
	if (!polygon_count)
	{
		*centroid = Point3D::Identity;
		return;
	}

	//
	//---------------------------------------------
	// Sum up all the polygon centroids and weights
	//---------------------------------------------
	//
	*centroid = Point3D::Identity;
	for (unsigned i=0; i<polygon_count; ++i)
	{
		PolygonProxy *polygon = polygons[i];
		Check_Object(polygon);
		Point3D center;
		Scalar area = polygon->GetSurfaceAreaAndCentroid(&center);
		if (area > SMALL)
			center /= area;
		*centroid += center;
	}
	*centroid /= static_cast<Scalar>(polygon_count);
	DetachArrayReferences(&polygons);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonMeshProxy::Recenter()
{
	Check_Object(this);

	//
	//-------------------------------------------------------------------------
	// Now, get our centroid in our mesh space, and recenter the mesh around it
	// in model space
	//-------------------------------------------------------------------------
	//
	Point3D centroid;
	GetCentroid(&centroid);
	if (centroid != Point3D::Identity)
	{
		LinearMatrix4D matrix;
		GetLocalToParent(&matrix);
		LinearMatrix4D shift(true);
		shift.BuildTranslation(centroid);
		LinearMatrix4D new_origin;
		new_origin.Multiply(shift, matrix);
		SetLocalToParent(new_origin);

		//
		//-----------------------------------------------------------
		// Now we have to subtract the centroid from all our vertices
		//-----------------------------------------------------------
		//
		DynamicArrayOf<VertexProxy*> vertices;
		unsigned vertex_count = UseVertexArray(&vertices);
		for (unsigned i=0; i<vertex_count; ++i)
		{
			VertexProxy *vertex = vertices[i];
			Check_Object(vertex);
			Point3D position;
			vertex->GetPosition(&position);
			position -= centroid;
			vertex->SetPosition(position);
		}
		DetachArrayReferences(&vertices);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	PolygonMeshProxy::UseMultiStateArray(
		DynamicArrayOf<MultiState*> *states,
		DynamicArrayOf<unsigned> *index,
		DynamicArrayOf<unsigned> *count,
		DynamicArrayOf<PolygonProxy*> &polygons
	)
{
	Check_Object(this);
	Check_Object(index);
	Check_Object(count);
	Check_Object(states);

	//
	//----------------------------------------------------------------------
	// We have to evaluate each polygon within the mesh to see how many have
	// unique states, so set up arrays to hold proxies and matching indices
	//----------------------------------------------------------------------
	//
	unsigned polygon_count = polygons.GetLength();

	//
	//-------------------------------------------------------
	// Spin through the polygons, check for max. nr of states
	//-------------------------------------------------------
	//
	unsigned poly;

	states->SetLength(polygon_count);
	index->SetLength(polygon_count);
	count->SetLength(polygon_count);

	unsigned i, j;
	for (i=0; i<polygon_count; ++i)
		(*count)[i] = 0;
	unsigned unique_combinations = 0;

	//
	//------------------------------------------------------
	// Spin through the polygons, looking for unique entries
	//------------------------------------------------------
	//
	for (poly=0; poly < polygon_count; ++poly)
	{
		PolygonProxy *polygon = polygons[poly];
		Check_Object(polygon);
		MultiState poly_states;

		polygon->UseMultiState(&poly_states);

		//
		//-------------------------------------------------------------------
		// Get the state proxies, and see if they index one of
		// the prior entries
		//-------------------------------------------------------------------
		//
		for (i=0; i<unique_combinations;++i)
		{
			//
			//--------------------------------------------------------------
			// Make sure that the state combo has the same length
			//--------------------------------------------------------------
			//
			unsigned state_count = (*states)[i]->GetLength();
			if(	poly_states.GetLength() != state_count)
			{
				continue;
			}

			//
			//--------------------------------------------------------------
			// Make sure that the state and texture exists in either both
			// proxies or neither proxy
			//--------------------------------------------------------------
			//
			for(j=0;j<state_count;++j)
			{
				if ((!poly_states[j] && (*(*states)[i])[j]) || 
					(poly_states[j] && !(*(*states)[i])[j]))
				{
					break;
				}
			}

			if(j<state_count)
			{
				continue;
			}

			//
			//-----------------------------------------------
			// Make sure that the state and textures index
			//-----------------------------------------------
			//
			if(poly_states.IsEqualTo(*(*states)[i]))
			{
				break;
			}
		}

		//
		//-------------------------------------------------------------------
		// If this is a new entry, bump the combo count and store the proxies
		//-------------------------------------------------------------------
		//
		if (i == unique_combinations)
		{
			++unique_combinations;
			(*states)[i] = new MultiState (poly_states);
//			Verify((*states)[i]->GetLength() > 0);
		}

		//
		//-------------------------
		// Move to the next polygon
		//-------------------------
		//
		(*index)[poly] = i;
		++(*count)[i];
		
		poly_states.DetachReferences();
	}

	//
	//-------------------------------------
	// Release the texture/state proxies
	//-------------------------------------
	//
	states->SetLength(unique_combinations);
	count->SetLength(unique_combinations);
	return unique_combinations;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	PolygonMeshProxy::GetBoundingSphere(Sphere *sphere)
{
	Check_Object(this);
	Check_Pointer(sphere);

	//
	//---------------------------------------------------------------------
	// Put the center of the sphere at the centroid, then set the radius to
	// just contain the mesh
	//---------------------------------------------------------------------
	//
	DynamicArrayOf<VertexProxy*> vertices;
	unsigned vertex_count = UseVertexArray(&vertices);
	Verify(vertex_count == vertices.GetLength());

#if 1
	DynamicArrayOf<Point3D> points;
	points.SetLength(vertex_count);
	for (unsigned i=0; i<vertex_count; ++i)
	{
		VertexProxy *vertex = vertices[i];
		Check_Object(vertex);
		vertex->GetPosition(&points[i]);
	}
	DetachArrayReferences(&vertices);
	sphere->ComputeBounds(points);

	//
	//-------------------------------------
	// Make sure the radius is properly set
	//-------------------------------------
	//
	if (sphere->radius == -1.0f)
	{
		sphere->radius = 0.0f;
		return false;
	}
	return true;
#else
	GetCentroid(&sphere->center);
	sphere->radius = -1.0f;
	for (unsigned i=0; i<vertex_count; ++i)
	{
		VertexProxy *vertex = vertices[i];
		Check_Object(vertex);
		Point3D position;
		vertex->GetPosition(&position);
		position -= sphere->center;
		Scalar range = position.GetLengthSquared();
		if (range > sphere->radius)
			sphere->radius = range;
	}
	DetachArrayReferences(&vertices);

	//
	//-------------------------------------
	// Make sure the radius is properly set
	//-------------------------------------
	//
	if (sphere->radius == -1.0f)
	{
		sphere->radius = 0.0f;
		return false;
	}
	sphere->radius = Sqrt(sphere->radius) + SMALL;
	return true;
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonMeshProxy::DetachPolygonProxy(PolygonProxy* proxy)
{
	Check_Object(this);
	activePolygonProxies.RemovePlug(proxy);
	Verify(referenceCount > 1);
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonMeshProxy::DetachArrayReferences(
		DynamicArrayOf<MultiState*> *states
	)
{
	Check_Object(states);

	//
	//------------------------------
	// Add all the vertices together
	//------------------------------
	//
	unsigned i, state_count = states->GetLength();
	for (i=0; i<state_count; ++i)
	{
		Check_Object((*states)[i]);
		(*states)[i]->DetachReferences();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonMeshProxy::DetachArrayReferences(
		DynamicArrayOf<PolygonProxy*> *polygons
	)
{
	Check_Object(polygons);

	//
	//------------------------------
	// Add all the vertices together
	//------------------------------
	//
	unsigned polygon_count = polygons->GetLength();
	for (unsigned i=0; i<polygon_count; ++i)
	{
		Check_Object((*polygons)[i]);
		(*polygons)[i]->DetachReference();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonMeshProxy::DetachArrayReferences(
		DynamicArrayOf<VertexProxy*> *vertices
	)
{
	Check_Object(vertices);

	//
	//------------------------------
	// Add all the vertices together
	//------------------------------
	//
	unsigned vertex_count = vertices->GetLength();
	for (unsigned i=0; i<vertex_count; ++i)
	{
		Check_Object((*vertices)[i]);
		(*vertices)[i]->DetachReference();
	}
}
