#include "ProxyHeaders.hpp"

//
//############################################################################
//############################    SceneProxy    ##############################
//############################################################################
//

SceneProxy::ClassData*
	SceneProxy::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SceneProxy::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			SceneProxyClassID,
			"SceneProxy",
			GenericProxy::DefaultData
		);
	Register_Object(DefaultData);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SceneProxy::SceneProxy(ClassData *class_data):
	GenericProxy(class_data),
	activeChildProxies(NULL)
{
	Check_Object(this);
	stateLibrary = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
SceneProxy::~SceneProxy()
{
	Check_Object(this);
	Verify(activeChildProxies.IsEmpty());
	Verify(!stateLibrary);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SceneProxy::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);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	SceneProxy::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*
	SceneProxy::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*
	SceneProxy::InsertMatchingChildProxy(
		CopyProcess *process,
		ChildProxy *child,
		ChildProxy *before
	)
{
	Check_Object(this);
	Check_Object(process);
	Check_Object(child);
	Check_Object(before);
	Verify(!before->GetParentGroupProxy());

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	SceneProxy::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
	SceneProxy::DetachChildProxy(ChildProxy* proxy)
{
	Check_Object(this);
	activeChildProxies.RemovePlug(proxy);
	Verify(referenceCount > 1);
	DetachReference();
}
