#include "AdeptHeaders.hpp"

#include "VideoHeaders.hpp"
#include <MLR\MLRShape.hpp>
#include <MLR\MLRCulturShape.hpp>

using namespace MidLevelRenderer;

//############################################################################
//###############################  ShapeComponent  ################################
//############################################################################

//
// Class Data Support
//
Component::ClassData* ShapeComponent::DefaultData = NULL;
HashOf<MidLevelRenderer::MLRShape*, ResourceID>* ShapeComponent::s_shapeHash = NULL;
ResourceID ShapeComponent::s_Erf_Resource;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ShapeComponent::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			ShapeComponentClassID,
			"Adept::ShapeComponent",
			BaseClass::DefaultData,
			0,
			NULL
		);
	Check_Object(DefaultData);

	Verify(!s_shapeHash);
	s_shapeHash = new HashOf<MidLevelRenderer::MLRShape*, ResourceID>(4099, NULL, true);
	Check_Object(s_shapeHash);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ShapeComponent::TerminateClass()
{
	Check_Object(s_shapeHash);

	{
		HashIteratorOf<MidLevelRenderer::MLRShape*, ResourceID> iterator(s_shapeHash);
		MidLevelRenderer::MLRShape* shape;
		while ((shape = iterator.ReadAndNext()) != NULL)
		{
			Check_Object(shape);
			Verify(shape->GetReferenceCount() == 1);
			shape->DetachReference();
		}
	}
	delete s_shapeHash;
	s_shapeHash = NULL;

	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ShapeComponent::ShapeComponent(
	ClassData *class_data,
	MemoryStream *stream,
	VideoComponentWeb *owning_web
):
	VideoComponent(
		class_data,
		stream,
		owning_web,
		ReadShape(stream, &m_unique)
	)
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Element*
	ShapeComponent::ReadShape(MemoryStream *stream, bool *unique)
{
	Check_Object(stream);

	//
	//---------------------------------------------------------
	// Read the unique status, and set up the resource variable
	//---------------------------------------------------------
	//
	*stream >> *unique >> s_Erf_Resource;
	Verify(s_Erf_Resource.m_fileID < 256);

	//
	//----------------------------------------------------------------------------------
	// Open the resource, and if we are not unique, give the ShapeHolder function to the
	// ElementRenderer
	//----------------------------------------------------------------------------------
	//
	ElementRenderer::ShapeElement::s_BoundsWereBad = false;
	Resource shape_stream(s_Erf_Resource);
	Element *shape =
		Element::Create(
			&shape_stream,
			ElementRenderer::ReadERFVersion(&shape_stream)
//			,
//			(m_unique) ? NULL : Cache_Shape
		);

	//
	//---------------------------------------------------------------------------
	// If we are looking for shape errors, print out the erf that has the problem
	//---------------------------------------------------------------------------
	//
	#if defined(LAB_ONLY)
		if (ElementRenderer::ShapeElement::s_CheckBounds && ElementRenderer::ShapeElement::s_BoundsWereBad)
			SPEWALWAYS(("", "%s has bad bounds", shape_stream.GetName()));
	#endif

	//
	//-------------------------------------------------------------
	// Register the new element, unlock the resource, and return it
	//-------------------------------------------------------------
	//
	return shape;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ShapeComponent::SkipStreamData(MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);

	ResourceID mesh_id;
	*stream >> mesh_id;

	BaseClass::SkipStreamData(stream);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ShapeComponent*
	ShapeComponent::Create(
		MemoryStream *stream,
		VideoComponentWeb *owning_web
	)
{
	Check_Object(stream);
	Check_Object(owning_web);
	Component *component = DoesComponentExist(stream, owning_web);
	ShapeComponent *object;
	if (component)
	{
		object = static_cast<ShapeComponent*>(component);
		Check_Object(object);
		object->SkipStreamData(stream);
	}
	else
	{
		object = new ShapeComponent(DefaultData, stream, owning_web);
		Check_Object(object);
	}
	return object;
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ShapeComponent::CleanDamage()
{
	Check_Object(this);

	if (m_unique)
		GetElement()->CleanDamage();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ShapeComponent::ApplyDamage(
		const Stuff::LinearMatrix4D &damage_spot,
		Stuff::Scalar radius
	)
{
	Check_Object(this);
	Check_Object(&damage_spot);

	if (m_unique)
		GetElement()->ApplyDamage(damage_spot, radius);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ShapeComponent::ApplyDamageDecal(
		const Stuff::LinearMatrix4D &damage_spot,
		Stuff::Scalar radius,
		MidLevelRenderer::MLRTexture *texture
	)
{
	Check_Object(this);
	Check_Object(&damage_spot);

	if (m_unique)
		GetElement()->ApplyDamageDecal(damage_spot, radius, texture);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MidLevelRenderer::MLRShape* ShapeComponent::Cache_Shape(
	Stuff::MemoryStream *stream,
	int mlr_version,
	int length
)
{
	//
	//-----------------------------------------------------------------
	// Look for the current ID in the hash, and add it if its not there
	//-----------------------------------------------------------------
	//
	MidLevelRenderer::MLRShape *shape = s_shapeHash->Find(s_Erf_Resource);
	if (!shape)
	{
		RegisteredClass::ClassID class_id;
		*stream >> class_id;

		switch(class_id)
		{
		case MidLevelRenderer::MLRShapeClassID:
			shape = MidLevelRenderer::MLRShape::Make(stream, mlr_version);
			break;
		case MidLevelRenderer::MLRCulturShapeClassID:
			shape = MidLevelRenderer::MLRCulturShape::Make(stream, mlr_version);
			break;
		default:
			STOP(("Don't know what this is"));
		}
		s_shapeHash->AddValue(shape, s_Erf_Resource);
	}

	//
	//-------------------------------------------------
	// Otherwise, we have to skip forward in the stream
	//-------------------------------------------------
	//
	else
		stream->AdvancePointer(length);

	//
	//--------------------------------------------------
	// Attach to the shape and bump the resource pointer
	//--------------------------------------------------
	//
	shape->AttachReference();
	Verify(s_Erf_Resource.m_fileID < 0xFF00);
	s_Erf_Resource.m_fileID += 0x100;
	return shape;
}
