/*

JEL Events Library
by Jonathan Dearborn, 2009

JEL is a set of classes that organizes and controls game events.

See tests for example usage.

*/



#ifndef _JEL_H__
#define _JEL_H__


#include <string>
#include <list>
#include <vector>

// FIXME: Make these right for more systems...
#ifndef Uint8
    #define Uint8 unsigned char
#endif
#ifndef Uint32
    #define Uint32 unsigned int
#endif


namespace JEL
{
    using std::string;
    using std::list;
    using std::vector;
    
    class Signal;
    
    
    
	typedef int ActionEventID;
	
	class TimeEventID
	{
	    private:
	    unsigned int id;
	    public:
	    TimeEventID(unsigned int id)
            : id(id)
        {}
        
        unsigned int getID()
        {
            return id;
        }
	};
	
	class Event
	{
		public:
		enum TypeEnum{ACTION, TIME};
		Signal* signal;
		
		Event()
		    : signal(NULL)
		{}
		Event(Signal* signal)
		    : signal(signal)
		{}
		
		virtual void trigger();
		virtual TypeEnum getType() = 0;
	};
	
	class ActionEvent : public Event
	{
		public:
		ActionEventID id;
		
		ActionEvent()
		    : Event()
		    , id(-1)
		{}
		ActionEvent(Signal* signal)
		    : Event(signal)
		    , id(-1)
		{}
		ActionEvent(ActionEventID id, Signal* signal)
		    : Event(signal)
		    , id(id)
		{}
		
		virtual TypeEnum getType()
		{
		    return ACTION;
		}
	};

	class TimeEvent : public Event
	{
		public:
		TimeEventID id;
		Uint32 time;
		
		TimeEvent()
		    : Event()
		    , id(0), time(0)
		{}
		TimeEvent(Signal* signal)
		    : Event(signal)
		    , id(0), time(0)
		{}
		TimeEvent(TimeEventID id, Signal* signal, Uint32 time)
		    : Event(signal)
		    , id(id), time(time)
		{}
		TimeEvent(const TimeEvent& event, Uint32 time, unsigned int idNum)
		    : Event(event.signal)
		    , id(idNum), time(time)
		{}
		
		virtual TypeEnum getType()
		{
		    return TIME;
		}
	};
	
	class Epoch;
	
	class Schedule
	{
		private:
		vector<ActionEvent> actionEvents;  // These are held for convenience.  They are triggered by the user's code (calls to triggerAction()).
		
		Uint32 time;
		Epoch* timeEvents;
		
		unsigned int lastTimeID;
		
		Schedule()
			: time(0), timeEvents(NULL), lastTimeID(0)
		{}
		
		static Schedule Instance;
		
		public:
		
		// The only way to get the Schedule (a singleton)
		static Schedule& instance()
		{
			return Instance;
		}
		
		~Schedule()
		{
		    //clearActionEvents();
		    //clearTimeEvents();
		}
		
		
		ActionEventID addActionEvent(const ActionEvent& event);
		TimeEventID addTimeEvent(const TimeEvent& event, Uint32 absoluteTime);
		TimeEventID addTimeEventFromNow(const TimeEvent& event, Uint32 time);
		
		void clearActionEvents();
		void clearTimeEvents();
		
		void clearPastTimeEvents();
		void clearFutureTimeEvents();
		
		ActionEvent removeActionEvent(ActionEventID id);
		TimeEvent removeTimeEvent(TimeEventID id);
		
		ActionEvent getActionEvent(ActionEventID id);
		TimeEvent getTimeEvent(TimeEventID id);
		
		vector<ActionEvent> getAllActionEvents();
		vector<TimeEvent> getAllTimeEvents();
		
		void addActionEvents(const vector<ActionEvent>& events);
		void addTimeEvents(const vector<TimeEvent>& events);
		
		
		void update(Uint32 dt);
		
		void setTime(Uint32 time);
		Uint32 getTime();
		
		// Trigger is here (not in the TimeEvent class) so I can search down iteratively, instead of recursively.  I think this is cheaper and faster.
		void triggerTime(Uint32 triggerTime);  // Recursively searches for and triggers any events that have a trigger time earlier than the given time.
		
