/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Timer calls registered functors, pauses the program, and provides time functions.
 *//*
 * LEGAL:   COPYRIGHT (C) 2006 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef BASE_TIMER_HH
#define BASE_TIMER_HH 1

#define GET_TIMER() (base::Timer::GetInstance())

#include <vector>
#include <set>
#include "base/singleton.hh"
#include "base/time.hh"  // Milliseconds etc

namespace base {

//------------------------------------------------------------------------------

namespace TimerDefs
{
    const bool         TIMER_ENABLED       = true;
    const bool         TIMER_DISABLED      = false;
    const Milliseconds MILLISECONDS_NEVER( 1LL << 62LL) ;            ///< Timer::Register() will ignore
    const Milliseconds MILLISECONDS_MIN( 1 );                        ///< (debug)
    const Milliseconds MILLISECONDS_MAX( THOUSAND * 60LL * 60LL );   ///< (debug) 1 hour
}

//------------------------------------------------------------------------------

class Timer;

//------------------------------------------------------------------------------

// Macro to define nested class.
// Functor must not hold a ref-ptr to its owner
// (would create a cycle of reference-counted pointers).
#define CLASS_TICK_FUNCTOR( CLASS, MEMBER )                             \
    class TickFunctor : public base::TickFunctor                        \
    {                                                                   \
    public:                                                             \
        TickFunctor( CLASS& obj ) : MEMBER(obj) { }                     \
        virtual void Tick( const Milliseconds millisecElapsed );        \
    private:                                                            \
        CLASS& MEMBER;                                                  \
    };                                                                  \
    friend class TickFunctor;

// Macro.
#define CLASS_TICK_FUNCTOR_BASIC                                        \
    class TickFunctor : public base::TickFunctor                        \
    {                                                                   \
    public:                                                             \
        virtual void Tick( const Milliseconds millisecElapsed );        \
    };                                                                  \
    friend class TickFunctor;

//------------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief User-defined timer-tick functor.
///
/// Implementation:
/// TickFunctors are modified with the last time they were called.
///
/// Historical note:
/// Timer stores Functors by pointer (derivatives probably won't define a copy ctor).
/// Anyway, the Functor class has grown too much for copying.
///
class TickFunctor : public Shared
{
friend class Timer;
PREVENT_COPYING(TickFunctor)
public:
    enum eDisablement { CAN_DISABLE, CANNOT_DISABLE };  ///< whether functor cannot be disabled even if Timer disabled

public:
                    TickFunctor( void );
    virtual         ~TickFunctor();
    virtual void    Tick( const Milliseconds millisecElapsed ) = 0;

private:
    void            CallTickIfNowIsTheTime( const Milliseconds millisecElapsed );

private:
    // Timer maintains these hidden members.
    bool            mRegistered;        ///< false if Unregister() applied
    Milliseconds    mMillisecFreq;      ///< how often to invoke functor
    Milliseconds    mMillisecCalled;    ///< last time it was called
    eDisablement    mDisablement;       ///< whether functor must be called even if Timer is disabled
public:
    DECLARE_TYPESIG( TYPESIG_TICK_FUNCTOR )
};

//------------------------------------------------------------------------------

///////////////////////////////////////////////////////////////////////////////
/// @brief Timer calls registered functors, pauses the program, and provides time functions.
///
/// Timer is designed to tolerate tick functors that unregister themselves
/// or indirectly register new functors, even while Timer is busy iterating thru functors.
///
/// Disabling Timer isn't absolute.
/// A functor that is constructed with Timer::CANNOT_DISABLE
/// must still be called even if Timer is disabled.
/// For example, polling keyboard should remain enabled always.
///
/// To integrate Timer:
/// Whichever function that is called periodically must call Timer::Tick()
/// to drive the Timer object.  Otherwise Timer won't be active.
///
/// Timer must be Singleton since globals register timers.
///
/// Pausing program by disabling Timer:
/// -----------------------------------
/// Time is stopped while the program is paused, except for
/// functors that cannot be disabled (still called while paused).
///
/// GetElapsedTime() returns the elapsed time minus time lost while paused.
/// GetElapsedTimeTotal() still counts time while Timer is disabled (paused).
/// Functors that cannot be disabled should call GetElapsedTimeTotal()
/// else their timing calculations will malfunction after pausing.
///
class Timer : public Singleton
{
PREVENT_COPYING(Timer)
private:
    Timer( void );  // singleton
    ~Timer();

public:
// Client interface:
                        DEFINE_GetInstance( Timer )  // Singleton
    void                Enable( bool enable );

    // Time since start of program (minus time lost while program was paused).
    Milliseconds        GetElapsedTime( void ) const { return mTimeVirtual; }

    // Total time since start of program.
    Milliseconds        GetElapsedTimeTotal( void ) const { return mTimeTotal; }

    void                Register( shptr<TickFunctor> functor,
                                  const Milliseconds millisecFreq,
                                  const TickFunctor::eDisablement canDisable = TickFunctor::CAN_DISABLE );
    void                Unregister( shptr<TickFunctor> functor );

// For integration:
    void                Tick( void );  // to pulse Timer

private:
    void                TickUpdateTime( const Milliseconds elapsedTime );
    void                TickDoPending( void );
    void                TickCallFunctors( void );

private:
    DECLARE_SINGLETON_CLASS_VARS( Timer )
    typedef std::set< shptr<TickFunctor> > Functors;
    typedef std::vector< shptr<TickFunctor> > PendingFunctors;
    bool                mEnabled;                       ///< program will be (mostly) paused if Timer is disabled
    Functors            mFunctors;                      ///< mapped by raw ptr to Functor
    PendingFunctors     mPendingRegister;               ///< pending registering
    PendingFunctors     mPendingUnregister;             ///< pending unregistering
    Milliseconds        mTimeTotal;                     ///< total time since start of program
    Milliseconds        mTimeVirtual;                   ///< time since start of program minus time lost while paused
    Milliseconds        mTimeTotalWhenPauseStarted;     ///< copy of mTimeTotal when pause was started
    Milliseconds        mTimeVirtualWhenPauseStarted;   ///< copy of mTimeVirtual when pause was started
    Milliseconds        mTimeLostWhilePaused;           ///< accumulated time lost while paused
};

} // namespace base

#endif // BASE_TIMER_HH
