/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Periodically destroys zombie Objects.
 *//*
 * 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 OBJECT_ZOMBIES_CC 1
#include "base/module.hh"
#include "base/stream.hh"
using namespace base;
#include "control/module.hh"
using namespace control;
#include "object/module.hh"
#include "object/object.hh"
#include "object/zombies.hh"
#include "object/events.hh"

namespace object {

INTERN const Milliseconds ZOMBIE_OBJECTS_TICK_FREQ( 1000 );

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  Zombies  /////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

DEFINE_SINGLETON_CLASS_VARS( ZombieObjects )

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
ZombieObjects::ZombieObjects( void )
:   mZombies(),
    mTickFunctor(new TickFunctor)
{
    GET_TIMER().Register( mTickFunctor, ZOMBIE_OBJECTS_TICK_FREQ );
}

ZombieObjects::~ZombieObjects()
{
    // NOP.
    // Don't bother unregistering tick functor from Singleton.
}

/*****************************************************************************
 * Enqueue zombie Object.
 *****************************************************************************/
void
ZombieObjects::Zombify( shptr<Object> zombie )
{
CHECK_TYPESIG(zombie,TYPESIG_OBJECT);

    // The current craft is never zombified.
    // The reasons are that it should be shown while exploding/burning,
    // and it is the target of the chase-view.
    if ( (not zombie->IfDummy())                        // keep dummy Object
     and (zombie.PTR() != GET_CURRENT_CRAFT().PTR()) )  // keep the current craft
    {
        mZombies.push_back( zombie );
    }
}

/*****************************************************************************
 * Pulse.
 *****************************************************************************/
void
ZombieObjects::Tick( void )
{
    // CRITICAL: All modules holding a reference to an Object
    // should listen for this event and release their reference,
    // to avoid consuming memory.
    for ( uint i = 0; i < mZombies.size(); ++i )
    {
    CHECK_TYPESIG( mZombies[i], TYPESIG_OBJECT );

        // Broadcast zombie.
        EVENT_DESTROY_OBJECT.Broadcast( mZombies[i] );
    }

#if DEBUG
    // Catch failure to respond to DestroyObject event.
    for ( uint i = 0; i < mZombies.size(); ++i )
    {
    CHECK_TYPESIG( mZombies[i], TYPESIG_OBJECT );

        const uint refCount = mZombies[i].GetRefCount();
        if ( refCount > 1 )
        {
            CDEBUG << "WARN: Zombie Object '" << mZombies[i]->GetName() << "' refCount = " << refCount << " (should be 1)." << std::endl
                   << "Some places in the code aren't responding to ObjectDestroy event!" << std::endl;
        }
    }
#endif

    // Clear container of Objects which should delete them finally.
    mZombies.clear();

#if 0
#if DEBUG
    CDEBUG << "objects = " << Object::GetObjectCount() << std::endl;
#endif
#endif
}

void
ZombieObjects::TickFunctor::Tick( const Milliseconds millisecElapsed )
{
    GET_ZOMBIE_OBJECTS().Tick();
}

} // namespace object