		void triggerAction(ActionEventID id);
		void triggerTime(TimeEventID id);
		
	};
    
    
    
    
    
// A class that holds a variable or a pointer to a variable.
// Abstracts access to such data
    template<class T>
    class LinkVar
    {
    private:
        T _value;
        T* _link;

    public:
        LinkVar()
                : _value(T())
                , _link(NULL)
        {}
        LinkVar(T value)
                : _value(value)
                , _link(NULL)
        {}
        LinkVar(T* value_ptr)
                : _value(T())
                , _link(value_ptr)
        {}
        void link(T* value_ptr)
        {
            _link = value_ptr;
        }
        T get()
        {
            if (_link == NULL)
                return _value;
            return *_link;
        }
        void set(T value)
        {
            if (_link == NULL)
                _value = value;
            else
                *_link = value;
        }
    };
    
    
    
    
    
    

// *****************************
// ********** Signals **********
// *****************************



    class Signal
    {
    public:
        Signal()
        {}
        virtual ~Signal()
        {}
        virtual void trigger() = 0;
    };


    // Intermediate derived class to make Collectors possible.
    // ...but are collectors necessary?  You can just get a list from the emitter.
    template<class RETURN_TYPE>
    class SignalType : public Signal
    {
    protected:
        LinkVar<RETURN_TYPE> value;
    public:
        SignalType()
                : Signal()
        {}
        virtual ~SignalType()
        {}
        void setValue(RETURN_TYPE Value)
        {
            value.set(Value);
        }
        void linkValue(RETURN_TYPE* value_ptr)
        {
            value.link(value_ptr);
        }
        RETURN_TYPE getValue()
        {
            return value.get();
        }
    };

    template<>
    class SignalType<void> : public Signal
    {
    public:
        SignalType()
                : Signal()
        {}
        virtual ~SignalType()
        {}
    };




// Signal with 0 arguments

    template<class RETURN_TYPE>
    class Signal0 : public SignalType<RETURN_TYPE>
    {
    private:
        RETURN_TYPE (*_fn)();

    public:
        Signal0()
                : SignalType<RETURN_TYPE>()
                , _fn(NULL)
        {}
        Signal0(RETURN_TYPE (*FN)())
                : SignalType<RETURN_TYPE>()
                , _fn(FN)
        {}
        virtual ~Signal0()
        {}

        void setFn(RETURN_TYPE (*FN)())
        {
            _fn = FN;
        }
        RETURN_TYPE (*getFn())()
        {
            return _fn;
        }

        void trigger()
        {
            if (_fn != NULL)
                SignalType<RETURN_TYPE>::value.set(_fn());
        }
    };

// Specialization of Signal0 for void
    template<>
    class Signal0 <void> : public SignalType<void>
    {
    private:
        void (*_fn)();

    public:
        Signal0()
                : SignalType<void>()
                , _fn(NULL)
        {}
        Signal0(void (*FN)())
                : SignalType<void>()
                , _fn(FN)
        {}
        virtual ~Signal0()
        {}

        void setFn(void (*FN)())
        {
            _fn = FN;
        }
        void (*getFn())()
        {
            return _fn;
        }

        void trigger()
        {
            if (_fn != NULL)
                _fn();
        }
    };

// Signal with 1 argument

    template<class RETURN_TYPE, class ARG1_TYPE>
    class Signal1 : public SignalType<RETURN_TYPE>
    {
    private:
        RETURN_TYPE (*_fn)(ARG1_TYPE);
        LinkVar<ARG1_TYPE> arg1;

    public:
        Signal1()
                : SignalType<RETURN_TYPE>()
                , _fn(NULL)
        {}
        Signal1(RETURN_TYPE (*FN)(ARG1_TYPE))
                : SignalType<RETURN_TYPE>()
                , _fn(FN)
        {}
        virtual ~Signal1()
        {}

        void setFn(RETURN_TYPE (*FN)(ARG1_TYPE))
        {
            _fn = FN;
        }
        RETURN_TYPE (*getFn())(ARG1_TYPE)
        {
            return _fn;
        }

        void setArg1(ARG1_TYPE value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }

