/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu 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 for more details.

You should have received a copy of the GNU General Public License
along with Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Key.hpp"

#include <sstream>

Key::Key() : m_type(Key::Type::Undefined) {}

Key::Key(const KeyboardKey &keyboardKey) :
m_type(Key::Type::Keyboard), m_keyboardKey(keyboardKey)
{

}

Key::Key(const JoyButtonKey &joyButtonKey) :
m_type(Key::Type::JoyButton), m_joyButtonKey(joyButtonKey)
{

}

Key::Key(const JoyHatKey &joyHatKey) :
m_type(Key::Type::JoyHat), m_joyHatKey(joyHatKey)
{

}

Key::Key(const JoyAxisKey &joyAxisKey) :
m_type(Key::Type::JoyAxis), m_joyAxisKey(joyAxisKey)
{

}

bool Key::buttonPressed(const SDL_Event &event, int deviceID) const
{
    if (m_type == Key::Type::Keyboard && deviceID == DEVICE_KEYBOARD)
        return m_keyboardKey.keyPressed(event);
    else if (m_type == Key::Type::JoyButton)
        return m_joyButtonKey.keyPressed(event, deviceID);

    return false;
}

bool Key::hatPressed(const SDL_Event &event, int deviceID,
    const std::map<Uint8, HatState> &previousHatStates) const
{
    if (m_type == Key::Type::JoyHat)
        return m_joyHatKey.keyPressed(event, deviceID, previousHatStates);

    return false;
}

bool Key::axisPressed(const SDL_Event &event, Uint8 deviceID,
    const std::map<Uint8, AxisState> &previousAxisStates) const
{
    if (m_type == Key::Type::JoyAxis)
        return m_joyAxisKey.keyPressed(event, deviceID, previousAxisStates);

    return false;
}

bool Key::buttonReleased(const SDL_Event &event, int deviceID) const
{
    if (m_type == Key::Type::Keyboard && deviceID == DEVICE_KEYBOARD)
        return m_keyboardKey.keyReleased(event);
    else if (m_type == Key::Type::JoyButton)
        return m_joyButtonKey.keyReleased(event, deviceID);

    return false;
}

bool Key::hatReleased(const SDL_Event &event, int deviceID,
    const std::map<Uint8, HatState> &previousHatStates) const
{
    if (m_type == Key::Type::JoyHat)
        return m_joyHatKey.keyReleased(event, deviceID, previousHatStates);

    return false;
}

bool Key::axisReleased(const SDL_Event &event, Uint8 deviceID,
    const std::map<Uint8, AxisState> &previousAxisStates) const
{
    if (m_type == Key::Type::JoyAxis)
        return m_joyAxisKey.keyReleased(event, deviceID, previousAxisStates);

    return false;
}

bool Key::isFromKeyboard() const
{
    return m_type == Key::Type::Keyboard;
}

bool Key::isFromJoystick() const
{
    return m_type == Key::Type::JoyButton || m_type == Key::Type::JoyHat ||
           m_type == Key::Type::JoyAxis;
}

bool Key::isUndefined() const
{
    return m_type == Key::Type::Undefined;
}

Key::Type Key::getType() const
{
    return m_type;
}

std::string Key::getName() const
{
    if (m_type == Key::Type::Keyboard)
        return m_keyboardKey.getName();
    else if (m_type == Key::Type::JoyButton)
        return m_joyButtonKey.getName();
    else if (m_type == Key::Type::JoyHat)
        return m_joyHatKey.getName();
    else if (m_type == Key::Type::JoyAxis)
        return m_joyAxisKey.getName();

    return "?";
}

std::string Key::getKeyCode() const
{
    if (m_type == Key::Type::Keyboard)
        return m_keyboardKey.getKeyCode();
    else if (m_type == Key::Type::JoyButton)
        return m_joyButtonKey.getKeyCode();
    else if (m_type == Key::Type::JoyHat)
        return m_joyHatKey.getKeyCode();
    else if (m_type == Key::Type::JoyAxis)
        return m_joyAxisKey.getKeyCode();

    return "undef";
}

Key Key::fromKeyCode(const std::string &code)
{
    if (code == "undef" || code.size() < 2)
        return Key();
    else
    {
        std::string qualifier = code.substr(0,2);
        std::string codeStr = code.substr(2);

        short keyCode;
        std::istringstream convertToShort(codeStr);
        convertToShort >> keyCode;

        if (convertToShort.fail())
            return Key();

        if (qualifier == "kb")
        {
            return KeyboardKey(static_cast<SDL_Scancode>(keyCode));
        }
        else if (qualifier == "jb")
        {
            return JoyButtonKey(static_cast<Uint8>(keyCode));
        }
        else if (qualifier == "jh")
        {
            short hatID;
            std::istringstream convertHatID(
                codeStr.substr(0, codeStr.size() - 2));
            convertHatID >> hatID;

            if (convertHatID.fail())
                return Key();

            std::string hatMoveCode = codeStr.substr(codeStr.size() - 2);
            return JoyHatKey(static_cast<Uint8>(hatID),
                             JoyHatKey::hatMoveFromStringCode(hatMoveCode));
        }
        else if (qualifier == "ja")
        {
            short axisID;
            std::istringstream convertAxisID(
                codeStr.substr(0, codeStr.size() - 1));
            convertAxisID >> axisID;

            if (convertAxisID.fail())
                return Key();

            std::string axisMoveCode = codeStr.substr(codeStr.size() - 1);
            return JoyAxisKey(static_cast<Uint8>(axisID),
                              JoyAxisKey::axisMoveFromStringCode(axisMoveCode));
        }
    }

    return Key();
}

bool Key::pressedFromEvent(const SDL_Event &event, Key &dstKey,
                           int &dstDeviceID)
{
    if (event.type == SDL_KEYDOWN)
    {
        dstKey = KeyboardKey(event.key.keysym.scancode);
        dstDeviceID = DEVICE_KEYBOARD;
        return true;
    }
    else if (event.type == SDL_JOYBUTTONDOWN)
    {
        dstKey = JoyButtonKey(event.jbutton.button);
        dstDeviceID = event.jbutton.which;
        return true;
    }
    else if (event.type == SDL_JOYHATMOTION)
    {
        JoyHatMove hatMoveValue;
        if (JoyHatKey::hatMoveFromSDLCode(event.jhat.value, hatMoveValue))
        {
            dstKey = JoyHatKey(event.jhat.hat, hatMoveValue);
            dstDeviceID = event.jhat.which;
            return true;
        }
    }
    else if (event.type == SDL_JOYAXISMOTION)
    {
        const AxisState axisStateValue =
            JoyAxisKey::axisStateFromSDLCode(event.jaxis.value);

        if (axisStateValue == AxisState::Centered)
            return false;
        else
        {
            dstKey = JoyAxisKey(event.jaxis.axis,
                                axisStateValue == AxisState::Negative ?
                                JoyAxisMove::Negative : JoyAxisMove::Positive);
            dstDeviceID = event.jaxis.which;
            return true;
        }
    }

    return false;
}
