#include "ElementRendererHeaders.hpp"
#include <gosFX\PointLight.hpp>
#include <MLR\MLRInfiniteLightWithFalloff.hpp>

//###########################################################################
//#############################  gosFX::Light  ##############################
//###########################################################################

DWORD Disable_Lights=0;

static bool
	__stdcall Check_DisableLights()
{
	return Disable_Lights!=0;
}

static void
	__stdcall Activate_DisableLights()
{
	Disable_Lights = !Disable_Lights;
}

//------------------------------------------------------------------------------
//
gosFX::Light::Light(MidLevelRenderer::MLRLight *light):
	Plug(DefaultData),
	m_light(light)
{
	Check_Pointer(this);
	Check_Object(light);
}

//------------------------------------------------------------------------------
//
gosFX::Light::~Light()
{
	Check_Object(this);
	Check_Object(m_light);
	delete m_light;
}

//------------------------------------------------------------------------------
//
void
	gosFX::Light::ChangeLight(gosFX::LightManager::Info *info)
{
	Check_Object(this);
	Check_Pointer(info);

	m_light->SetIntensity(info->m_intensity);
	m_light->SetColor(info->m_color);
	m_light->SetLightToWorldMatrix(info->m_origin);
}

//------------------------------------------------------------------------------
//
void
	gosFX::Light::GetInfo(gosFX::LightManager::Info *info)
{
	Check_Object(this);
	Check_Pointer(info);

	Check_Object(m_light);
	m_light->GetColor(info->m_color);
	info->m_origin = m_light->GetLightToWorldMatrix();
	info->m_intensity = m_light->GetIntensity();
}

//############################################################################
//########################  LightElementManager  #############################
//############################################################################

ElementRenderer::LightElementManager*
	&ElementRenderer::LightElementManager::Instance=
		*Cast_Pointer(
			ElementRenderer::LightElementManager**,
			&gosFX::LightManager::Instance
		);

//------------------------------------------------------------------------------
//
ElementRenderer::LightElementManager::LightElementManager()
{
}

//------------------------------------------------------------------------------
//
ElementRenderer::LightElementManager::~LightElementManager()
{
}

//------------------------------------------------------------------------------
//
void
	ElementRenderer::LightElementManager::ChangeLight(
		gosFX::Light* light,
		Info *info
	)
{
	Check_Object(this);
	Check_Object(light);

	light->ChangeLight(info);
}

//------------------------------------------------------------------------------
//
void
	ElementRenderer::LightElementManager::DeleteLight(gosFX::Light *light)
{
	Check_Object(this);
	Check_Object(light);

	delete light;
}

//############################################################################
//############################  LightElement  ################################
//############################################################################

HGOSHEAP
	ElementRenderer::LightElement::s_Heap = NULL;

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LightElement::InitializeClass()
{
	Verify(!s_Heap);
	s_Heap = gos_CreateMemoryHeap("Light", 0, g_LibraryHeap);
	Check_Pointer(s_Heap);

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			LightElementClassID,
			"ElementRenderer::LightElement",
			BaseClass::DefaultData,
			reinterpret_cast<Factory>(&Make)
		);
	Check_Object(DefaultData);

	AddDebuggerMenuItem("Libraries\\ElementRenderer\\Disable Lighting", Check_DisableLights, Activate_DisableLights, 0 );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LightElement::TerminateClass()
{
	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	Check_Pointer(s_Heap);
	gos_DestroyMemoryHeap(s_Heap);
	s_Heap = NULL;
}

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

	m_draw = static_cast<DrawMethod>(&ElementRenderer::LightElement::Draw);

	//
	//---------------------------------
	// Create the shape from the stream
	//---------------------------------
	//
	gos_PushCurrentHeap(MidLevelRenderer::LightsHeap);
	MidLevelRenderer::MLRLight *light =
		MidLevelRenderer::MLRLight::Make(
			stream,
			MidLevelRenderer::ReadMLRVersion(stream)
		);
	gos_PopCurrentHeap();
	Check_Object(light);
	m_light = ElementRenderer::LightElementManager::Instance->MakeLight(light);
	Check_Object(m_light);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::LightElement::LightElement():
	Element(DefaultData)
{
	Check_Pointer(this);

	m_draw = static_cast<DrawMethod>(&ElementRenderer::LightElement::Draw);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ElementRenderer::LightElement::~LightElement()
{
	Check_Object(this);
	Check_Object(m_light);
	delete m_light;
}

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

	gos_PushCurrentHeap(s_Heap);
	LightElement *element = new LightElement(stream, version);
	gos_PopCurrentHeap();

	return element;
}

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

	//
	//-----------------------------
	// Save the shape to the stream
	//-----------------------------
	//
	MidLevelRenderer::WriteMLRVersion(stream);
	Check_Object(m_light);
	MidLevelRenderer::MLRLight *light = m_light->m_light;
	Check_Object(light);
	light->Save(stream);
}

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LightElement::SetSyncState()
{
	Check_Object(this);

	m_sync = static_cast<SyncMethod>(&ElementRenderer::LightElement::LightSync);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LightElement::LightSync()
{
	Check_Object(this);

	//
	//--------------------
	// Update our matrices
	//--------------------
	//
	m_localToParent = m_newLocalToParent;
	Check_Object(m_parent);
	if (!IsLocalToParentIdentity())
		m_localToWorld.Multiply(m_localToParent, m_parent->GetLocalToWorld());
	else
		m_localToWorld = m_parent->GetLocalToWorld();
	MatrixIsClean();

	//
	//-----------------
	// Update the light
	//-----------------
	//
	m_lightInfo.m_origin = m_localToWorld;
	Check_Object(m_light);
	m_light->ChangeLight(&m_lightInfo);
}

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

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

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LightElement::SetDrawState()
{
	Check_Object(this);
	m_draw = static_cast<DrawMethod>(&ElementRenderer::LightElement::Draw);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LightElement::Draw(
		CameraElement *camera,
		const StateChange *inherited_state,
		int culling_state
	)
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	ElementRenderer::LightElement::CountTriangles()
{
	Check_Object(this);
	return 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	ElementRenderer::LightElement::AdoptLight(gosFX::Light* light)
{
	Check_Object(this);
	Check_Object(light);
	m_light = light;
	light->GetInfo(&m_lightInfo);
}
