//------------------------------------------------------------------------------
// emTimer.h
//
// Copyright (C) 2006-2008,2010,2016 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emTimer_h
#define emTimer_h

#ifndef emEngine_h
#include <emCore/emEngine.h>
#endif


//==============================================================================
//================================== emTimer ===================================
//==============================================================================

class emTimer : public emUncopyable {

public:

        // Class for a timer which signals an emSignal on each timer event.

        emTimer(emScheduler & scheduler);
                // Construct a timer. The given scheduler is used for signaling
                // the timer signal (and for an internal engine which serves all
                // the timers on that scheduler).

        virtual ~emTimer();
                // Destructor.

        emScheduler & GetScheduler() const;
                // Get the scheduler.

        const emSignal & GetSignal() const;
                // This signal is signaled on each timer event.

        void Start(emUInt64 millisecs, bool periodic=false);
                // Start the timer.
                // Arguments:
                //   millisecs - Time in milliseconds after which a timer event
                //               shall be generated.
                //   periodic  - If true, additional timer events are generated
                //               periodically. The given number of milliseconds
                //               is the period duration. The timer tries to keep
                //               up that rate on average, even if timer events
                //               are delayed through blockings of the scheduler,
                //               but it is limited to the rate of scheduler time
                //               slices by all means.

        bool IsRunning() const;
                // Ask whether the timer is still active.

        void Stop(bool abortSignal);
                // Stop the timer.
                // Arguments:
                //   abortSignal - Whether to perform an Abort() on the signal,
                //                 but remember that engines could already have
                //                 been woken up. Say false for an asynchronous
                //                 stop, or true for a synchronous stop.

private:

        struct TimeNode {
                emUInt64 SigTime;
                TimeNode * Prev;
                TimeNode * Next;
        };

        class TimerCentral : public emEngine {
        public:
                TimerCentral(emScheduler & scheduler);
                void Insert(TimeNode * node, emUInt64 sigTime);
                int RefCount;
        protected:
                virtual bool Cycle();
        private:
                TimeNode InList;
                TimeNode OutList;
                bool Busy;
        };

        friend class TimerCentral;

        TimerCentral * Central;
        emSignal TimerSignal;
        emUInt64 Period;
        TimeNode Node;
};

inline emScheduler & emTimer::GetScheduler() const
{
        return Central->GetScheduler();
}

inline const emSignal & emTimer::GetSignal() const
{
        return TimerSignal;
}

inline bool emTimer::IsRunning() const
{
        return Node.Prev!=NULL;
}


#endif