#include "gosFXHeaders.hpp"

//==========================================================================//
// File:	 gosFX_EffectCloud.cpp											//
// Project:  gosFX					                                        //
// Contents: Base gosFX::EffectCloud Component										//
//--------------------------------------------------------------------------//
// Date      Who    Modification                                            //
// 09/28/98  JTR    Created													//
//																			//
//--------------------------------------------------------------------------//
// Copyright (C) 1997-1998, Virtual World Entertainment, Inc.               //
// All Rights reserved worldwide                                            //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL              //
//==========================================================================//

//------------------------------------------------------------------------------
//
gosFX::EffectCloud__Specification::EffectCloud__Specification(
	Stuff::MemoryStream *stream,
	int gfx_version
):
	SpinningCloud__Specification(gosFX::EffectCloudClassID, stream, gfx_version)
{
	Check_Pointer(this);
	Check_Object(stream);
	Verify(m_class == EffectCloudClassID);
	m_totalParticleSize = gosFX::EffectCloud::ParticleSize;
	m_particleClassSize = sizeof(gosFX::EffectCloud::Particle);

	if (gfx_version >= 21)
	{
		Stuff::MString name;
		*stream >> name;
		Check_Object(EffectLibrary::Instance);
		int id = EffectLibrary::Instance->Find(name);
		Verify(id >= 0);
		m_particleEffectID = id;
	}
	else
		*stream >> m_particleEffectID;
}

//------------------------------------------------------------------------------
//
gosFX::EffectCloud__Specification::EffectCloud__Specification():
	SpinningCloud__Specification(gosFX::EffectCloudClassID)
{
	Check_Pointer(this);
	m_totalParticleSize = gosFX::EffectCloud::ParticleSize;
	m_particleClassSize = sizeof(gosFX::EffectCloud::Particle);
}

//------------------------------------------------------------------------------
//
gosFX::EffectCloud__Specification::~EffectCloud__Specification()
{
	Check_Object(this);
}

//------------------------------------------------------------------------------
//
gosFX::EffectCloud__Specification*
	gosFX::EffectCloud__Specification::Make(
		Stuff::MemoryStream *stream,
		int gfx_version
	)
{
	Check_Object(stream);

	gos_PushCurrentHeap(Heap);
	EffectCloud__Specification *spec =
		new gosFX::EffectCloud__Specification(stream, gfx_version);
	gos_PopCurrentHeap();

	return spec;
}

//------------------------------------------------------------------------------
//
void
	gosFX::EffectCloud__Specification::Save(Stuff::MemoryStream *stream)
{
	Check_Object(this);
	Check_Object(stream);
	BaseClass::Save(stream);

	Check_Object(EffectLibrary::Instance);
	Effect::Specification *effect = EffectLibrary::Instance->Find(m_particleEffectID);
	Check_Object(effect);
	*stream << effect->m_name;
}

//------------------------------------------------------------------------------
//
void
	gosFX::EffectCloud__Specification::Copy(EffectCloud__Specification *spec)
{
	Check_Object(this);
	Check_Object(spec);

	BaseClass::Copy(spec);

	m_particleEffectID = spec->m_particleEffectID;
}

//############################################################################
//##############################  gosFX::EffectCloud  ################################
//############################################################################

gosFX::EffectCloud::ClassData*
	gosFX::EffectCloud::DefaultData = NULL;

//------------------------------------------------------------------------------
//
void
	gosFX::EffectCloud::InitializeClass()
{
	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			EffectCloudClassID,
			"gosFX::EffectCloud",
			BaseClass::DefaultData,
			(Effect::Factory)&Make,
			(Specification::Factory)&Specification::Make
		);
	Check_Object(DefaultData);
}

