/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Special-effects container.
 *//*
 * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define FX_FX_CC 1
#include <list>
#include "base/module.hh"
#include "base/stream.hh"
using namespace base;
#include "object/module.hh"
using namespace object;
#include "graph/module.hh"
#include "graph/scene_graph.hh"
using namespace graph;
#include "fx/module.hh"
#include "fx/defs.hh"
#include "fx/fx.hh"
#include "fx/partsys_explosion.hh"
#include "fx/partsys_trail.hh"

namespace fx {

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////  FX : core  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

FX* FX::msInstance;

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
FX::FX( void )
:   mTickFunctor(new FX::TickFunctor(*this)),
    mObjects(),
    mZombieObjects(),
    mParticleSystems(),
    mZombieParticleSystems()
{
    GET_TIMER().Register( mTickFunctor, fx::defs::FX_TICK_FREQ );
}

FX::~FX()
{
    // Must unregister!  Else functor would access a destroyed object.
    GET_TIMER().Unregister( mTickFunctor );
}

/*****************************************************************************
 * Stop all special-effects.
 *****************************************************************************/
void
FX::Reset( void )
{
    // Detach Objects.
    Detach( mObjects );
    Detach( mZombieObjects );

    // Detach ParticleSystems.
    Detach( mParticleSystems );
    Detach( mZombieParticleSystems );
}

/*****************************************************************************
 * Periodically destroy zombie special-effects.
 *****************************************************************************/
void
FX::Tick( const Milliseconds millisecElapsed )
{
    Detach( mZombieObjects );
    Detach( mZombieParticleSystems );
}

// Reflect.
void
FX::TickFunctor::Tick( const Milliseconds millisecElapsed )
{
    mFX.Tick( millisecElapsed );
}

/*****************************************************************************
 * Detach special-effects (Objects).
 *****************************************************************************/
void
FX::Detach( Objects& objects )
{
    // ~Object() will detach node.
    objects.clear();
}

/*****************************************************************************
 * Detach special-effects (ParticleSystems).
 *****************************************************************************/
void
FX::Detach( ParticleSystems& particleSystems )
{
#if DEBUG_PARTSYS
    CDEBUG << "FX::DetachParticleSystems() count=" << particleSystems.size() << std::endl;
#endif

    // Erase container.  ParticleSystems aren't 3D Objects and outside scene graph.
    particleSystems.clear();
}

/*****************************************************************************
 * Turn Object into a zombie to be deleted later.
 *****************************************************************************/
void
FX::AddZombie( shptr<Object> zombie )
{
CHECK_TYPESIG(zombie,TYPESIG_OBJECT);

    // Add to zombie list.  Re-adding would be a benign error because of shptr.
    mZombieObjects.push_back( zombie );

    // Remove from active list.
    for ( Objects::iterator iter = mObjects.begin();
          iter != mObjects.end();
          ++iter )
    {
        CHECK_TYPESIG( *iter, TYPESIG_OBJECT );  // *iter = shptr<Object>
        if ( (*iter).PTR() == zombie.PTR() )  // compare address
        {
            // ~Object() will detach node.
            mObjects.erase( iter );  // ok since going to return
            return;
        }
    }
    ASSERT( false );  // oops, it should be in active list
}

/*****************************************************************************
 * Turn ParticleSystem into a zombie to be deleted later.
 *****************************************************************************/
void
FX::AddZombie( shptr<ParticleSystem> zombie )
{
CHECK_TYPESIG(zombie,TYPESIG_PARTICLE_SYSTEM);

    // Add to zombie list.  Re-adding would be a benign error because of shptr.
    mZombieParticleSystems.push_back( zombie );

    // Remove from active list.
    for ( ParticleSystems::iterator iter = mParticleSystems.begin();
          iter != mParticleSystems.end();
          ++iter )
    {
        CHECK_TYPESIG(*iter,TYPESIG_PARTICLE_SYSTEM);  // *iter = shptr<ParticleSystem>
        if ( (*iter).PTR() == zombie.PTR() )  // compare address
        {
            mParticleSystems.erase( iter );  // ok since going to return
            return;
        }
    }
    ASSERT( false );  // oops, it should be in active list
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////  FX : make special-effects  ///////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Create an explosion.
 *****************************************************************************/
void
FX::MakeExplosion( const Milliseconds lifetime,
                   const WorldVertex& position,
                   const fp radius )
{
    // Create a ParticleSystem.  Add to FX container.
    mParticleSystems.push_back( new ExplosionParticleSystem( lifetime, position, radius ) );
}

/*****************************************************************************
 * Create smoke trails.
 *****************************************************************************/
void
FX::MakeSmokeTrail( const Milliseconds lifetime,
                    shptr<Object> trailMaker )
{
    // Create a ParticleSystem.  Add to FX container.
    mParticleSystems.push_back( new TrailParticleSystem( lifetime, trailMaker ) );
}

} // namespace fx
