/*
StickyInput by Jonathan Dearborn
This file is licensed under the TSL.
See StickyInput.h for details.
*/

#include "StickyInput.h"
#include <cmath>

namespace SI
{

unsigned int numjoysticks = 0;
SDL_Joystick** joysticks = NULL;
Uint8* keystates = NULL;
bool wasinit = false;


inline bool inrange(float low, float value, float high)
{
    return (low <= value && value <= high);
}

void Reset(bool print)
{
    wasinit = false;
    numjoysticks = SDL_NumJoysticks();

    if(numjoysticks > 0)
    {
        if(print)
            printf("Closing %d joystick%s...\n", numjoysticks, (numjoysticks > 1? "s" : ""));

        for(unsigned int i = 0; i < numjoysticks; i++ )
        {
            if(print)
                printf("%d.", i+1);
            SDL_JoystickClose(SDL_JoystickOpen(i));
        }
        if(print)
            printf("\n");

    }

    if(SDL_WasInit(SDL_INIT_JOYSTICK) & SDL_INIT_JOYSTICK)
        SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
    delete[] joysticks;
    joysticks = NULL;

    // Init joysticks
    SDL_InitSubSystem(SDL_INIT_JOYSTICK);

    numjoysticks = SDL_NumJoysticks();
    if(numjoysticks > 0)
    {
        if(print)
        {
            printf("Opening joysticks: %d found...\n", numjoysticks);
        }

        joysticks = new SDL_Joystick*[numjoysticks];

        for(unsigned int i = 0; i < numjoysticks; i++ )
        {
            if(print)
            {
                printf("    \"%s\"...", SDL_JoystickName(i));
                joysticks[i] = SDL_JoystickOpen(i);
                if(joysticks[i] != NULL)
                    printf("  Ok\n");
                else
                    printf("  Failed\n");
            }
            else
                joysticks[i] = SDL_JoystickOpen(i);
        }

    }
    else if(print)
        printf("Opening joysticks: None found\n");

    // Init key holding
    keystates = SDL_GetKeyState(NULL);
    wasinit = true;
}

void PrintButton(const Button& button)
{
    switch(button.type)
    {
        case Button::KEY:
        printf("Key: %s\n", SDL_GetKeyName(button.key));
        break;
        case Button::MOUSEBUTTON:
        printf("Mouse #%d: Button %d\n", button.mouse, button.mouseButton);
        break;
        case Button::JOYBUTTON:
        printf("Joystick #%d: Button %d\n", button.joystick, button.joyItem);
        break;
        case Button::JOYAXIS_NEG:
        printf("Joystick #%d: Neg Axis %d\n", button.joystick, button.joyItem);
        break;
        case Button::JOYAXIS_POS:
        printf("Joystick #%d: Pos Axis %d\n", button.joystick, button.joyItem);
        break;
        case Button::JOYHAT_CENTER:
        printf("Joystick #%d: Hat %d Center\n", button.joystick, button.joyItem);
        break;
        case Button::JOYHAT_UP:
        printf("Joystick #%d: Hat %d Up\n", button.joystick, button.joyItem);
        break;
        case Button::JOYHAT_DOWN:
        printf("Joystick #%d: Hat %d Down\n", button.joystick, button.joyItem);
        break;
        case Button::JOYHAT_LEFT:
        printf("Joystick #%d: Hat %d Left\n", button.joystick, button.joyItem);
        break;
        case Button::JOYHAT_RIGHT:
        printf("Joystick #%d: Hat %d Right\n", button.joystick, button.joyItem);
        break;
        case Button::JOYHAT_LEFTUP:
        printf("Joystick #%d: Hat %d LeftUp\n", button.joystick, button.joyItem);
        break;
        case Button::JOYHAT_LEFTDOWN:
        printf("Joystick #%d: Hat %d LeftDown\n", button.joystick, button.joyItem);
        break;
        case Button::JOYHAT_RIGHTUP:
        printf("Joystick #%d: Hat %d RightUp\n", button.joystick, button.joyItem);
        break;
        case Button::JOYHAT_RIGHTDOWN:
        printf("Joystick #%d: Hat %d RightDown\n", button.joystick, button.joyItem);
        break;
        default:
        break;
    }
}


/*
MonitorInput() runs a loop, waiting for input events.  When an event
happens, the information is sent to the function that is given as an argument.
That function then should present the information to the user somehow.
A sample function is provided, PrintButton().
When SDLK_ESCAPE is pressed, the loop is ended and the function returns.
*/
void MonitorInput(void (*fn)(const Button&), int deadZone)
{
    if(fn == NULL)
        return;

    Button result(SDLK_UNKNOWN);

    SDL_Event event;
    bool done = false;
    while(!done)
    {
        while(SDL_PollEvent(&event))
        {
            if(event.type == SDL_QUIT)
            {
                done = true;
            }
            else if(event.type == SDL_KEYDOWN)
            {
                if(event.key.keysym.sym == SDLK_ESCAPE)
                    done = true;
                else
                {
                    result.type = Button::KEY;
                    result.key = event.key.keysym.sym;
                    fn(result);
                }
            }
            else if(event.type == SDL_JOYAXISMOTION)
            {
                result.joystick = event.jaxis.which;
                result.joyItem = event.jaxis.axis;
                if(event.jaxis.value < -deadZone)
                {
                    result.type = Button::JOYAXIS_NEG;
                    fn(result);
                }
                else if(event.jaxis.value > deadZone)
                {
                    result.type = Button::JOYAXIS_POS;
                    fn(result);
                }
            }
            else if(event.type == SDL_JOYBUTTONDOWN)
            {
                result.type = Button::JOYBUTTON;
                result.joystick = event.jbutton.which;
                result.joyItem = event.jbutton.button;

                fn(result);
            }
            else if(event.type == SDL_MOUSEBUTTONDOWN)
            {
                result.type = Button::MOUSEBUTTON;
                result.mouse = 0;
                result.mouseButton = event.button.button;

                fn(result);
            }
            else if(event.type == SDL_JOYHATMOTION)
            {
                result.type = Button::JOYHAT_CENTER;
                if((event.jhat.value & SDL_HAT_LEFTUP) == SDL_HAT_LEFTUP)
                    result.type = Button::JOYHAT_LEFTUP;
                if((event.jhat.value & SDL_HAT_LEFTDOWN) == SDL_HAT_LEFTDOWN)
                    result.type = Button::JOYHAT_LEFTDOWN;
                if((event.jhat.value & SDL_HAT_RIGHTUP) == SDL_HAT_RIGHTUP)
                    result.type = Button::JOYHAT_RIGHTUP;
                if((event.jhat.value & SDL_HAT_RIGHTDOWN) == SDL_HAT_RIGHTDOWN)
                    result.type = Button::JOYHAT_RIGHTDOWN;
                if(event.jhat.value & SDL_HAT_UP)
                    result.type = Button::JOYHAT_UP;
                if(event.jhat.value & SDL_HAT_DOWN)
                    result.type = Button::JOYHAT_DOWN;
                if(event.jhat.value & SDL_HAT_LEFT)
                    result.type = Button::JOYHAT_LEFT;
                if(event.jhat.value & SDL_HAT_RIGHT)
                    result.type = Button::JOYHAT_RIGHT;
                result.joystick = event.jhat.which;
                result.joyItem = event.jhat.hat;

                fn(result);
            }
        }


        SDL_Delay(100);
    }
}



/*
WaitPress() waits in a loop until a valid input 'button' is pressed.
These 'buttons' can be a keyboard key or any axis/button/hat/ball
on a joystick.  If the optional pointer to a Button object is filled in, then
that will be set to the same value as the return value.
If 'waitTime' is nonzero, the function will return after that many milliseconds.
*/
Button WaitPress(Button* button, int deadZone, Uint32 waitTime, Uint32 delay)
{
    Button result(SDLK_UNKNOWN);

    Uint32 beginWait = SDL_GetTicks();

    SDL_Event event;
    bool done = false;
    while(!done)
    {
        while(SDL_PollEvent(&event))
        {
            if(event.type == SDL_QUIT)
            {
                done = true;
            }
            else if(event.type == SDL_KEYDOWN)
            {
                result.key = event.key.keysym.sym;
                done = true;
            }
            else if(event.type == SDL_JOYAXISMOTION)
            {
                result.joystick = event.jaxis.which;
                result.joyItem = event.jaxis.axis;
                if(event.jaxis.value < -deadZone)
                {
                    result.type = Button::JOYAXIS_NEG;
                    done = true;
                }
                else if(event.jaxis.value > deadZone)
                {
                    result.type = Button::JOYAXIS_POS;
                    done = true;
                }
            }
            else if(event.type == SDL_JOYBUTTONDOWN)
            {
                result.type = Button::JOYBUTTON;
                result.joystick = event.jbutton.which;
                result.joyItem = event.jbutton.button;
                done = true;
            }
            else if(event.type == SDL_MOUSEBUTTONDOWN)
            {
                result.type = Button::MOUSEBUTTON;
                result.mouse = 0;
                result.mouseButton = event.button.button;

                done = true;
            }
            else if(event.type == SDL_JOYHATMOTION)
            {
                result.type = Button::JOYHAT_CENTER;
                if((event.jhat.value & SDL_HAT_LEFTUP) == SDL_HAT_LEFTUP)
                    result.type = Button::JOYHAT_LEFTUP;
                if((event.jhat.value & SDL_HAT_LEFTDOWN) == SDL_HAT_LEFTDOWN)
                    result.type = Button::JOYHAT_LEFTDOWN;
                if((event.jhat.value & SDL_HAT_RIGHTUP) == SDL_HAT_RIGHTUP)
                    result.type = Button::JOYHAT_RIGHTUP;
                if((event.jhat.value & SDL_HAT_RIGHTDOWN) == SDL_HAT_RIGHTDOWN)
                    result.type = Button::JOYHAT_RIGHTDOWN;
                if(event.jhat.value & SDL_HAT_UP)
                    result.type = Button::JOYHAT_UP;
                if(event.jhat.value & SDL_HAT_DOWN)
                    result.type = Button::JOYHAT_DOWN;
                if(event.jhat.value & SDL_HAT_LEFT)
                    result.type = Button::JOYHAT_LEFT;
                if(event.jhat.value & SDL_HAT_RIGHT)
                    result.type = Button::JOYHAT_RIGHT;
                result.joystick = event.jhat.which;
                result.joyItem = event.jhat.hat;
                done = true;
            }
        }

        SDL_Delay(delay);
        if(waitTime > 0 && SDL_GetTicks() - beginWait > waitTime)
            done = true;
    }


    if(button != NULL)
        *button = result;
    return result;
}



// JoystickInfo class

bool JoystickInfo::load(unsigned int index)
{
    if(index < numjoysticks)
    {
        this->index = index;
        joystick = joysticks[index];
        name = SDL_JoystickName(index);
        axes = SDL_JoystickNumAxes(joystick);
        buttons = SDL_JoystickNumButtons(joystick);
        hats = SDL_JoystickNumHats(joystick);
        balls = SDL_JoystickNumBalls(joystick);
        return true;
    }
    else
    {
        this->index = 0;
        joystick = NULL;
        name = NULL;
        axes = 0;
        buttons = 0;
        hats = 0;
        balls = 0;
        return false;
    }
}



// Button class

// Very similar to getState(), but checks against deadZone too.
bool Button::checkHold()
{
    if(type != KEY && type != MOUSEBUTTON && (joystick >= numjoysticks || joysticks == NULL))
        return false;

    switch(type)
    {
        case KEY:
            return (keystates != NULL && keystates[key]);

        case MOUSEBUTTON:
            //printf("Mouse: button: %d, state: %d, mask: %d\n", mouseButton, SDL_GetMouseState(NULL, NULL), SDL_BUTTON((mouseButton+1)));
            //printf("LEFT: %d, RIGHT: %d, MIDDLE: %d, LMASK: %d, RMASK: %d, MMASK: %d\n", SDL_BUTTON_LEFT, SDL_BUTTON_RIGHT, SDL_BUTTON_MIDDLE, SDL_BUTTON_LMASK, SDL_BUTTON_RMASK, SDL_BUTTON_MMASK);
            return (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(mouseButton+1));

        case JOYBUTTON:
            return SDL_JoystickGetButton(joysticks[joystick], joyItem);

        case JOYAXIS_NEG:
            //printf("Joyaxis Neg: %d (%d)\n", SDL_JoystickGetAxis(joysticks[joystick], joyItem), joyItem);
            return (SDL_JoystickGetAxis(joysticks[joystick], joyItem) <= -deadZone);
        case JOYAXIS_POS:
            return (SDL_JoystickGetAxis(joysticks[joystick], joyItem) >= deadZone);

        case JOYHAT_UP:
            return (distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_UP : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_UP);
        case JOYHAT_DOWN:
            return (distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_DOWN : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_DOWN);
        case JOYHAT_LEFT:
            return (distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_LEFT : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_LEFT);
        case JOYHAT_RIGHT:
            return (distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_RIGHT : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_RIGHT);
        case JOYHAT_LEFTUP:
            return (distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_LEFTUP : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_LEFTUP);
        case JOYHAT_LEFTDOWN:
            return (distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_LEFTDOWN : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_LEFTDOWN);
        case JOYHAT_RIGHTUP:
            return (distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_RIGHTUP : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_RIGHTUP);
        case JOYHAT_RIGHTDOWN:
            return (distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_RIGHTDOWN : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_RIGHTDOWN);
        default:
            break;
    }
    return false;
}

bool Button::checkDown(const SDL_Event& event)
{
    if(event.type == SDL_KEYDOWN)
    {
        return (type == KEY && event.key.keysym.sym == key);
    }

    if(joystick >= numjoysticks || joysticks == NULL)
        return false;

    if(event.type == SDL_JOYBUTTONDOWN)
    {
        return (type == JOYBUTTON && joystick == event.jbutton.which && event.jbutton.button == joyItem);
    }
    if(event.type == SDL_JOYAXISMOTION)
    {
        if((type == JOYAXIS_NEG || type == JOYAXIS_POS) && joystick == event.jaxis.which && event.jaxis.axis == joyItem)
            oldAxis = event.jaxis.value;
        else
            return false;
        return (type == JOYAXIS_NEG && event.jaxis.value <= -deadZone)
               || (type == JOYAXIS_POS && event.jaxis.value >= deadZone);
    }
    if(event.type == SDL_MOUSEBUTTONDOWN)
    {
        return (type == MOUSEBUTTON && /*event.button.which == mouse &&*/ event.button.button == mouseButton+1);
    }
    if(event.type == SDL_JOYHATMOTION)
    {
        switch(type)
        {
            case JOYHAT_UP:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    oldHat = event.jhat.value;
                    return (distinctHatDirections? event.jhat.value == SDL_HAT_UP : event.jhat.value & SDL_HAT_UP);
                }
                break;
            case JOYHAT_DOWN:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    oldHat = event.jhat.value;
                    return (distinctHatDirections? event.jhat.value == SDL_HAT_DOWN : event.jhat.value & SDL_HAT_DOWN);
                }
                break;
            case JOYHAT_LEFT:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    oldHat = event.jhat.value;
                    return (distinctHatDirections? event.jhat.value == SDL_HAT_LEFT : event.jhat.value & SDL_HAT_LEFT);
                }
                break;
            case JOYHAT_RIGHT:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    oldHat = event.jhat.value;
                    return (distinctHatDirections? event.jhat.value == SDL_HAT_RIGHT : event.jhat.value & SDL_HAT_RIGHT);
                }
                break;
            case JOYHAT_LEFTUP:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    oldHat = event.jhat.value;
                    return (distinctHatDirections? event.jhat.value == SDL_HAT_LEFTUP : event.jhat.value & SDL_HAT_LEFTUP);
                }
                break;
            case JOYHAT_LEFTDOWN:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    oldHat = event.jhat.value;
                    return (distinctHatDirections? event.jhat.value == SDL_HAT_LEFTDOWN : event.jhat.value & SDL_HAT_LEFTDOWN);
                }
                break;
            case JOYHAT_RIGHTUP:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    oldHat = event.jhat.value;
                    return (distinctHatDirections? event.jhat.value == SDL_HAT_RIGHTUP : event.jhat.value & SDL_HAT_RIGHTUP);
                }
                break;
            case JOYHAT_RIGHTDOWN:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    oldHat = event.jhat.value;
                    return (distinctHatDirections? event.jhat.value == SDL_HAT_RIGHTDOWN : event.jhat.value & SDL_HAT_RIGHTDOWN);
                }
                break;
            default:
                break;
        }

    }
    return false;
}


