    //------------------------------------------------------------------------------
    // emEngine.h
    //
    // Copyright (C) 2005-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 emEngine_h
    #define emEngine_h

    #ifndef emSignal_h
    #include <emCore/emSignal.h>
    #endif

    //==============================================================================
    //================================== emEngine ==================================
    //==============================================================================

    class emEngine : public emUncopyable {

    public:

            // The classes emEngine, emSignal and emScheduler can be used for some
            // kind of CPU sharing and event handling, within one thread of
            // execution.
            //
            // The central point is the virtual method Cycle of emEngine. Derived
            // classes have to overload it for doing something useful in it.
            //
            // Polling
            // -------
            //
            // One possibility is that Cycle could be called by the scheduler on
            // every time slice. The size of a time slice is defined by the actual
            // derivative of emScheduler. It should be about 10 millisecs.
            //
            // Such a polling can be made by waking up the engine once through
            // calling WakeUp, and by always returning true from the Cycle call.
            //
            // Not polling
            // -----------
            //
            // For sparing CPU cycles, polling should be used in rare cases only.
            // Best is to let the engine sleep most times, and to wake it up
            // whenever there is something useful to do.
            //
            // After calling WakeUp on an engine, Cycle is called by the scheduler
            // within the current time slice. The Cycle call should return false for
            // falling asleep again.
            //
            // Through the wake-up mechanism, Cycle can be called multiple times
            // within one time slice. For example, this allows to have chains of
            // signals without any delay through the time slices.
            //
            // Signals
            // -------
            //
            // emSignal can be used for sending a unary event to the engines without
            // having references to them. But all receiving engines must share the
            // same scheduler, and the sender must have a reference to that
            // scheduler. Very often, the sender is even an engine.
            //
            // An engine can receive a signal event by calling IsSignaled from
            // within Cycle. To avoid polling, an engine can be woken up by signals.
            // Therefore any number of signals can be connected to an engine with
            // AddWakeUpSignal.
            //
            // Here is an example of how a sender can send two different signals A
            // and B to receivers:
            //
            // class MySender : public emEngine {
            // private:
            //   emSignal A;
            //   emSignal B;
            // public:
            //   inline MySender(emScheduler & scheduler) : emEngine(scheduler) {}
            //   inline const emSignal & GetSignalA() const { return A; }
            //   inline const emSignal & GetSignalB() const { return B; }
            //   inline void SendA() { Signal(A); }
            //   inline void SendB() { Signal(B); }
            // };
            //
            // class MyReceiver : public emEngine {
            // private:
            //   MySender & Sender;
            // public:
            //   inline MyReceiver(MySender & sender)
            //     : emEngine(sender.GetScheduler()), Sender(sender)
            //   {
            //     AddWakeUpSignal(Sender.GetSignalA());
            //     AddWakeUpSignal(Sender.GetSignalB());
            //   }
            // protected:
            //   virtual bool Cycle();
            // };
            //
            // bool MyReceiver::Cycle()
            // {
            //   if (IsSignaled(Sender.GetSignalA())) {
            //     //...got signal A...
            //   }
            //   if (IsSignaled(Sender.GetSignalB())) {
            //     //...got signal B...
            //   }
            //   return false;
            // }
            //
            // Minimum fairness
            // ----------------
            //
            // A call to Cycle should not block for a too long time. If an engine
            // wants to perform a long-time job, it should divide that job into
            // multiple time slices. The method IsTimeSliceAtEnd can be helpful
            // here. Example:
            //
            // bool JobEngine::Cycle()
            // {
            //   while (!JobFinished()) {
            //     WorkOnTheJobALittleBit();
            //     if (IsTimeSliceAtEnd()) break;
            //   }
            //   return !JobFinished();
            // }
            //
            // The above example provides just minimum fairness, because if there
            // are two such job engines, one would always eat the whole time slice
            // and the other would perform WorkOnTheJobALittleBit only once per time
            // slice.
            //
            // ??? More fairness for parallel jobs may be supported in the future.
            // ??? Maybe the return value of Cycle should be replaced by a method.
            //
            // There is a helper for serializing jobs: emPriSchedAgent

            emEngine(emScheduler & scheduler);
                    // Construct this as a sleeping engine.
                    // Arguments:
                    //   scheduler - The scheduler which has to serve this engine.

            virtual ~emEngine();
                    // Destructor.

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

            void WakeUp();
                    // Wake up this engine. This means that Cycle will be called by
                    // the scheduler within the current time slice. Even if WakeUp
                    // is called from within Cycle, Cycle will be called again
                    // within the current time slice.

            void AddWakeUpSignal(const emSignal & signal);
                    // Wake up this engine whenever the given signal is signaled.
                    // Waking up through a signal is like calling WakeUp. Adding the
                    // same signal more than once does not cost any additional
                    // resources. But the number of additions is counted.

            void RemoveWakeUpSignal(const emSignal & signal);
                    // Remove the given signal from the set of signals waking up
                    // this engine. If a signal has been added multiple times, it
                    // would have to be removed for the same number of times, before
                    // it would really be disconnected from the engine (this is for
                    // solving conflicts between base classes and derived classes:
                    // the derived class does not need to know whether the base
                    // class has added a certain signal for its own purpose). There
                    // is no need to remove signals before destruction of signals or
                    // engines. This is solved by the destructors more efficient.

            int GetWakeUpSignalRefs(const emSignal & signal) const;
                    // Get number of references to the given signal (how often the
                    // signal has been added and not removed).

            void Signal(emSignal & signal);
                    // Signal the given signal. This is just a short cut for
                    // signal.Signal(GetScheduler()). It does not matter on which
                    // emEngine the call is made, as long as they belong to the same
                    // scheduler.

            bool IsSignaled(const emSignal & signal) const;
                    // Ask whether the given signal has been signaled. This must be
                    // called only from within the Cycle method of this engine. The
                    // return value is true, if the signal has been signaled between
                    // the beginning of the previous call to Cycle and the beginning
                    // of the current call to Cycle.

            bool IsTimeSliceAtEnd() const;
                    // Returns true if the current time slice is at its end.

            enum PriorityType {
                    VERY_LOW_PRIORITY  = 0,
                    LOW_PRIORITY       = 1,
                    DEFAULT_PRIORITY   = 2,
                    HIGH_PRIORITY      = 3,
                    VERY_HIGH_PRIORITY = 4
            };
            void SetEnginePriority(PriorityType priority);
            PriorityType GetEnginePriority() const;
                    // Set or get the priority. If two engines are to be called
                    // within the same time slice, the one with the higher priority
                    // is called first. And if the priority is equal, it's the order
                    // of waking up the engines. VERY_LOW_PRIORITY and
                    // VERY_HIGH_PRIORITY are meant to be used by driver
                    // implementations only. A GUI driver should perform input at
                    // VERY_HIGH_PRIORITY and output at VERY_LOW_PRIORITY.

    protected:

            virtual bool Cycle() = 0;
                    // This is the virtual Cycle method mentioned all above. It is
                    // only to be called by the scheduler. There is no problem with
                    // doing a 'delete this' within Cycle, from the schedulers point
                    // of view.
                    // The meaning of the return value is:
                    //  false: Fall asleep if this engine has not been woken up
                    //         since the beginning of this call to Cycle.
                    //   true: Wake up this engine on next time slice if the engine
                    //         will not be woken up earlier or if it has not been
                    //         woken up since the beginning of this call to Cycle.

    private:

            friend class emScheduler;
            friend class emSignal;

            void WakeUpImp();

            static void RemoveLink(emSignal::Link * link);

            emScheduler & Scheduler;
                    // The scheduler.

            emScheduler::EngineRingNode RNode;
                    // A node for this engine in a list of awake engines, garbage
                    // when sleeping.

            emSignal::Link * SLFirst;
                    // First element in the list of connected signals.

            emInt8 AwakeState;
                    // -1 <=> Sleeping.
                    // emScheduler::TimeSlice <=> Busy in current time slice
                    // emScheduler::TimeSlice^1 <=> Busy in next time slice

            emUInt8 Priority;
                    // The priority (0-4).

            emUInt64 Clock;
                    // State of emScheduler::Clock after last call to Cycle().
    };

    inline emScheduler & emEngine::GetScheduler() const
    {
            return Scheduler;
    }

    inline void emEngine::WakeUp()
    {
            if (AwakeState!=Scheduler.TimeSlice) WakeUpImp();
    }

    inline void emEngine::Signal(emSignal & signal)
    {
            if (!signal.RNode.Next) {
                    signal.RNode.Next=Scheduler.PSList.Next;
                    Scheduler.PSList.Next=&signal.RNode;
            }
    }

    inline bool emEngine::IsSignaled(const emSignal & signal) const
    {
            return signal.Clock>Clock;
    }

    inline bool emEngine::IsTimeSliceAtEnd() const
    {
            return Scheduler.IsTimeSliceAtEnd();
    }

    inline emEngine::PriorityType emEngine::GetEnginePriority() const
    {
            return (PriorityType)Priority;
    }

    #endif