#include "ProxyHeaders.hpp"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ChildProxy::SplitByState(SplitByStateProcess *process)
{
	Check_Object(this);
	Check_Object(process);
	process->SplitCallback(this, SplitByStateProcess::StatusCheck);
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	PolygonMeshProxy::SplitByState(SplitByStateProcess *process)
{
	Check_Object(this);
	Check_Object(process);

	//
	//--------------------------
	// Make sure we can continue
	//--------------------------
	//
	process->SplitCallback(this, SplitByStateProcess::StatusCheck);
	if (!process->continueProcess)
		return true;

	//
	//-----------------------------------------------------------------------
	// We have to evaluate each polygon within the mesh to see how many have
	// unique texture/material combinations, so set up arrays to hold proxies
	// and matching indices
	//-----------------------------------------------------------------------
	//
	DynamicArrayOf<unsigned> match;
	DynamicArrayOf<unsigned> count;
	DynamicArrayOf<MultiState*> states;
	DynamicArrayOf<PolygonProxy*> polygons;
	UsePolygonArray(&polygons);
	unsigned unique_combinations =
		UseMultiStateArray(&states, &match, &count, polygons);

	//
	//-------------------------------------------------------------------------
	// If there are no unique entries, than this is a spurious polygon mesh and
	// should be destroyed
	//-------------------------------------------------------------------------
	//
	if (!unique_combinations)
	{
		Destroy();
		return false;
	}

	//
	//---------------------------------------------------------
	// If there is only one unique entry, this mesh is OK as is
	//---------------------------------------------------------
	//
	if (unique_combinations == 1)
	{
		DetachArrayReferences(&polygons);
		states[0]->DetachReferences();
		return true;
	}

	//
	//-----------------------------------------------------------------------
	// There were more than one entry, so this mesh must be split up, so find
	// the parent and get the local to parent transform of the base mesh
	//-----------------------------------------------------------------------
	//
	GroupProxy *parent = GetParentGroupProxy();
	LinearMatrix4D m;
	GetLocalToParent(&m);

	//
	//------------------------------------------------------------------------
	// For each unique combination, create a new polygon mesh, and give it the
	// same transform as the original polygon mesh
	//------------------------------------------------------------------------
	//
	unsigned total_polys = polygons.GetLength();
	for (unsigned i=0; i<unique_combinations; ++i)
	{
		PolygonMeshProxy *mesh;
		if (parent)
		{
			Check_Object(parent);
			mesh = parent->AppendNewPolygonMeshProxy();
		}
		else
			mesh = GetSceneProxy()->AppendNewPolygonMeshProxy();
		mesh->SetLocalToParent(m);

		//
		//--------------------------------------------------------------------
		// For each unique combination, we need to create the mesh data
		// structures, so we need to first identify the polygons to be grouped
		// together
		//--------------------------------------------------------------------
		//
		Verify(count[i]>0);
		DynamicArrayOf<PolygonProxy*> new_polys(count[i]);
		unsigned poly_count = 0;
		for (unsigned j=0; j<total_polys && poly_count<count[i]; ++j)
		{
			PolygonProxy *polygon = polygons[j];
			Check_Object(polygon);
			if (match[j] == i)
				new_polys[poly_count++] = polygon;
		}
		Verify(poly_count == count[i]);

		//
		//----------------------------------------------------------------------
		// Now that we have the appropriate polygons sorted out, put them in the
		// mesh
		//----------------------------------------------------------------------
		//
		mesh->AddPolygons(process, new_polys);
		
		states[i]->DetachReferences();

		mesh->DetachReference();
	}

	//
	//---------------------
	// Clean up the proxies
	//---------------------
	//
	DetachArrayReferences(&polygons);
	Destroy();
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	GroupProxy::SplitByState(SplitByStateProcess *process)
{
	Check_Object(this);
	Check_Object(process);

	//
	//--------------------------
	// Make sure we can continue
	//--------------------------
	//
	process->SplitCallback(this, SplitByStateProcess::StatusCheck);
	if (!process->continueProcess)
		return true;

	//
	//-------------------
	// Split the children
	//-------------------
	//
	unsigned child_count = GetChildCount();
	ChildProxy *child = UseFirstChildProxy();
	for (unsigned i=0; i<child_count; ++i)
	{
		Check_Object(child);
		ChildProxy *next = child->UseNextSiblingProxy();
		if (child->SplitByState(process))
			child->DetachReference();
		child = next;
		if (!process->continueProcess)
		{
			if (child)
				child->DetachReference();
			break;
		}
	}

	//
	//-------------------------------------------
	// Make sure to discard any remaining proxies
	//-------------------------------------------
	//
	if (child)
		child->DetachReference();
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SceneProxy::SplitByState(SplitByStateProcess *process)
{
	Check_Object(this);
	Check_Object(process);

	//
	//--------------------------
	// Make sure we can continue
	//--------------------------
	//
	process->SplitCallback(this, SplitByStateProcess::StatusCheck);
	if (!process->continueProcess)
		return;

	//
	//-------------------
	// Split the children
	//-------------------
	//
	unsigned child_count = GetChildCount();
	ChildProxy *child = UseFirstChildProxy();
	unsigned i;
	for (i=0; i<child_count; ++i)
	{
		Check_Object(child);
		ChildProxy *next = child->UseNextSiblingProxy();
		if (child->SplitByState(process))
			child->DetachReference();
		child = next;
		if (!process->continueProcess)
		{
			if (child)
				child->DetachReference();
			break;
		}
	}

	//
	//------------------------------------------------------------------------
	// Make sure to discard any remaining proxies, and bypass the second phase
	// of the sort if the process has been aborted
	//------------------------------------------------------------------------
	//
	if (child)
		child->DetachReference();
}