//------------------------------------------------------------------------------
//
void
	gosFX::EffectCloud::TerminateClass()
{
	Check_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

//------------------------------------------------------------------------------
//
gosFX::EffectCloud::EffectCloud(
	Specification *spec,
	unsigned flags
):
	SpinningCloud(DefaultData, spec, flags)
{
	Check_Object(spec);
}

//------------------------------------------------------------------------------
//
gosFX::EffectCloud::~EffectCloud()
{
	if (m_activeParticleCount)
	{
		for (int i=0; i < m_activeParticleCount; i++)
		{
			Particle *particle = GetParticle(i);
			Check_Object(particle);
			if (particle->m_effect)
			{
				Check_Object(particle->m_effect);
				delete particle->m_effect;
				particle->m_effect = NULL;
			}
		}
	}
}

//------------------------------------------------------------------------------
//
gosFX::EffectCloud*
	gosFX::EffectCloud::Make(
		Specification *spec,
		unsigned flags
	)
{
	Check_Object(spec);

	gos_PushCurrentHeap(Heap);
	EffectCloud *cloud = new gosFX::EffectCloud(spec, flags);
	gos_PopCurrentHeap();

	return cloud;
}

//------------------------------------------------------------------------------
//
void
	gosFX::EffectCloud::CreateNewParticle(
		unsigned index,
		Stuff::Point3D *translation
	)
{
	Check_Object(this);

	//
	//---------------------------
	// Let our parent do creation
	//---------------------------
	//
	BaseClass::CreateNewParticle(index, translation);

	//
	//----------------------------------------
	// Now create a new effect under ourselves
	//----------------------------------------
	//
	Specification *spec = GetSpecification();
	Check_Object(spec);
	Particle *particle = GetParticle(index);
	Check_Object(particle);
	particle->m_effect =
		EffectLibrary::Instance->MakeEffect(
			spec->m_particleEffectID,
			ExecuteFlag|DynamicWorldSpaceSimulationMode
		);
	Effect *effect = particle->m_effect;
	Check_Object(effect);
	particle->m_radius = 0.0f;

	//
	//-------------------------------------------------------------
	// Set the transform on the effect, then start the child effect
	//-------------------------------------------------------------
	//
	effect->m_localToParent.BuildTranslation(particle->m_localTranslation);
	effect->m_localToParent.BuildRotation(particle->m_localRotation);
	ExecuteInfo
		local_info(
			m_lastRan,
			&m_localToWorld,
			NULL,
			particle->m_seed
		);
	local_info.m_age = particle->m_age;
	local_info.m_ageRate = particle->m_ageRate;
	effect->Start(&local_info);
}

//------------------------------------------------------------------------------
//
bool
	gosFX::EffectCloud::AnimateParticle(
		unsigned index,
		const Stuff::LinearMatrix4D *world_to_new_local,
		Stuff::Time till
	)
{
	Check_Object(this);
	PARTICLE_LOGIC("Effect");

	//
	//--------------------------------------------------------------------
	// Make sure that we don't blow the age counters out of the base cloud
	// effects
	//--------------------------------------------------------------------
	//
	Particle *particle = GetParticle(index);
	Check_Object(particle);
	if (particle->m_age >= 1.0f)
		particle->m_age = 1.0f - Stuff::SMALL;
	BaseClass::AnimateParticle(index, world_to_new_local, till);

	//
	//---------------------------------
	// Update the location of the cloud
	//---------------------------------
	//
	Effect *effect = particle->m_effect;
	Check_Object(effect);
	effect->m_localToParent.BuildTranslation(particle->m_localTranslation);
	effect->m_localToParent.BuildRotation(particle->m_localRotation);

	//
	//-----------------------
	// Execute all the effect
	//-----------------------
	//
	Stuff::OBB bounds;
	ExecuteInfo
		info(
			till,
			&m_localToWorld,
			&bounds
		);
	if (effect->Execute(&info))
	{
		Stuff::Point3D center(bounds.localToParent);
		particle->m_radius = center.GetLength() + bounds.sphereRadius + Stuff::SMALL;
		return true;
	}

	particle->m_radius = 0.0f;
	delete particle->m_effect;
	particle->m_effect = NULL;
	return false;
}

//------------------------------------------------------------------------------
//
void gosFX::EffectCloud::DestroyParticle(unsigned index)
{
	Check_Object(this);

	Particle *particle = GetParticle(index);
	Check_Object(particle);
	if (particle->m_effect)
	{
		Check_Object(particle->m_effect);
		delete particle->m_effect;
		particle->m_effect = NULL;
	}
	BaseClass::DestroyParticle(index);
}

//------------------------------------------------------------------------------
//
void gosFX::EffectCloud::Stop()
{
	Check_Object(this);

	//
	//---------------------
	// Stop ourselves first
	//---------------------
	//
	BaseClass::Stop();

	//
	//---------------------------------------
	// If we have active particles, stop them
	//---------------------------------------
	//
	if (m_activeParticleCount)
	{
		for (int i=0; i < m_activeParticleCount; i++)
		{
			Particle *particle = GetParticle(i);
			Check_Object(particle);
			if (particle->m_age < 1.0f)
			{
				if (particle->m_effect)
				{
					Check_Object(particle->m_effect);
					particle->m_effect->Stop();
				}
			}
		}
	}
}

//------------------------------------------------------------------------------
//
void gosFX::EffectCloud::Draw(DrawInfo *info)
{
	Check_Object(this);
	Check_Object(info);
	GFX_RENDER("EffectCloud");

	//
	//---------------------------------------------------------
	// If we have active particles, set up the draw information
	//---------------------------------------------------------
	//
	if (m_activeParticleCount)
	{
		for (int i=0; i < m_activeParticleCount; i++)
		{
			Particle *particle = GetParticle(i);
			Check_Object(particle);

			//
			//-----------------------------------------------------------------
			// If the particle is still alive, concatenate into world space and
			// issue the draw command
			//-----------------------------------------------------------------
			//
			if (particle->m_age < 1.0f)
			{
				if (particle->m_effect)
				{
					Check_Object(particle->m_effect);
					particle->m_effect->Draw(info);
				}
			}
		}
	}

	BaseClass::Draw(info);
}

//------------------------------------------------------------------------------
//
void
	gosFX::EffectCloud::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}