bool Button::checkUp(const SDL_Event& event)
{
    if(event.type == SDL_KEYUP)
    {
        return (type == KEY && event.key.keysym.sym == key);
    }

    if(joystick >= numjoysticks || joysticks == NULL)
        return false;

    if(event.type == SDL_JOYBUTTONUP)
    {
        return (type == JOYBUTTON && joystick == event.jbutton.which && event.jbutton.button == joyItem);
    }
    // Axis in opposite dir (or less than deadZone)...  Hat in another dir...
    if(event.type == SDL_JOYAXISMOTION)
    {
        return (type == JOYAXIS_NEG && joystick == event.jaxis.which && event.jaxis.axis == joyItem && oldAxis <= -deadZone && event.jaxis.value > -deadZone)
               || (type == JOYAXIS_POS && joystick == event.jaxis.which && event.jaxis.axis == joyItem && oldAxis >= deadZone && event.jaxis.value < deadZone);
    }
    if(event.type == SDL_MOUSEBUTTONUP)
    {
        return (type == MOUSEBUTTON && /*event.button.which == mouse &&*/ event.button.button == mouseButton);
    }
    if(event.type == SDL_JOYHATMOTION)
    {
        switch(type)
        {
            case JOYHAT_UP:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    if(distinctHatDirections)
                        return (oldHat == SDL_HAT_UP && event.jhat.value != SDL_HAT_UP);
                    else
                        return (oldHat & SDL_HAT_UP && !(event.jhat.value & SDL_HAT_UP));
                }
                break;
            case JOYHAT_DOWN:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    if(distinctHatDirections)
                        return (oldHat == SDL_HAT_DOWN && event.jhat.value != SDL_HAT_DOWN);
                    else
                        return (oldHat & SDL_HAT_DOWN && !(event.jhat.value & SDL_HAT_DOWN));
                }
                break;
            case JOYHAT_LEFT:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    if(distinctHatDirections)
                        return (oldHat == SDL_HAT_LEFT && event.jhat.value != SDL_HAT_LEFT);
                    else
                        return (oldHat & SDL_HAT_LEFT && !(event.jhat.value & SDL_HAT_LEFT));
                }
                break;
            case JOYHAT_RIGHT:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    if(distinctHatDirections)
                        return (oldHat == SDL_HAT_RIGHT && event.jhat.value != SDL_HAT_RIGHT);
                    else
                        return (oldHat & SDL_HAT_RIGHT && !(event.jhat.value & SDL_HAT_RIGHT));
                }
                break;
            case JOYHAT_LEFTUP:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    if(distinctHatDirections)
                        return (oldHat == SDL_HAT_LEFTUP && event.jhat.value != SDL_HAT_LEFTUP);
                    else
                        return (oldHat & SDL_HAT_LEFTUP && !(event.jhat.value & SDL_HAT_LEFTUP));
                }
                break;
            case JOYHAT_LEFTDOWN:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    if(distinctHatDirections)
                        return (oldHat == SDL_HAT_LEFTDOWN && event.jhat.value != SDL_HAT_LEFTDOWN);
                    else
                        return (oldHat & SDL_HAT_LEFTDOWN && !(event.jhat.value & SDL_HAT_LEFTDOWN));
                }
                break;
            case JOYHAT_RIGHTUP:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    if(distinctHatDirections)
                        return (oldHat == SDL_HAT_RIGHTUP && event.jhat.value != SDL_HAT_RIGHTUP);
                    else
                        return (oldHat & SDL_HAT_RIGHTUP && !(event.jhat.value & SDL_HAT_RIGHTUP));
                }
                break;
            case JOYHAT_RIGHTDOWN:
                if(joystick == event.jhat.which && event.jhat.hat == joyItem)
                {
                    if(distinctHatDirections)
                        return (oldHat == SDL_HAT_RIGHTDOWN && event.jhat.value != SDL_HAT_RIGHTDOWN);
                    else
                        return (oldHat & SDL_HAT_RIGHTDOWN && !(event.jhat.value & SDL_HAT_RIGHTDOWN));
                }
                break;
            default:
                break;
        }

        return false;
    }
    return false;
}

