/*****************************************************************************
 * $LastChangedDate: 2011-02-04 19:23:35 -0500 (Fri, 04 Feb 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Trail particle-system (private implementation of FX class).
 *//*
 * 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_PARTSYS_TRAIL_CC 1
#include "base/module.hh"
#include "base/random.hh"
using namespace base;
#include "object/module.hh"
using namespace object;
#include "world/module.hh"
using namespace world;
#include "fx/module.hh"
#include "fx/defs.hh"
#include "fx/texture.hh"
#include "fx/partsys_trail.hh"

namespace fx {

INTERN const uint TRAIL_PARTICLE_LIMIT       = 5000;
INTERN const uint TRAIL_PARTICLES_PER_SECOND = 80;
INTERN const RGBA TRAIL_COLOR( 0xb0, 0xb0, 0xb0, 0xa0 );  // gray

////////////////////////////////////////////////////////////////////////////////
////////////////////////////  TrailParticleSystem  /////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 * @param   lifetime
 *          Lifetime of entire particle-system.
 * @param   trailMaker
 *          Emitter of particles (missile, damaged craft, etc).
 *****************************************************************************/
TrailParticleSystem::TrailParticleSystem( const Milliseconds lifetime,
                                          shptr<Object> trailMaker )
:   MediumParticleSystem(MediumParticleSystem::Args(lifetime,
                                                    TimerDefs::TIMER_ENABLED,
                                                    NodeSort(fx::defs::PARTSYS_NODE_SORT_ATTRIBS,
                                                             Texture::NewTextureAsStateSet("textures/smoke.png")))),
    mTrailMaker(trailMaker),
    mTrailMakerPrevPos(trailMaker->GetPosition()),
    mTimeOfDeath(GET_TIMER().GetElapsedTime() + lifetime),
    mTimeOfPrevTick(GET_TIMER().GetElapsedTime()),
    mDestroyObjectListener(new DestroyObjectListener(*this))
{
    EVENT_DESTROY_OBJECT.Listen( mDestroyObjectListener );

    SetColorAll( TRAIL_COLOR );
}

TrailParticleSystem::~TrailParticleSystem()
{
    EVENT_DESTROY_OBJECT.Unlisten( mDestroyObjectListener );
}

/*****************************************************************************
 * Every tick, decide:
 * - whether to expire entire particle system
 *   (base ParticleSystemFX decides this)
 * - whether to create a new Particle
 *****************************************************************************/
void
TrailParticleSystem::Tick( const Milliseconds millisecElapsed )
{
CHECK_TYPESIG(this,TYPESIG_MEDIUM_PARTICLE_SYSTEM);
CHECK_TYPESIG(mTrailMaker,TYPESIG_OBJECT);

    // Call parent's method.  It may expire this.
    Parent::Tick( millisecElapsed );

    // Stop emitting new particles if:
    if ( (GetParticleCount() < TRAIL_PARTICLE_LIMIT)  // too many particles?
     and (not IfExpired())                            // partsys has expired?
     and (not mTrailMaker->IfDisabled()) )            // Object making trails will be destroyed?
    {
        // From time between now and previous tick,
        // compute how many Particles to create.
        const Milliseconds timeDelta = millisecElapsed - mTimeOfPrevTick;
        ASSERT( timeDelta >= Milliseconds(0) );
        const uint particleCount = Max( uint(1), uint(timeDelta.FPX() / 1000.0 * TRAIL_PARTICLES_PER_SECOND) );
        MakeParticles( particleCount, millisecElapsed );
    }

    // Update time-stamp.
    mTimeOfPrevTick = millisecElapsed;
    mTrailMakerPrevPos = mTrailMaker->GetPosition();
}

/*****************************************************************************
 * Make Particles.
 *****************************************************************************/
void
TrailParticleSystem::MakeParticles( const uint count, const Milliseconds millisecElapsed )
{
CHECK_TYPESIG(this,TYPESIG_PARTICLE_SYSTEM);
CHECK_TYPESIG(mTrailMaker,TYPESIG_OBJECT);
ASSERT( count < fx::defs::MAX_PARTICLES_ONE_SYSTEM );

    const fp r = world::conv::Meters2Sim( Meter(0.65f) );  // randomize position slightly

    // Create/attach new particles.
    // Interpolate particles across current step of moving trail maker.
    WorldVertex pos = mTrailMakerPrevPos;
    WorldVertex posInc = (mTrailMaker->GetPosition() - mTrailMakerPrevPos) / fp(count);
    MediumParticle particle;
    for ( uint i = 0; i < count; ++i )
    {
        particle.mPosition = pos;
        pos += posInc;  // linear interpolation

        pos[XX] += Random::random_f( r );
        pos[YY] += Random::random_f( r );
        pos[ZZ] += Random::random_f( r );

        AttachParticle( particle );
    }
}

/*****************************************************************************
 * Listen for the DestroyObject event.
 *****************************************************************************/
void
TrailParticleSystem::DestroyObjectListener::operator()( shptr<Object> object )
{
CHECK_TYPESIG(&mTrailParticleSystem,TYPESIG_PARTICLE_SYSTEM);
CHECK_TYPESIG(object,TYPESIG_OBJECT);

    // Does this have a reference to that Object?
    if ( object.PTR() == mTrailParticleSystem.mTrailMaker.PTR() )
    {
        // Release reference to trail-maker Object.
        mTrailParticleSystem.mTrailMaker = Object::MakeDummyObject();

        // Partsys should survive (for a while) after trail-maker expires.
    }
}

} // namespace fx
