#include "ElementProxyHeaders.hpp"

#include <ElementRenderer\ShapeLODElement.hpp>
#include <MLR\MLRShape.hpp>
//
//############################################################################
//#########################    ElementSceneProxy    ##########################
//############################################################################
//

ElementSceneProxy::ClassData*
	ElementSceneProxy::DefaultData = NULL;

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

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementSceneProxy::ElementSceneProxy(
	GroupElement *scene,
	const char *texture_path
):
	SceneProxy(DefaultData),
	proxiedScene(scene)
{
	Check_Pointer(this);
	Check_Object(scene);
	stateLibrary = MLRStatePoolProxy::MakeProxy(this, texture_path);
	Register_Object(stateLibrary);
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementSceneProxy::ElementSceneProxy(GroupElement *scene):
	SceneProxy(DefaultData),
	proxiedScene(scene)
{
	Check_Pointer(this);
	Check_Object(scene);
	stateLibrary = MLRStatePoolProxy::MakeProxy(this);
	Register_Object(stateLibrary);
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementSceneProxy::~ElementSceneProxy()
{
	Check_Object(this);
	Check_Object(stateLibrary);
	Cast_Object(MLRStatePoolProxy*, stateLibrary)->CloseLibrary();
	Verify(stateLibrary->GetReferenceCount() == 1);
	stateLibrary->DetachReference();
	stateLibrary = NULL;
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementSceneProxy::GetOBB(OBB *obb)
{
	Check_Object(this);
	Check_Pointer(obb);
	Check_Object(proxiedScene);

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementSceneProxy::SetOBB(const OBB &obb)
{
	Check_Object(this);
	Check_Object(&obb);
	Check_Object(proxiedScene);

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementSceneProxy::GetBoundingSphere(Sphere *sphere)
{
	Check_Object(this);
	Check_Pointer(sphere);
	Check_Object(proxiedScene);

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementSceneProxy::SetBoundingSphere(const Sphere &sphere)
{
	Check_Object(this);
	Check_Object(&sphere);
	Check_Object(proxiedScene);

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StateLibrary*
	ElementSceneProxy::GetStateLibrary()
{
	Check_Object(this);
	return stateLibrary;
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	ElementSceneProxy::GetChildCount()
{
	Check_Object(this);
	Check_Object(proxiedScene);
	ChainIteratorOf<Element*> elements(&proxiedScene->m_group);
	return elements.GetSize();
}

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

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
PolygonMeshProxy*
	ElementSceneProxy::AppendNewPolygonMeshProxy()
{
	Check_Object(this);
	gos_PushCurrentHeap(ElementRenderer::ShapeElement::s_Heap);
	ShapeElement *new_mesh = new ShapeElement;
	Register_Object(new_mesh);
	gos_PopCurrentHeap();
	Check_Object(proxiedScene);
	proxiedScene->AttachChild(new_mesh);
	ChainIteratorOf<Element*> *iterator =
		new ChainIteratorOf<Element*>(&proxiedScene->m_group);
	Register_Object(iterator);
	ElementPolygonMeshProxy *proxy =
		ElementPolygonMeshProxy::MakeProxy(this, NULL, new_mesh, iterator);
	Register_Object(proxy);
	return proxy;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
PolygonMeshProxy*
	ElementSceneProxy::InsertNewPolygonMeshProxy(ChildProxy *before)
{
	Check_Object(this);
	Check_Object(before);
	Verify(!before->GetParentGroupProxy());
	STOP(("Not implemented"));
	return NULL;
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ChildProxy*
	ElementSceneProxy::InterpretElement(
		GroupProxy *parent,
		ChainIterator *iterator
	)
{
	Check_Object(this);
	Check_Object(iterator);

	//
	//----------------------------------------------------------------------
	// Make sure the iterator is pointing at something, and if not, go ahead
	// and delete the iterator
	//----------------------------------------------------------------------
	//
Try_Again:
	Element *child = static_cast<Element*>(iterator->GetCurrentItem());
	if (!child)
	{
		Unregister_Object(iterator);
		delete iterator;
		return NULL;
	}

	//
	//---------------------------------------------------------------
	// Figure out the type of record, then make the appropriate proxy
	//---------------------------------------------------------------
	//
	Check_Object(child);
	ChildProxy *proxy=NULL;
	switch (child->GetClassID())
	{
	case ShapeElementClassID:
		proxy =
			ElementPolygonMeshProxy::MakeProxy(
				this,
				parent,
				Cast_Object(ShapeElement*, child),
				iterator
			);
		break;

	case GroupElementClassID:
		proxy =
			ElementGroupProxy::MakeProxy(
				this,
				parent,
				Cast_Object(GroupElement*, child),
				iterator
			);
		break;

	case ListElementClassID:
		proxy =
			ElementListProxy::MakeProxy(
				this,
				parent,
				Cast_Object(ListElement*, child),
				iterator
			);
		break;
	case ShapeLODElementClassID:
		{
			ShapeLODElement *element = Cast_Object(ShapeLODElement*,child);

			MidLevelRenderer::MLRShape *lod_shape = NULL;
			const ElementRenderer::LODElement::Entry *lod_entry = NULL;
			element->GetLOD(0,&lod_shape,&lod_entry);
			
			ElementRenderer::ShapeElement *element_shape = new ElementRenderer::ShapeElement;
			Register_Object(element_shape);
			element_shape->SetMLRShape(lod_shape);
			lod_shape->DetachReference();
			lod_shape->DetachReference();

			proxy =
				ElementPolygonMeshProxy::MakeProxy(
					this,
					parent,
					Cast_Object(ShapeElement*, element_shape),
					iterator
				);
			break;
		}

	default:
		iterator->Next();
		goto Try_Again;
	}
	Register_Object(proxy);
	return proxy;
}