/*
Returns the current value of the button.  This value ranges from 0
to 32767.
*/
#define MAX_STATE_VALUE 32767

Uint16 Button::getState()
{
    if(type != KEY && type != MOUSEBUTTON && (joystick >= numjoysticks || joysticks == NULL))
        return 0;

    Sint16 temp;
    switch(type)
    {
        case KEY:
            return (keystates != NULL && keystates[key]? MAX_STATE_VALUE : 0);
            break;

        case MOUSEBUTTON:
            return ((SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(mouseButton+1))? MAX_STATE_VALUE : 0);

        case JOYBUTTON:
            return (SDL_JoystickGetButton(joysticks[joystick], joyItem)? MAX_STATE_VALUE : 0);
            break;

        case JOYAXIS_NEG:
            temp = SDL_JoystickGetAxis(joysticks[joystick], joyItem);
            if(temp >= 0)
                return 0;
            if(temp < -MAX_STATE_VALUE)
                return MAX_STATE_VALUE;
            return -temp;
            break;
        case JOYAXIS_POS:
            temp = SDL_JoystickGetAxis(joysticks[joystick], joyItem);
            if(temp <= 0)
                return 0;
            if(temp > MAX_STATE_VALUE)
                return MAX_STATE_VALUE;
            return temp;
            break;

        case JOYHAT_UP:
            if(distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_UP : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_UP)
                return MAX_STATE_VALUE;
            return 0;
            break;
        case JOYHAT_DOWN:
            if(distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_DOWN : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_DOWN)
                return MAX_STATE_VALUE;
            return 0;
            break;
        case JOYHAT_LEFT:
            if(distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_LEFT : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_LEFT)
                return MAX_STATE_VALUE;
            return 0;
            break;
        case JOYHAT_RIGHT:
            if(distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_RIGHT : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_RIGHT)
                return MAX_STATE_VALUE;
            return 0;
            break;
        case JOYHAT_LEFTUP:
            if(distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_LEFTUP : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_LEFTUP)
                return MAX_STATE_VALUE;
            return 0;
            break;
        case JOYHAT_LEFTDOWN:
            if(distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_LEFTDOWN : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_LEFTDOWN)
                return MAX_STATE_VALUE;
            return 0;
            break;
        case JOYHAT_RIGHTUP:
            if(distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_RIGHTUP : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_RIGHTUP)
                return MAX_STATE_VALUE;
            return 0;
            break;
        case JOYHAT_RIGHTDOWN:
            if(distinctHatDirections? SDL_JoystickGetHat(joysticks[joystick], joyItem) == SDL_HAT_RIGHTDOWN : SDL_JoystickGetHat(joysticks[joystick], joyItem) & SDL_HAT_RIGHTDOWN)
                return MAX_STATE_VALUE;
            return 0;
            break;
        default:
            break;
    }
    return 0;
}







