#include "ElementProxyHeaders.hpp"

//
//############################################################################
//#########################    ElementGroupProxy    ##########################
//############################################################################
//

MemoryBlock*
	ElementGroupProxy::AllocatedMemory = NULL;
ElementGroupProxy::ClassData*
	ElementGroupProxy::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementGroupProxy::InitializeClass()
{
	Verify(!AllocatedMemory);
	AllocatedMemory =
		new MemoryBlock(
			sizeof(ElementGroupProxy),
			10,
			10,
			"ElementGroupProxy"
		);
	Register_Object(AllocatedMemory);

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			ElementGroupProxyClassID,
			"ElementGroupProxy",
			GroupProxy::DefaultData
		);
	Register_Object(DefaultData);
}

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

	Unregister_Object(AllocatedMemory);
	delete AllocatedMemory;
	AllocatedMemory = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementGroupProxy::Destroy()
{
	//
	//-------------------------------
	// Make sure we are OK to destroy
	//-------------------------------
	//
	Check_Object(this);
	Verify(referenceCount == 1);
	Verify(activeChildProxies.IsEmpty());

	//
	//--------------------------
	// Destroy the group element
	//--------------------------
	//
	Unregister_Object(proxiedGroup);
	delete proxiedGroup;
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementGroupProxy::ElementGroupProxy(
	ElementSceneProxy *scene,
	GroupProxy *parent,
	GroupElement *group,
	ChainIterator *iterator
):
	GroupProxy(DefaultData, scene, parent),
	proxiedGroup(group),
	siblingIterator(iterator)
{
	Check_Pointer(this);
	Check_Pointer(proxiedGroup);
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementGroupProxy::~ElementGroupProxy()
{
	Check_Object(this);
	Unregister_Object(siblingIterator);
	delete siblingIterator;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementGroupProxy::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
	Check_Object(siblingIterator);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementGroupProxy::TransferAndAppendToParentGroup(GroupProxy *parent)
{
	Check_Object(this);

	//
	//--------------------------------------------
	// Delete our attachment to our current parent
	//--------------------------------------------
	//
	Check_Pointer(proxiedGroup);
	proxiedGroup->DetachFromParent();
	ElementGroupProxy *old_parent =
		Cast_Object(ElementGroupProxy*, GetParentGroupProxy());
	if (old_parent)
	{
		Check_Object(old_parent);
		old_parent->DetachChildProxy(this);
	}
	else
	{
		Check_Object(GetSceneProxy());
		GetSceneProxy()->DetachChildProxy(this);
	}

	//
	//---------------------------------------------------------------------
	// If a parent is specified, then attach to it, otherwise attach to the
	// scene
	//---------------------------------------------------------------------
	//
	GroupElement *parent_rec;
	parentProxy = parent;
	if (parent)
	{
		ElementGroupProxy *element_parent =
			Cast_Object(ElementGroupProxy*, parent);
		parent_rec = element_parent->GetProxiedGroup();
		parent->AttachChildProxy(this);
	}
	else
	{
		ElementSceneProxy *scene = GetSceneProxy();
		Check_Object(scene);
		parent_rec = scene->GetProxiedScene();
		scene->AttachChildProxy(this);
	}
	Check_Pointer(parent_rec);
	parent_rec->AttachChild(proxiedGroup);
	proxiedGroup->Sync();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ChildProxy*
	ElementGroupProxy::UseNextSiblingProxy()
{
	Check_Object(this);

	//
	//------------------------------------------------------------------------
	// Clone our iterator, then move it and have the scene figure out the type
	// of proxy to create
	//------------------------------------------------------------------------
	//
	Check_Object(siblingIterator);
	ChainIterator *iterator = siblingIterator->MakeClone();
	Register_Object(iterator);
	iterator->Next();
	return GetSceneProxy()->InterpretElement(GetParentGroupProxy(), iterator);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ChildProxy*
	ElementGroupProxy::UsePreviousSiblingProxy()
{
	Check_Object(this);

	//
	//------------------------------------------------------------------------
	// Clone our iterator, then move it and have the scene figure out the type
	// of proxy to create
	//------------------------------------------------------------------------
	//
	Check_Object(siblingIterator);
	ChainIterator *iterator = siblingIterator->MakeClone();
	Register_Object(iterator);
	iterator->Previous();
	return GetSceneProxy()->InterpretElement(GetParentGroupProxy(), iterator);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementGroupProxy::GetName(MString *name)
{
	Check_Object(this);
	Check_Object(name);
	Check_Object(proxiedGroup);
	name->AllocateLength(0);
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementGroupProxy::SetName(const char* name)
{
	Check_Object(this);
	Check_Object(proxiedGroup);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementGroupProxy::GetLocalToParent(LinearMatrix4D *matrix)
{
	Check_Object(this);
	Check_Pointer(matrix);
	Check_Object(proxiedGroup);
	*matrix = proxiedGroup->GetLocalToParent();
	return *matrix != LinearMatrix4D::Identity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementGroupProxy::SetLocalToParent(const LinearMatrix4D &matrix)
{
	Check_Object(this);
	Check_Object(&matrix);
	Check_Object(proxiedGroup);
	if (matrix == LinearMatrix4D::Identity)
		proxiedGroup->SetLocalToParentToIdentity();
	else
		proxiedGroup->SetLocalToParent(matrix);
	proxiedGroup->Sync();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementGroupProxy::GetCentroid(Point3D *center)
{
	Check_Object(this);
	Check_Pointer(center);
	Check_Object(proxiedGroup);
	if (proxiedGroup->m_localOBB.sphereRadius > 0.0f)
		*center = proxiedGroup->m_localOBB.localToParent;
	else
		GroupProxy::GetCentroid(center);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementGroupProxy::GetOBB(OBB *obb)
{
	Check_Object(this);
	Check_Pointer(obb);
	Check_Object(proxiedGroup);

	//
	//-----------------------------------------------
	// Make sure that this proxy thinks it has an OBB
	//-----------------------------------------------
	//
	if (!proxiedGroup->IsBoundedByOBB())
	{
		return false;
	}

	//
	//------------------------------
	// Copy the data into the sphere
	//------------------------------
	//
	*obb = proxiedGroup->m_localOBB;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementGroupProxy::SetOBB(const OBB &obb)
{
	Check_Object(this);
	Check_Object(&obb);
	Check_Object(proxiedGroup);

	//
	//-------------------------------------------------------------------------
	// Set the element into sphere mode, and copy the sphere data into the OBB.
	// Then sync it up so everyone is happy
	//-------------------------------------------------------------------------
	//
	proxiedGroup->m_localOBB = obb;
	proxiedGroup->SetOBBMode();
	proxiedGroup->SetVolumeCullMode();
	proxiedGroup->Sync();
}

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

	//
	//------------------------------
	// Copy the data into the sphere
	//------------------------------
	//
	sphere->center = proxiedGroup->m_localOBB.localToParent;
	sphere->radius = proxiedGroup->m_localOBB.sphereRadius;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementGroupProxy::SetBoundingSphere(const Sphere &sphere)
{
	Check_Object(this);
	Check_Object(&sphere);
	Check_Object(proxiedGroup);

	//
	//-------------------------------------------------------------------------
	// Set the element into sphere mode, and copy the sphere data into the OBB.
	// Then sync it up so everyone is happy
	//-------------------------------------------------------------------------
	//
	proxiedGroup->m_localOBB.localToParent.BuildTranslation(sphere.center);
	proxiedGroup->m_localOBB.sphereRadius = sphere.radius;
	proxiedGroup->SetSphereMode();
	proxiedGroup->SetVolumeCullMode();
	proxiedGroup->Sync();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	ElementGroupProxy::GetChildCount()
{
	Check_Object(this);
	Check_Object(proxiedGroup);
	ChainIteratorOf<Element*> children(&proxiedGroup->m_group);
	return children.GetSize();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
PolygonMeshProxy*
	ElementGroupProxy::AppendNewPolygonMeshProxy()
{
	Check_Object(this);

	gos_PushCurrentHeap(ElementRenderer::ShapeElement::s_Heap);
	ShapeElement *new_mesh = new ShapeElement;
	Register_Object(new_mesh);
	gos_PopCurrentHeap();

	Check_Object(proxiedGroup);
	proxiedGroup->AttachChild(new_mesh);
	ChainIteratorOf<Element*> *iterator =
		new ChainIteratorOf<Element*>(&proxiedGroup->m_group);
	Register_Object(iterator);
	ElementPolygonMeshProxy *proxy =
		ElementPolygonMeshProxy::MakeProxy(
			GetSceneProxy(),
			this,
			new_mesh,
			iterator
		);
	Register_Object(proxy);
	return proxy;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
PolygonMeshProxy*
	ElementGroupProxy::InsertNewPolygonMeshProxy(ChildProxy *before)
{
	Check_Object(this);
	Check_Object(before);
	Verify(before->GetParentGroupProxy() == this);

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GroupProxy*
	ElementGroupProxy::AppendNewGroupProxy()
{
	Check_Object(this);

	GroupElement *new_hierarchy = new GroupElement();
	Register_Object(new_hierarchy);
	Check_Object(proxiedGroup);
	proxiedGroup->AttachChild(new_hierarchy);
	ChainIteratorOf<Element*> *iterator =
		new ChainIteratorOf<Element*>(&proxiedGroup->m_group);
	Register_Object(iterator);
	ElementGroupProxy *proxy =
		ElementGroupProxy::MakeProxy(
			GetSceneProxy(),
			this,
			new_hierarchy,
			iterator
		);
	Register_Object(proxy);
	return proxy;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GroupProxy*
	ElementGroupProxy::InsertNewGroupProxy(ChildProxy *before)
{
	Check_Object(this);
	Check_Object(before);
	Verify(before->GetParentGroupProxy() == this);

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ChildProxy*
	ElementGroupProxy::UseFirstChildProxy()
{
	Check_Object(this);
	Check_Object(proxiedGroup);
	ChainIteratorOf<Element*> *elements =
		new ChainIteratorOf<Element*>(&proxiedGroup->m_group);
	Register_Object(elements);
	return GetSceneProxy()->InterpretElement(this, elements);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ChildProxy*
	ElementGroupProxy::UseLastChildProxy()
{
	Check_Object(this);
	STOP(("Not implemented"));
	return NULL;
}