        void trigger()
        {
            if (_fn != NULL)
                SignalType<RETURN_TYPE>::value.set(_fn(arg1.get()));
        }
    };

// Specialization of Signal1 for void
    template<class ARG1_TYPE>
    class Signal1 <void, ARG1_TYPE>: public SignalType<void>
    {
    private:
        void (*_fn)(ARG1_TYPE);
        LinkVar<ARG1_TYPE> arg1;

    public:
        Signal1()
                : SignalType<void>()
                , _fn(NULL)
        {}
        Signal1(void (*FN)(ARG1_TYPE))
                : SignalType<void>()
                , _fn(FN)
        {}
        virtual ~Signal1()
        {}

        void setFn(void (*FN)(ARG1_TYPE))
        {
            _fn = FN;
        }
        void (*getFn())(ARG1_TYPE)
        {
            return _fn;
        }

        void setArg1(ARG1_TYPE value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }

        void trigger()
        {
            if (_fn != NULL)
                _fn(arg1.get());
        }


    };


// Signal with 2 arguments

    template<class RETURN_TYPE, class ARG1_TYPE, class ARG2_TYPE>
    class Signal2 : public SignalType<RETURN_TYPE>
    {
    private:
        RETURN_TYPE (*_fn)(ARG1_TYPE, ARG2_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;

    public:
        Signal2()
                : SignalType<RETURN_TYPE>()
                , _fn(NULL)
        {}
        Signal2(RETURN_TYPE (*FN)(ARG1_TYPE, ARG2_TYPE))
                : SignalType<RETURN_TYPE>()
                , _fn(FN)
        {}
        virtual ~Signal2()
        {}

        void setFn(RETURN_TYPE (*FN)(ARG1_TYPE, ARG2_TYPE))
        {
            _fn = FN;
        }
        RETURN_TYPE (*getFn())(ARG1_TYPE, ARG2_TYPE)
        {
            return _fn;
        }

        void setArg1(ARG1_TYPE value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        void setArg2(ARG2_TYPE value)
        {
            arg2.set(value);
        }
        void linkArg2(ARG2_TYPE* value_ptr)
        {
            arg2.link(value_ptr);
        }
        ARG2_TYPE getArg2()
        {
            return arg2.get();
        }
        
        void setArgs(ARG1_TYPE val1, ARG2_TYPE val2)
        {
            arg1.set(val1);
            arg2.set(val2);
        }
        void setArgs(ARG1_TYPE* val1, ARG2_TYPE val2)
        {
            arg1.link(val1);
            arg2.set(val2);
        }
        void setArgs(ARG1_TYPE val1, ARG2_TYPE* val2)
        {
            arg1.set(val1);
            arg2.link(val2);
        }
        void setArgs(ARG1_TYPE* val1, ARG2_TYPE* val2)
        {
            arg1.link(val1);
            arg2.link(val2);
        }

        void trigger()
        {
            if (_fn != NULL)
                SignalType<RETURN_TYPE>::value.set(_fn(arg1.get(), arg2.get()));
        }


    };

// Specialization of Signal2 for void
    template<class ARG1_TYPE, class ARG2_TYPE>
    class Signal2 <void, ARG1_TYPE, ARG2_TYPE>: public SignalType<void>
    {
    private:
        void (*_fn)(ARG1_TYPE, ARG2_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;

    public:
        Signal2()
                : SignalType<void>()
                , _fn(NULL)
        {}
        Signal2(void (*FN)(ARG1_TYPE, ARG2_TYPE))
                : SignalType<void>()
                , _fn(FN)
        {}
        virtual ~Signal2()
        {}

        void setFn(void (*FN)(ARG1_TYPE, ARG2_TYPE))
        {
            _fn = FN;
        }
        void (*getFn())(ARG1_TYPE, ARG2_TYPE)
        {
            return _fn;
        }

        void setArg1(ARG1_TYPE value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        void setArg2(ARG2_TYPE value)
        {
            arg2.set(value);
        }
        void linkArg2(ARG2_TYPE* value_ptr)
        {
            arg2.link(value_ptr);
        }
        ARG2_TYPE getArg2()
        {
            return arg2.get();
        }
        
        void setArgs(ARG1_TYPE val1, ARG2_TYPE val2)
        {
            arg1.set(val1);
            arg2.set(val2);
        }
        void setArgs(ARG1_TYPE* val1, ARG2_TYPE val2)
        {
            arg1.link(val1);
            arg2.set(val2);
        }
        void setArgs(ARG1_TYPE val1, ARG2_TYPE* val2)
        {
            arg1.set(val1);
            arg2.link(val2);
        }
        void setArgs(ARG1_TYPE* val1, ARG2_TYPE* val2)
        {
            arg1.link(val1);
            arg2.link(val2);
        }

        void trigger()
        {
            if (_fn != NULL)
                _fn(arg1.get(), arg2.get());
        }


    };


// Signal with 3 arguments

    template<class RETURN_TYPE, class ARG1_TYPE, class ARG2_TYPE, class ARG3_TYPE>
    class Signal3 : public SignalType<RETURN_TYPE>
    {
    private:
        RETURN_TYPE (*_fn)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;
        LinkVar<ARG3_TYPE> arg3;

    public:
        Signal3()
                : SignalType<RETURN_TYPE>()
                , _fn(NULL)
        {}
        Signal3(RETURN_TYPE (*FN)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE))
                : SignalType<RETURN_TYPE>()
                , _fn(FN)
        {}
        virtual ~Signal3()
        {}

        void setFn(RETURN_TYPE (*FN)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE))
        {
            _fn = FN;
        }
        RETURN_TYPE (*getFn())(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE)
        {
            return _fn;
        }

        void setArg1(ARG1_TYPE value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        void setArg2(ARG2_TYPE value)
        {
            arg2.set(value);
        }
        void linkArg2(ARG2_TYPE* value_ptr)
        {
            arg2.link(value_ptr);
        }
        ARG2_TYPE getArg2()
        {
            return arg2.get();
        }
        void setArg3(ARG3_TYPE value)
        {
            arg3.set(value);
        }
        void linkArg3(ARG3_TYPE* value_ptr)
        {
            arg3.link(value_ptr);
        }
        ARG3_TYPE getArg3()
        {
            return arg3.get();
        }

        void trigger()
        {
            if (_fn != NULL)
                SignalType<RETURN_TYPE>::value.set(_fn(arg1.get(), arg2.get(), arg3.get()));
        }


    };

// Specialization of Signal3 for void
    template<class ARG1_TYPE, class ARG2_TYPE, class ARG3_TYPE>
    class Signal3 <void, ARG1_TYPE, ARG2_TYPE, ARG3_TYPE>: public SignalType<void>
    {
    private:
        void (*_fn)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE);
        LinkVar<ARG1_TYPE> arg1;
        LinkVar<ARG2_TYPE> arg2;
        LinkVar<ARG3_TYPE> arg3;

    public:
        Signal3()
                : SignalType<void>()
                , _fn(NULL)
        {}
        Signal3(void (*FN)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE))
                : SignalType<void>()
                , _fn(FN)
        {}
        virtual ~Signal3()
        {}

        void setFn(void (*FN)(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE))
        {
            _fn = FN;
        }
        void (*getFn())(ARG1_TYPE, ARG2_TYPE, ARG3_TYPE)
        {
            return _fn;
        }

        void setArg1(ARG1_TYPE value)
        {
            arg1.set(value);
        }
        void linkArg1(ARG1_TYPE* value_ptr)
        {
            arg1.link(value_ptr);
        }
        ARG1_TYPE getArg1()
        {
            return arg1.get();
        }
        void setArg2(ARG2_TYPE value)
        {
            arg2.set(value);
        }
        void linkArg2(ARG2_TYPE* value_ptr)
        {
            arg2.link(value_ptr);
        }
        ARG2_TYPE getArg2()
        {
            return arg2.get();
        }
        void setArg3(ARG3_TYPE value)
        {
            arg3.set(value);
        }
        void linkArg3(ARG3_TYPE* value_ptr)
        {
            arg3.link(value_ptr);
        }
        ARG3_TYPE getArg3()
        {
            return arg3.get();
        }

        void trigger()
        {
            if (_fn != NULL)
                _fn(arg1.get(), arg2.get(), arg3.get());
        }


    };
    
    

}  // END namespace JEL

#endif
