#include "ProxyHeaders.hpp"

//
//############################################################################
//############################    GroupProxy    ##############################
//############################################################################
//

GroupProxy::ClassData*
	GroupProxy::DefaultData = NULL;

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GroupProxy::~GroupProxy()
{
	Check_Object(this);
	Verify(activeChildProxies.IsEmpty());
}

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

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

	//
	//-----------------------------------------------
	// If we have no children, just return our origin
	//-----------------------------------------------
	//
	unsigned child_count = GetChildCount();
	*centroid = Point3D::Identity;
	if (!child_count)
		return;

	//
	//--------------------------------------------
	// Otherwise, just average our child centroids
	//--------------------------------------------
	//
	ChildProxy *child = UseFirstChildProxy();
	while (child)
	{
		Check_Object(child);
		ChildProxy *next = child->UseNextSiblingProxy();
		Point3D local_centroid;
		child->GetCentroid(&local_centroid);
		LinearMatrix4D m;
		child->GetLocalToParent(&m);
		Point3D world_centroid;
		world_centroid.Multiply(local_centroid, m);
		*centroid += world_centroid;
		child->DetachReference();
		child = next;
	}
	Verify(!child);
	*centroid /= static_cast<Scalar>(child_count);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GroupProxy::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);
		matrix(3,0) += centroid.x;
		matrix(3,1) += centroid.y;
		matrix(3,2) += centroid.z;
		SetLocalToParent(matrix);

		//
		//----------------------------------------------------------
		// First, allow each child hierarchy to remove redundancies
		//----------------------------------------------------------
		//
		matrix = LinearMatrix4D::Identity;
		centroid.Negate(centroid);
		matrix.BuildTranslation(centroid);
		unsigned child_count = GetChildCount();
		ChildProxy *child = UseFirstChildProxy();
		for (unsigned i=0; i<child_count; ++i)
		{
			Check_Object(child);
			ChildProxy *next = child->UseNextSiblingProxy();
			child->TransformLocalToParent(matrix);
			child->DetachReference();
			child = next;
		}
		if (child)
			child->DetachReference();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GroupProxy::SortAndAddPolygons(
		BinSortProcess *process,
		Stuff::DynamicArrayOf<PolygonProxy*> &polygons
	)
{
	Check_Object(this);
	Check_Object(process);
	Check_Object(&polygons);
	Verify(process->binSize > 0);

	//
	//---------------------------------------------------------------
	// See if we can just directly add these polygons into a new mesh
	//---------------------------------------------------------------
	//
	unsigned polygon_count = polygons.GetLength();
	if (polygon_count <= process->binSize)
	{
Copy_Mesh:
		PolygonMeshProxy *mesh = AppendNewPolygonMeshProxy();
		Check_Object(mesh);
		mesh->AddPolygons(process, polygons);
		mesh->DetachReference();
		return;
	}

	//
	//---------------------------------
	// Make a list of all the centroids
	//---------------------------------
	//
	DynamicArrayOf<Point3D> centroids(polygon_count);
	unsigned i;
	PolygonProxy *polygon;
	for (i=0; i<polygon_count; ++i)
	{
		polygon = polygons[i];
		Check_Object(polygon);
		Scalar area = polygon->GetSurfaceAreaAndCentroid(&centroids[i]);
		if (area > SMALL)
			centroids[i] /= area;
	}

	//
	//------------------------------------------------------------------------
	// Calculate the dividing plane, and if none can be found, don't do nothin
	//------------------------------------------------------------------------
	//
	Plane plane;
	if (!plane.ComputeBestDividingPlane(centroids))
		goto Copy_Mesh;

	//
	//-----------------------------------------------------------------------
	// Create two new meshes under the group for the mesh to be split up into
	//-----------------------------------------------------------------------
	//
	DynamicArrayOf<PolygonProxy*>
		group_a(polygon_count),
		group_b(polygon_count);
	unsigned
		count_a = 0,
		count_b = 0;

	//
	//------------------------------------------------------------------
	// Sort each of the centroids against the plane into one of two bins
	//------------------------------------------------------------------
	//
	for (i=0; i<polygon_count; ++i)
	{
		polygon = polygons[i];
		Check_Object(polygon);
		if (plane.GetDistanceTo(centroids[i]) < 0.0f)
			group_b[count_b++] = polygon;
		else
			group_a[count_a++] = polygon;
	}
	group_a.SetLength(count_a);
	group_b.SetLength(count_b);

	//
	//------------------
	// Now sort each bin
	//------------------
	//
	SortAndAddPolygons(process, group_a);
	SortAndAddPolygons(process, group_b);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	GroupProxy::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
	//---------------------------------------------------------------------
	//
	GetCentroid(&sphere->center);
	sphere->radius = -1.0f;
	ChildProxy *child = UseFirstChildProxy();
	while (child)
	{
		Check_Object(child);
		ChildProxy *next = child->UseNextSiblingProxy();

		//
		//----------------------------------------------------------
		// Transform's the child's bounding sphere into parent space
		//----------------------------------------------------------
		//
		LinearMatrix4D child_to_parent;
		child->GetLocalToParent(&child_to_parent);
		Sphere child_sphere;
		child->GetBoundingSphere(&child_sphere);
		Point3D position;
		position.Multiply(child_sphere.center, child_to_parent);

		//
		//-----------------------------------------------------------------
		// Now stretch the radius of the bounding sphere so that it totally
		// includes the child sphere
		//-----------------------------------------------------------------
		//
		position -= sphere->center;
		Scalar range = position.GetLength() + child_sphere.radius;
		if (range > sphere->radius)
		{
			sphere->radius = range;
		}
		child->DetachReference();
		child = next;
	}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ChildProxy*
	GroupProxy::AppendMatchingChildProxy(
		CopyProcess *process,
		ChildProxy *child
	)
{
	Check_Object(this);
	Check_Object(process);
	Check_Object(child);

	if (child->IsDerivedFrom(PolygonMeshProxy::DefaultData))
	{
		PolygonMeshProxy *proxy = AppendNewPolygonMeshProxy();
		Check_Object(proxy);
		proxy->Copy(process, Cast_Object(PolygonMeshProxy*, child));
		return proxy;
	}
	else if (child->IsDerivedFrom(GroupProxy::DefaultData))
	{
		GroupProxy *proxy = AppendNewGroupProxy();
		Check_Object(proxy);
		proxy->Copy(process, Cast_Object(GroupProxy*, child));
		return proxy;
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ChildProxy*
	GroupProxy::InsertMatchingChildProxy(
		CopyProcess *process,
		ChildProxy *child,
		ChildProxy *before
	)
{
	Check_Object(this);
	Check_Object(process);
	Check_Object(child);
	Check_Object(before);
	Verify(before->GetParentGroupProxy() == this);

	STOP(("Not implemented"));
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GroupProxy::FindNamedChildren(
		DynamicArrayOf<ChildProxy*> *children,
		const char* prefix,
		bool matching
	)
{
	Check_Object(this);
	Check_Object(children);
	Check_Pointer(prefix);

	//
	//----------------------
	// Get the prefix length
	//----------------------
	//
	int size = strlen(prefix);

	//
	//---------------------------------
	// Find out how many children match
	//---------------------------------
	//
	unsigned count = 0;
	ChildProxy *child = UseFirstChildProxy();
	while (child)
	{
		ChildProxy *next = child->UseNextSiblingProxy();
		MString name;
		if (child->GetName(&name))
		{
			if ((!_strnicmp(name, prefix, size)) == matching)
				++count;
		}
		child->DetachReference();
		child = next;
	}

	//
	//------------------------------------------------------------------------
	// Set the array length, and if we have any matching children, fill in the
	// array with those proxies
	//------------------------------------------------------------------------
	//
	children->SetLength(count);
	if (count > 0)
	{
		child = UseFirstChildProxy();
		count = 0;
		while (child)
		{
			ChildProxy *next = child->UseNextSiblingProxy();

			//
			//-----------------------------------------------------------------
			// If the child has a name and it matches what we are looking for,
			// store it in the array and bump the reference count so it doesn't
			// go away
			//-----------------------------------------------------------------
			//
			MString name;
			if (child->GetName(&name))
			{
				if ((!_strnicmp(name, prefix, size)) == matching)
				{
					(*children)[count++] = child;
					child->AttachReference();
				}
			}
			child->DetachReference();
			child = next;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	GroupProxy::DetachChildProxy(ChildProxy* proxy)
{
	Check_Object(this);
	activeChildProxies.RemovePlug(proxy);
	Verify(referenceCount > 1);
	DetachReference();
}