// Checks the Buttons or sets the mouse position each frame
// Also piles up deltas if events are not being used.
void Cursor::update(float dt)
{
    my_dt = dt;
    bool speedup = false;
    if(type == BUTTONS)
    {
        bool up = button[0].checkHold();
        bool down = button[1].checkHold();
        bool left = button[2].checkHold();
        bool right = button[3].checkHold();
        if(up && !(down && stopOnOppositePress))
        {
            y -= vel*dt;
            speedup = true;
        }
        else if(down && !(up && stopOnOppositePress))
        {
            y += vel*dt;
            speedup = true;
        }
        if(left && !(right && stopOnOppositePress))
        {
            x -= vel*dt;
            speedup = true;
        }
        else if(right && !(left && stopOnOppositePress))
        {
            x += vel*dt;
            speedup = true;
        }
    }
    else if(type == MOUSE && updateMouseByState)
    {
        // Select mouse index here...
        // No acceleration here.
        if(!useRelativeMouse)
        {
            SDL_GetMouseState(&x, &y);
        }
        else if(relativeOneForOne)
        {
            // This looks silly, but it is a little different than absolute positions.
            SDL_GetMouseState(&dx, &dy);
            dx = dx - x;
            dy = dy - y;
            x += dx;
            y += dy;
        }
        else
        {
            SDL_GetMouseState(&dx, &dy);
            dx = dx - x;
            dy = dy - y;

            if(dx != 0 || dy != 0)
            {
                float dir = atan2(dy, dx);
                x += cos(dir)*vel*dt;
                y += sin(dir)*vel*dt;
            }

            speedup = true;
        }
    }
    else if(type == TRACKBALL && updateTrackballByState && joystick < numjoysticks)
    {
        SDL_JoystickGetBall(joysticks[joystick], trackball, &dx, &dy);
        if(relativeOneForOne)
        {
            x += dx;
            y += dy;
        }
        else if(dx != 0 || dy != 0)
        {
            float dir = atan2(dy, dx);
            x += cos(dir)*vel*dt;
            y += sin(dir)*vel*dt;

            speedup = true;
        }
    }

    if(speedup)
    {
        vel += accel*dt;
        if(vel > maxVel)
        {
            vel = maxVel;
        }
    }
    else  // If you're not speeding up, you're stopping.
    {
        vel = minVel;
    }
}

