#include "ProxyHeaders.hpp"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BinSortProcess::BinSortProcess(NotationFile *data_file):
	Process(data_file)
{
	Check_Object(data_file);

	binSize = 20;
	Page *page = data_file->FindPage("BinSort");
	if (page)
		page->GetEntry("BinSize", reinterpret_cast<int*>(&binSize));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ChildProxy::BinSort(BinSortProcess *process)
{
	Check_Object(this);
	Check_Object(process);
	process->BinSortCallback(this);
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	PolygonMeshProxy::BinSort(BinSortProcess *process)
{
	Check_Object(this);
	Check_Object(process);

	//
	//---------------------------------------
	// Make sure that the process says its OK
	//---------------------------------------
	//
	process->BinSortCallback(this);
	if (!process->continueProcess)
		return true;

	//
	//------------------------------------------------------------------------
	// Make a list of all the polygons, and if its empty, destroy the mesh and
	// return
	//------------------------------------------------------------------------
	//
	DynamicArrayOf<PolygonProxy*> polygons;
	UsePolygonArray(&polygons);
	unsigned polygon_count = polygons.GetLength();
	if (!polygon_count)
	{
		Destroy();
		return false;
	}

	//
	//---------------------------------------------------------
	// If the polygon mesh is already small enough, just return
	//---------------------------------------------------------
	//
	if (polygon_count <= process->binSize)
	{
		DetachArrayReferences(&polygons);
		return true;
	}

	//
	//---------------------------------
	// 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))
	{
		DetachArrayReferences(&polygons);
		return true;
	}

	//
	//-------------------------------------------------------------------------
	// The mesh is too big, so we have to cut it up.  Make a group proxy to
	// hold the new mesh collection
	//-------------------------------------------------------------------------
	//
	GroupProxy *parent = GetParentGroupProxy();
	GroupProxy *group;
	if (parent)
	{
		Check_Object(parent);
		group = parent->AppendNewGroupProxy();
	}
	else
		group = GetSceneProxy()->AppendNewGroupProxy();

	//
	//----------------------------------
	// Set the position of the group
	//----------------------------------
	//
	LinearMatrix4D m;
	if (GetLocalToParent(&m))
	{
		SetLocalToParent(LinearMatrix4D::Identity);
		group->SetLocalToParent(m);
	}
	MString name;
	if (GetName(&name))
	{
		SetName(NULL);
		group->SetName(name);
	}

	//
	//-----------------------------------------------------------------------
	// Create two new meshes under the group for the mesh to be split up into
	//-----------------------------------------------------------------------
	//
	PolygonMeshProxy *bin_a = group->AppendNewPolygonMeshProxy();
	Check_Object(bin_a);
	PolygonMeshProxy *bin_b = group->AppendNewPolygonMeshProxy();
	Check_Object(bin_b);
	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;
	}

	//
	//---------------------------------
	// Now add the polygons to each bin
	//---------------------------------
	//
	Verify(count_a>0);
	group_a.SetLength(count_a);
	bin_a->AddPolygons(process, group_a);
	Verify(count_b>0);
	group_b.SetLength(count_b);
	bin_b->AddPolygons(process, group_b);

	//
	//-------------------------------------------------------------------
	// Now that the mesh has been split up, Bin_Sort each smaller mesh and
	// destroy this mesh
	//-------------------------------------------------------------------
	//
	if (bin_a->BinSort(process))
		bin_a->DetachReference();
	if (!process->continueProcess)
	{
		bin_b->DetachReference();
		group->DetachReference();
		DetachArrayReferences(&polygons);
		Destroy();
		return false;
	}
	if (bin_b->BinSort(process))
		bin_b->DetachReference();

	//
	//-----------------------------------------
	// Now set the bounding sphere of the group
	//-----------------------------------------
	//
	group->DetachReference();
	DetachArrayReferences(&polygons);
	Destroy();
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	GroupProxy::BinSort(BinSortProcess *process)
{
	Check_Object(this);
	Check_Object(process);

	//
	//---------------------------------------
	// Make sure that the process says its OK
	//---------------------------------------
	//
	process->BinSortCallback(this);
	if (!process->continueProcess)
		return true;

	//
	//-----------------------------------------------------------------------
	// If this group has two or fewer children, we won't have to split it, so
	// just return
	//-----------------------------------------------------------------------
	//
	unsigned child_count = GetChildCount();
	ChildProxy *child = UseFirstChildProxy();
	unsigned i;
	if (child_count <= 2)
	{
		for (i=0; child; ++i)
		{
			Check_Object(child);
			ChildProxy *next = child->UseNextSiblingProxy();
			if (child->BinSort(process))
				child->DetachReference();
			child = next;
			if (!process->continueProcess)
			{
				if (child)
				{
					Check_Object(child);
					child->DetachReference();
					break;
				}
			}
		}
		return true;
	}

	//
	//---------------------------------
	// Make a list of all the centroids
	//---------------------------------
	//
	DynamicArrayOf<Point3D> centroids(child_count);
	for (i=0; child; ++i)
	{
		Check_Object(child);
		ChildProxy *next = child->UseNextSiblingProxy();
		Point3D local_centroid;
		child->GetCentroid(&local_centroid);
		LinearMatrix4D m;
		child->GetLocalToParent(&m);
		centroids[i].Multiply(local_centroid, m);
		child->DetachReference();
		child = next;
	}
	Verify(!child);

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

	//
	//----------------------------------------------------------------------
	// The collection is too big, so we have to cut it up.  Make a group
	// proxy to hold the new collection
	//----------------------------------------------------------------------
	//
	GroupProxy *group_a = AppendNewGroupProxy();
	GroupProxy *group_b = AppendNewGroupProxy();

	//
	//------------------------------------------------------------------
	// Calculate the dividing plane, and then sort each of the centroids
	// against the plane
	//------------------------------------------------------------------
	//
	child = UseFirstChildProxy();
	for (i=0; i<child_count; ++i)
	{
		Check_Object(child);
		ChildProxy *next = child->UseNextSiblingProxy();
		Scalar distance = plane.GetDistanceTo(centroids[i]);
		if (distance > SMALL)
			child->TransferAndAppendToParentGroup(group_b);
		else
			child->TransferAndAppendToParentGroup(group_a);
		child->DetachReference();
		child = next;
	}
	if (child)
		child->DetachReference();

	//
	//------------------------------------------------------------------------
	// Now that the group has been split up, Bin_Sort each smaller mesh and
	// destroy this mesh
	//------------------------------------------------------------------------
	//
	Verify(group_a->GetChildCount() > 0);
	if (group_a->BinSort(process))
		group_a->DetachReference();
	if (!process->continueProcess)
	{
		group_b->DetachReference();
		return true;
	}
	Verify(group_b->GetChildCount() > 0);
	if (group_b->BinSort(process))
		group_b->DetachReference();
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SceneProxy::BinSort(BinSortProcess *process)
{
	Check_Object(this);
	Check_Object(process);

	//
	//---------------------------------------
	// Make sure that the process says its OK
	//---------------------------------------
	//
	process->BinSortCallback(this);
	if (!process->continueProcess)
		return;

	//
	//------------------------------------------------------------------------
	// If this group has two or fewer children, we won't have to split it,
	// so just return
	//------------------------------------------------------------------------
	//
	unsigned child_count = GetChildCount();
	ChildProxy *child = UseFirstChildProxy();
	unsigned i;
	if (child_count <= 2)
	{
		for (i=0; child; ++i)
		{
			Check_Object(child);
			ChildProxy *next = child->UseNextSiblingProxy();
			if (child->BinSort(process))
				child->DetachReference();
			child = next;
			if (!process->continueProcess)
			{
				if (child)
				{
					Check_Object(child);
					child->DetachReference();
					break;
				}
			}
		}
		return;
	}

	//
	//---------------------------------
	// Make a list of all the centroids
	//---------------------------------
	//
	DynamicArrayOf<Point3D> centroids(child_count);
	for (i=0; child; ++i)
	{
		Check_Object(child);
		ChildProxy *next = child->UseNextSiblingProxy();
		Point3D local_centroid;
		child->GetCentroid(&local_centroid);
		LinearMatrix4D m;
		child->GetLocalToParent(&m);
		centroids[i].Multiply(local_centroid, m);
		child->DetachReference();
		child = next;
	}
	Verify(!child);

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

	//
	//----------------------------------------------------------------------
	// Create two new meshes under the group for the mesh to be split up
	// into
	//----------------------------------------------------------------------
	//
	GroupProxy *a = AppendNewGroupProxy();
	GroupProxy *b = AppendNewGroupProxy();

	//
	//------------------------------------------------------------------
	// Calculate the dividing plane, and then sort each of the centroids
	// against the plane
	//------------------------------------------------------------------
	//
	child = UseFirstChildProxy();
	for (i=0; i<child_count; ++i)
	{
		ChildProxy *next = child->UseNextSiblingProxy();
		if (plane.GetDistanceTo(centroids[i]) > SMALL)
			child->TransferAndAppendToParentGroup(a);
		else
			child->TransferAndAppendToParentGroup(b);
		child->DetachReference();
		child = next;
	}
	child->DetachReference();

	//
	//------------------------------------------------------------------------
	// Now that the group has been split up, Bin_Sort each smaller mesh and
	// destroy this mesh
	//------------------------------------------------------------------------
	//
	Verify(a->GetChildCount() > 0);
	if (a->BinSort(process))
		a->DetachReference();
	if (!process->continueProcess)
		b->DetachReference();
	else
	{
		Verify(b->GetChildCount() > 0);
		if (b->BinSort(process))
			b->DetachReference();
	}
}
