#include "ElementRendererHeaders.hpp"
#include "LineCloudElement.hpp"
#include <MLR\MLRLineCloud.hpp>

//############################################################################
//##########################  LineCloudElement  ##############################
//############################################################################
//#define BOUNDS_BUG

ElementRenderer::LineCloudElement::ClassData*
	ElementRenderer::LineCloudElement::DefaultData = NULL;

ElementRenderer::Element::DrawMethod
	ElementRenderer::LineCloudElement::DrawMethods[DrawStateCount]=
{
	//
	// No billboarding
	//
	DRAW_METHOD(LineCloudElement,	Inherit),
	DRAW_METHOD(LineCloudElement,	Override)
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LineCloudElement::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			LineCloudElementClassID,
			"ElementRenderer::LineCloudElement",
			BaseClass::DefaultData,
			reinterpret_cast<Factory>(&Make)
		);
	Register_Object(DefaultData);
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::LineCloudElement::LineCloudElement(
	Stuff::MemoryStream *stream,
	int version
):
	Element(DefaultData, stream, version)
{
	Check_Pointer(this);
	Check_Object(stream);

	unsigned index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];

	//
	//---------------------------------
	// Create the shape from the stream
	//---------------------------------
	//
#if 0
	m_MLRLineCloud = MidLevelRenderer::MLRLineCloud::Make(stream);
	Register_Object(m_MLRLineCloud);
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::LineCloudElement::LineCloudElement(unsigned max_points):
	Element(DefaultData)
{
	Check_Pointer(this);

	unsigned index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];

	gos_PushCurrentHeap(MidLevelRenderer::EffectHeap);
	m_MLRLineCloud = new MidLevelRenderer::MLRLineCloud(max_points);
	Register_Object(m_MLRLineCloud);
	gos_PopCurrentHeap();

	m_MLRLineCloud->TurnAllOn();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::LineCloudElement::~LineCloudElement()
{
	Check_Object(this);
	Unregister_Object(m_MLRLineCloud);
	delete m_MLRLineCloud;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::LineCloudElement*
	ElementRenderer::LineCloudElement::Make(
		Stuff::MemoryStream *stream,
		int version,
		ShapeHolder shapes
	)
{
	Check_Object(stream);

	gos_PushCurrentHeap(g_Heap);
	LineCloudElement *element = new LineCloudElement(stream, version);
	gos_PopCurrentHeap();

	return element;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LineCloudElement::Save(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	BaseClass::Save(stream);

	//
	//-----------------------------
	// Save the shape to the stream
	//-----------------------------
	//
#if 0
	Check_Object(m_MLRLineCloud);
	m_MLRLineCloud->Save(stream);
#endif
}

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LineCloudElement::AttachChild(Element *child)
{
	Check_Object(this);
	Check_Object(child);
	STOP(("LineCloud elements can't have children!"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LineCloudElement::DetachChild(Element *child)
{
	Check_Object(this);
	Check_Object(child);
	STOP(("LineCloud elements can't have children!"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementRenderer::LineCloudElement::CastCulledRay(CollisionQuery *query)
{
	Check_Object(this);
	Check_Object(query);

	STOP(("LineCloud is not collidable!"));
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::Element*
	ElementRenderer::LineCloudElement::FindSmallestElementContainingCulled(SphereTest *test)
{
	Check_Object(this);
	Check_Object(test);

	STOP(("LineCloud is not collidable!"));
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LineCloudElement::SetDrawState()
{
	Check_Object(this);
	unsigned index = (m_state>>DrawStateBit) & DrawStateMask;
	Verify(index < DrawStateCount);
	m_draw = DrawMethods[index];
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LineCloudElement::InheritDrawMethod(
		CameraElement *camera,
		const StateChange *inherited_state,
		int culling_state
	)
{
	Check_Object(this);
	Check_Object(inherited_state);
	Check_Object(camera);
	Verify(culling_state != -1);

	//
	//----------------------------------------------------------------------
	// If we are running slow enough, go ahead and check our vertices to see
	// that they are in the bounds
	//----------------------------------------------------------------------
	//
	#if defined(_ARMOR) && defined(BOUNDS_BUG)
		if (!culling_state && ArmorLevel > 3)
		{
			Point3D center(m_localOBB.localToParent);
			Scalar radius_squared = m_localOBB.sphereRadius*m_localOBB.sphereRadius;
				const Point3D *verts;
				int count;
				m_MLRLineCloud->GetCoordData(&verts, &count);
				for (int v=0; v<count; ++v)
				{
					Vector3D diff;
					diff.Subtract(verts[v], center);
					Verify(diff.GetLengthSquared() <= radius_squared);
				}
		}
	#endif

	//
	//--------------------------------------
	// Draw our bounds if we are supposed to
	//--------------------------------------
	//
	#if defined(LAB_ONLY)
		Callback *callback = GetCallbackSet();
		unsigned index = GetCallbackIndex();
		if (callback && callback[index])
		{
			Check_Pointer(callback[index]);
			(*callback[index])(camera, inherited_state, culling_state, this);
		}
	#endif

	//
	//----------------------------
	// Set up the draw information
	//----------------------------
	//
	MidLevelRenderer::DrawEffectInformation info;
	Check_Object(m_MLRLineCloud);
	info.effect = m_MLRLineCloud;
	info.clippingFlags.SetClippingState(0x2f);
	Check_Object(&m_localToWorld);
	info.effectToWorld = &m_localToWorld;
	info.state = inherited_state->GetMLRState();

	//
	//---------------
	// Draw the shape
	//---------------
	//
	MidLevelRenderer::MLRClipper *clipper = camera->GetClipper();
	Check_Object(clipper);

	Stop_Timer(Graph_Traversal);
	ELEMENT_RENDER("Culling::LineCloud::Inherited");
	Start_Timer(Draw_Effects);
	clipper->DrawEffect(&info);
	Stop_Timer(Draw_Effects);
	Start_Timer(Graph_Traversal);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LineCloudElement::OverrideDrawMethod(
		CameraElement *camera,
		const StateChange *inherited_state,
		int culling_state
	)
{
	Check_Object(this);
	Check_Object(inherited_state);
	Check_Object(camera);
	Verify(culling_state != -1);

	//
	//-------------------
	// Mix the properties
	//-------------------
	//
	Check_Object(m_stateChange);
	StateChange mixed;
	mixed.Mix(*inherited_state, *m_stateChange);

	//
	//----------------------------------------------------------------------
	// If we are running slow enough, go ahead and check our vertices to see
	// that they are in the bounds
	//----------------------------------------------------------------------
	//
	#if defined(_ARMOR) && defined(BOUNDS_BUG)
		if (!culling_state && ArmorLevel > 3)
		{
			Point3D center(m_localOBB.localToParent);
			Scalar radius_squared = m_localOBB.sphereRadius*m_localOBB.sphereRadius;
				const Point3D *verts;
				int count;
				m_MLRLineCloud->GetCoordData(&verts, &count);
				for (int v=0; v<count; ++v)
				{
					Vector3D diff;
					diff.Subtract(verts[v], center);
					Verify(diff.GetLengthSquared() <= radius_squared);
				}
		}
	#endif

	//
	//--------------------------------------
	// Draw our bounds if we are supposed to
	//--------------------------------------
	//
	#if defined(LAB_ONLY)
		Callback *callback = GetCallbackSet();
		unsigned index = GetCallbackIndex();
		if (callback && callback[index])
		{
			Check_Pointer(callback[index]);
			(*callback[index])(camera, inherited_state, culling_state, this);
		}
	#endif

	//
	//----------------------------
	// Set up the draw information
	//----------------------------
	//
	MidLevelRenderer::DrawEffectInformation info;
	Check_Object(m_MLRLineCloud);
	info.effect = m_MLRLineCloud;
	info.clippingFlags.SetClippingState(0x2f);
	Check_Object(&m_localToWorld);
	info.effectToWorld = &m_localToWorld;
	info.state = mixed.GetMLRState();

	//
	//---------------
	// Draw the shape
	//---------------
	//
	MidLevelRenderer::MLRClipper *clipper = camera->GetClipper();
	Check_Object(clipper);

	Stop_Timer(Graph_Traversal);
	ELEMENT_RENDER("Culling::LineCloud::Overridden");
	Start_Timer(Draw_Effects);
	clipper->DrawEffect(&info);
	Stop_Timer(Draw_Effects);
	Start_Timer(Graph_Traversal);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	ElementRenderer::LineCloudElement::CountTriangles()
{
	Check_Object(this);
	STOP(("Not implemented"));
	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LineCloudElement::SetDataPointers(
		const unsigned *used_points,
		const Stuff::Point3D *point_data,
		const Stuff::RGBAColor *color_data
	)
{
	Check_Pointer(this);
	Check_Pointer(used_points);
	Check_Pointer(point_data);
	Check_Pointer(color_data);

	Check_Object(m_MLRLineCloud);
	m_MLRLineCloud->SetData(
		reinterpret_cast<const int*>(used_points),
		point_data,
		color_data
	);
}
