
#ifndef _PL_LOOP_H__
#define _PL_LOOP_H__
#include "SDL.h"

namespace PL
{

enum TypeEnum{PL_STATE, PL_UPDATE, PL_DRAW, PL_THROTTLE, PL_MOUSEEVENT, PL_KEYEVENT, PL_JOYEVENT, PL_OTHEREVENT, PL_ANYEVENT, PL_SET};

// Programmable event loop base classes

class PL_Set;

class PL_Type
{
    public:
    bool temp;
    PL_Type()
        : temp(false)
    {}
    PL_Type(bool temp)
        : temp(temp)
    {}
    
    virtual ~PL_Type()
    {}
    
    virtual TypeEnum getType() = 0;
};

class PL_State : public PL_Type
{
    public:
    PL_State()
        : PL_Type()
        , set(NULL)
        , dt(0)
        , done(false)
        , lastTime(0)
        , screen(NULL)
        , keystate(NULL)
    {}
    
    PL_Set* set;
    
    float dt;
    bool done;
    Uint32 lastTime;
    SDL_Surface* screen;
    Uint8* keystate;
    
    virtual TypeEnum getType()
    {return PL_STATE;}
};

class PL_Update : public PL_Type
{
    public:
    PL_Update()
        : PL_Type()
    {}
    
    virtual void update(PL_State* state)
    {}
    virtual TypeEnum getType()
    {return PL_UPDATE;}
};

class PL_Draw : public PL_Type
{
    public:
    PL_Draw()
        : PL_Type()
    {}
    
    virtual void draw(PL_State* state)
    {}
    virtual TypeEnum getType()
    {return PL_DRAW;}
};

class PL_Throttle : public PL_Type
{
    public:
    float fps;
    Uint32 maxDelayTime;
    
    PL_Throttle()
        : PL_Type()
        , fps(30)
        , maxDelayTime(1000/fps)
    {}
    
    virtual void throttle(PL_State* state);
    virtual void setFPS(float frames_per_second);
    
    virtual TypeEnum getType()
    {return PL_THROTTLE;}
};

class PL_MouseEvent : public PL_Type
{
    public:
    PL_MouseEvent()
        : PL_Type()
    {}
    
    virtual void buttonUp(PL_State* state, int x, int y, int button)
    {}
    virtual void buttonDown(PL_State* state, int x, int y, int button)
    {}
    virtual void motion(PL_State* state, int x, int y, int dx, int dy)
    {}
    virtual TypeEnum getType()
    {return PL_MOUSEEVENT;}
};

class PL_KeyEvent : public PL_Type
{
    public:
    PL_KeyEvent()
        : PL_Type()
    {}
    
    virtual void keyUp(PL_State* state, SDLKey key, SDLMod modifier)
    {}
    virtual void keyDown(PL_State* state, SDLKey key, SDLMod modifier)
    {
        if(state != NULL)
        {
            if(key == SDLK_ESCAPE)
                state->done = true;
        }
    }
    virtual TypeEnum getType()
    {return PL_KEYEVENT;}
};

class PL_JoyEvent : public PL_Type
{
    public:
    PL_JoyEvent()
        : PL_Type()
    {}
    
    // FIXME: Add joystick index
    virtual void buttonUp(PL_State* state, int button)
    {}
    virtual void buttonDown(PL_State* state, int button)
    {}
    // FIXME: Add axis, ball, etc.
    virtual TypeEnum getType()
    {return PL_JOYEVENT;}
};

class PL_OtherEvent : public PL_Type
{
    public:
    PL_OtherEvent()
        : PL_Type()
    {}
    
    virtual void quit(PL_State* state)
    {
        if(state != NULL)
            state->done = true;
    }
    //SDL_ActiveEvent
    // -enum FocusEnum {MOUSE, INPUT, ACTIVE};
    // -gainFocus(PL_State* state, FocusEnum type)
    // -loseFocus(PL_State* state, FocusEnum type)
    //SDL_ResizeEvent
    // -resize(PL_State* state, unsigned int w, unsigned int h)
    //SDL_ExposeEvent
    // -expose(PL_State* state)  // automatically call draw()?
    //SDL_UserEvent
    // -user(PL_State* state, int code, void* data1, void* data2)
    //SDL_SysWMEvent
    // -system(PL_State* state, SDL_SysWMmsg *msg)
    virtual TypeEnum getType()
    {return PL_OTHEREVENT;}
};

class PL_AnyEvent : public PL_Type
{
    public:
    PL_AnyEvent()
        : PL_Type()
    {}
    
    virtual bool handleEvent(PL_State* state, SDL_Event& event)
    {
        return true;
    }
    virtual TypeEnum getType()
    {return PL_ANYEVENT;}
};


void defaultThrottleCallback(PL_State* state);
void defaultKeyDownCallback(PL_State* state, SDLKey key, SDLMod modifier);
void defaultQuitCallback(PL_State* state);


class PL_Set
{
    public:
    PL_Update* update;
    PL_Draw* draw;
    PL_Throttle* throttle;
    PL_MouseEvent* mouse;
    PL_KeyEvent* key;
    PL_JoyEvent* joy;
    PL_OtherEvent* other;
    PL_AnyEvent* any;
    
    void (*updateCallback)(PL_State* state);
    void (*drawCallback)(PL_State* state);
    void (*throttleCallback)(PL_State* state);
    void (*mouseButtonUpCallback)(PL_State* state, int x, int y, int button);
    void (*mouseButtonDownCallback)(PL_State* state, int x, int y, int button);
    void (*mouseMotionCallback)(PL_State* state, int x, int y, int dx, int dy);
    void (*keyUpCallback)(PL_State* state, SDLKey key, SDLMod modifier);
    void (*keyDownCallback)(PL_State* state, SDLKey key, SDLMod modifier);
    void (*joyButtonUpCallback)(PL_State* state, int button);
    void (*joyButtonDownCallback)(PL_State* state, int button);
    void (*quitCallback)(PL_State* state);
    bool (*anyEventCallback)(PL_State* state, SDL_Event& event);

    PL_Set()
        : update(NULL), draw(NULL), throttle(NULL), mouse(NULL), key(NULL), joy(NULL), other(NULL), any(NULL)
        , updateCallback(NULL), drawCallback(NULL), throttleCallback(&defaultThrottleCallback), mouseButtonUpCallback(NULL), mouseButtonDownCallback(NULL), mouseMotionCallback(NULL), keyUpCallback(NULL), keyDownCallback(&defaultKeyDownCallback), joyButtonUpCallback(NULL), joyButtonDownCallback(NULL), quitCallback(&defaultQuitCallback), anyEventCallback(NULL)
    {}
    PL_Set(PL_Update* update, PL_Draw* draw, PL_Throttle* throttle, PL_MouseEvent* mouse, PL_KeyEvent* key, PL_JoyEvent* joy, PL_OtherEvent* other)
        : update(update), draw(draw), throttle(throttle), mouse(mouse), key(key), joy(joy), other(other), any(NULL)
        , updateCallback(NULL), drawCallback(NULL), throttleCallback(NULL), mouseButtonUpCallback(NULL), mouseButtonDownCallback(NULL), mouseMotionCallback(NULL), keyUpCallback(NULL), keyDownCallback(NULL), joyButtonUpCallback(NULL), joyButtonDownCallback(NULL), quitCallback(NULL), anyEventCallback(NULL)
    {}
    
    virtual ~PL_Set();
    
    virtual TypeEnum getType()
    {return PL_SET;}
};

void PL_Loop(PL_State* state, PL_Set& set);

}

#endif