void Cursor::update(const SDL_Event& event)  // Uses the mouse or ball
{
    bool speedup = false;
    if(type == MOUSE && !updateMouseByState)
    {
        if(event.type == SDL_MOUSEMOTION)
        {
            // Check mouse index...
            if(useRelativeMouse)
            {
                // Apply velocities somehow?
                dx = event.motion.xrel;
                dy = event.motion.yrel;
                if(relativeOneForOne)
                {
                    x += dx;
                    y += dy;
                }
                else
                {
                    if(dx != 0 || dy != 0)
                    {
                        float dir = atan2(dy, dx);
                        x += cos(dir)*vel*my_dt;
                        y += sin(dir)*vel*my_dt;
                    }

                    speedup = true;
                }
            }
            else
            {
                dx = event.motion.xrel;
                dy = event.motion.yrel;
                x = event.motion.x;
                y = event.motion.y;
            }
        }
    }
    else if(type == TRACKBALL && !updateTrackballByState)
    {
        if(event.type == SDL_JOYBALLMOTION)
        {
            if(event.jball.which == joystick && event.jball.ball == trackball)
            {
                dx = event.jball.xrel;
                dy = event.jball.yrel;
                if(relativeOneForOne)
                {
                    x += dx;
                    y += dy;
                }
                else if(dx != 0 || dy != 0)
                {
                    float dir = atan2(dy, dx);
                    x += cos(dir)*vel*my_dt;
                    y += sin(dir)*vel*my_dt;

                    speedup = true;
                }
            }
        }
    }

    if(speedup)
    {
        vel += accel*my_dt;
        if(vel > maxVel)
        {
            vel = maxVel;
        }
    }
    else  // If you're not speeding up, you're stopping.
    {
        vel = minVel;
    }
}



