#include "InputAndJoy.h"

// Data description
struct GamePadData {
    GamePadData() : joystick(nullptr), nbAxes(0), nbButtons(0) {};

    SDL_Joystick *joystick;
    int nbAxes;
    int nbButtons;

    std::vector<int> axeValue;
    std::vector<bool> buttonValue;
};

// InputAndJoy methods
InputAndJoy::InputAndJoy() {
    // JoyStick init
    if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) {
        std::cout << "InputAndJoy::InputAndJoy() > " << SDL_GetError() << std::endl;
    } else {
        // Opening joysticks
        SDL_JoystickEventState(SDL_ENABLE);
        SDL_Joystick* currentJoystick(NULL);
        int i(0);
        while (true)
        {
            // Is a new joystick available
            currentJoystick = SDL_JoystickOpen(i);
            
            if (currentJoystick == NULL)
                break;
            
            m_pad.push_back(new GamePadData);
            
            // Read data
            m_pad[i]->joystick = currentJoystick;
            m_pad[i]->nbAxes = SDL_JoystickNumAxes(currentJoystick);
            m_pad[i]->nbButtons = SDL_JoystickNumButtons(currentJoystick);
            
            for (int k(0); k < m_pad[i]->nbAxes; k++)
                m_pad[i]->axeValue.push_back(0);
            
            for (int k(0); k < m_pad[i]->nbButtons; k++)
                m_pad[i]->buttonValue.push_back(false);
            
            // Next one
            i++;
        }
    }
}

InputAndJoy::~InputAndJoy() {
    while (!m_pad.empty()) {
        SDL_JoystickClose(m_pad.back()->joystick);
        delete m_pad.back();
        m_pad.back() = nullptr;
        m_pad.pop_back();
    }
}

void InputAndJoy::updateEvents() {
    // Doubling code
    m_xRel = 0;
    m_yRel = 0;

    // Clear instant keys
    m_instantKeys.clear();

    // Super event loop
    while (SDL_PollEvent(&m_event)) {
        if (catchKeyBoardEvents(m_event))
            continue;
        else if (catchMouseEvents(m_event))
            continue;
        else if (catchWindowEvents(m_event))
            continue;
        else
            catchPadEvents(m_event);
    }

    // Keeping mouse in window
    if (m_relativeMouse)
        SDL_WarpMouseInWindow(m_window, m_windowHalfWidth, m_windowHalfHeight);
}

bool InputAndJoy::catchPadEvents(const SDL_Event &event) {
    switch (event.type) {
        case SDL_JOYAXISMOTION:
            m_pad[event.jaxis.which]->axeValue[event.jaxis.axis] = event.jaxis.value;
            return true;
        case SDL_JOYBUTTONDOWN:
            m_pad[event.jaxis.which]->buttonValue[event.jbutton.button] = true;
            return true;
        case SDL_JOYBUTTONUP:
            m_pad[event.jaxis.which]->buttonValue[event.jbutton.button] = false;
            return true;
        default:
            return false;
    }
}

int InputAndJoy::getAxeValue(const Uint8 axeID, Sint32 joyID) const {
    if (joyID >= m_pad.size()) {
        std::cout << "InputAndJoy::getAxeValue() > Game-pad " << joyID << " doesn't exist." << std::endl;
        return -1;
    }

    if (axeID < m_pad[joyID]->nbAxes)
        return m_pad[joyID]->axeValue[axeID];
    else
        std::cout << "InputAndJoy::getAxeValue() > Axe number " << axeID << " doesn't exist on pad ";
        std::cout << "number " << joyID << " named " << SDL_JoystickName(m_pad[joyID]->joystick) << "." << std::endl;
    return -1;
}

bool InputAndJoy::getButtonPad(const Uint8 button, Sint32 joyID) const {
    if (joyID >= m_pad.size()) {
        std::cout << "InputAndJoy::getButtonPad() > Game-pad " << joyID << " doesn't exist." << std::endl;
        return -1;
    }

    if (button < m_pad[joyID]->nbButtons)
        return m_pad[joyID]->buttonValue[button];
    else
        std::cout << "InputAndJoy::getButtonPad() > Button number " << button << " doesn't exist on pad ";
        std::cout << "number " << joyID << " named " << SDL_JoystickName(m_pad[joyID]->joystick) << "." << std::endl;
    return false;
}

unsigned long InputAndJoy::getNbPads() const {
    return m_pad.size();
}