//------------------------------------------------------------------------------
// emScheduler.h
//
// Copyright (C) 2005-2008 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 emScheduler_h
#define emScheduler_h

#ifndef emStd2_h
#include <emCore/emStd2.h>
#endif

class emEngine;


//==============================================================================
//================================ emScheduler =================================
//==============================================================================

class emScheduler : public emUncopyable {

public:

        // Abstract base class for a scheduler on emEngine objects.

        emScheduler();
        virtual ~emScheduler();

        virtual int Run() = 0;
                // Do all the time slices until InitiateTermination() is called.

        virtual bool IsTimeSliceAtEnd() const = 0;
                // See emEngine::IsTimeSliceAtEnd().

        virtual void InitiateTermination(int returnCode) = 0;
                // Tell the scheduler to terminate round about after the current
                // time slice.

        emUInt64 GetTimeSliceCounter() const;
                // This is incremented by one on each time slice.

protected:

        void DoTimeSlice();
                // This has to be called by the derived class on each time
                // slice. It performs all the scheduling for one time slice, but
                // it does not wait for IsTimeSliceAtEnd.

private:

        friend class emSignal;
        friend class emEngine;
        friend class emTimer;

        struct SignalRingNode {
                // Node for a circular single-linked list of pending signals.
                SignalRingNode * Next;
        };

        struct EngineRingNode {
                // Node for a circular double-linked list of awake engines.
                EngineRingNode * Prev;
                EngineRingNode * Next;
        };

        SignalRingNode PSList;
                // Circular single-linked list of pending signals. The order is
                // reversed before processing.

        EngineRingNode AwakeLists[10];
                // Circular double-linked lists of awake engines. Index is:
                // Priority*2+TimeSlice

        EngineRingNode * CurrentAwakeList;
                // The list currently processed for calling the engines.

        emEngine * CurrentEngine;
                // The engine currently called, or NULL.

        emUInt32 EngineCount;
                // Total number of engines.

        emInt8 TimeSlice;
                // Whether the current time slice is even (0) or odd (1).

        emUInt64 Clock;
                // Incremented on each loop-run of handling pending signals and
                // calling emEngine::Cycle.

        emUInt64 TimeSliceCounter;
                // Incremented on each time slice.

        void * TimerStuff;
                // A little hack for the implementation of emTimer.
};

inline emUInt64 emScheduler::GetTimeSliceCounter() const
{
        return TimeSliceCounter;
}


//==============================================================================
//============================ emStandardScheduler =============================
//==============================================================================

class emStandardScheduler : public emScheduler {

public:

        // Class for a standard scheduler. It tries to make the time slices 10
        // millisecs long, but IsTimeSliceAtEnd() allows to have 50 millisecs
        // per time slice (for reducing the graphics frame rate when busy).

        emStandardScheduler();

        virtual int Run();
        virtual bool IsTimeSliceAtEnd() const;
        virtual void InitiateTermination(int returnCode);

private:

        bool TerminationInitiated;
        int ReturnCode;
        emUInt64 SyncTime, DeadlineTime;
};


#endif