void loadDefaultController(Controller& controller)
{
    controller.buttons.clear();
    controller.buttons.push_back(Button(SDLK_UP));
    controller.buttons.push_back(Button(SDLK_DOWN));
    controller.buttons.push_back(Button(SDLK_LEFT));
    controller.buttons.push_back(Button(SDLK_RIGHT));
    controller.buttons.push_back(Button(SDLK_SPACE));
    controller.buttons.push_back(Button(SDLK_RETURN));
}

void loadDefaultController(Controller& controller, unsigned int joystick)
{
    controller.buttons.clear();
    controller.buttons.push_back(Button(Button::JOYAXIS_NEG, joystick, 1));
    controller.buttons.push_back(Button(Button::JOYAXIS_POS, joystick, 1));
    controller.buttons.push_back(Button(Button::JOYAXIS_NEG, joystick, 0));
    controller.buttons.push_back(Button(Button::JOYAXIS_POS, joystick, 0));
    controller.buttons.push_back(Button(Button::JOYBUTTON, joystick, 1));
    controller.buttons.push_back(Button(Button::JOYBUTTON, joystick, 0));
}



Controller::Controller()
{}
Controller::Controller(unsigned int joystick)
{}

unsigned int Controller::addButton(const Button& button)
{
    buttons.push_back(button);
    return buttons.size() - 1;
}

void Controller::setButton(unsigned int buttonIndex, const Button& button)
{
    while(buttons.size() <= buttonIndex)
        buttons.push_back(Button());
    buttons[buttonIndex] = button;
}

Button& Controller::getButton(unsigned int buttonIndex)
{
    if(buttonIndex >= buttons.size())
        throw std::logic_error("Tried to getButton() out of range.");
    return buttons[buttonIndex];
}

void Controller::deleteButtons()
{
    buttons.clear();
}

bool Controller::checkHold(unsigned int buttonIndex)
{
    if(buttonIndex >= buttons.size())
        throw std::logic_error("Tried to checkHold() out of range.");
    return buttons[buttonIndex].checkHold();
}


unsigned int Controller::checkInput(SDL_Event& event, bool checkDown)
{
    if(checkDown)
    {
        for(unsigned int i = 0; i < buttons.size(); i++)
        {
            if(buttons[i].checkDown(event))
                return i;
        }
    }
    else
    {
        for(unsigned int i = 0; i < buttons.size(); i++)
        {
            if(buttons[i].checkUp(event))
                return i;
        }
    }

    return NO_BUTTON;
}






} // END namespace SI

