/*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 "JoyHatKey.hpp"

#include "Tools/Format.hpp"
#include "Tools/I18n.hpp" ///< _ macro for i18n
#include <sstream>

//////////////
///HatState///
//////////////
HatState::HatState()
{
    centerHat();
}

HatState::HatState(Uint8 sdlValue)
{
    fromSDLCode(sdlValue);
}

void HatState::fromSDLCode(Uint8 sdlValue)
{
    centerHat();

    if (sdlValue == SDL_HAT_DOWN || sdlValue == SDL_HAT_LEFTDOWN ||
        sdlValue == SDL_HAT_RIGHTDOWN)
        setMove(JoyHatMove::Down, true);

    if (sdlValue == SDL_HAT_UP || sdlValue == SDL_HAT_LEFTUP ||
        sdlValue == SDL_HAT_RIGHTUP)
        setMove(JoyHatMove::Up, true);

    if (sdlValue == SDL_HAT_LEFT || sdlValue == SDL_HAT_LEFTUP ||
        sdlValue == SDL_HAT_LEFTDOWN)
        setMove(JoyHatMove::Left, true);

    if (sdlValue == SDL_HAT_RIGHT || sdlValue == SDL_HAT_RIGHTUP ||
        sdlValue == SDL_HAT_RIGHTDOWN)
        setMove(JoyHatMove::Right, true);
}

void HatState::centerHat()
{
    m_state[JoyHatMove::Up] = false;
    m_state[JoyHatMove::Down] = false;
    m_state[JoyHatMove::Left] = false;
    m_state[JoyHatMove::Right] = false;
}

void HatState::setMove(const JoyHatMove &joyMove, bool value)
{
    m_state[joyMove] = value;
}

bool HatState::state(const JoyHatMove &joyMove) const
{
    const std::map<JoyHatMove, bool>::const_iterator it = m_state.find(joyMove);
    if (it != m_state.end())
        return it->second;

    return false;
}

///////////////
///JoyHatKey///
///////////////
JoyHatKey::JoyHatKey() : JoyHatKey(0, JoyHatMove::Up) {}

JoyHatKey::JoyHatKey(Uint8 hatID, JoyHatMove hatMove) :
m_hatID(hatID), m_hatMove(hatMove)
{

}

bool JoyHatKey::keyPressed(const SDL_Event &event, int deviceID,
    const std::map<Uint8, HatState> &previousHatStates) const
{
    if (event.type == SDL_JOYHATMOTION && deviceID == event.jhat.which &&
        m_hatID == event.jhat.hat)
    {
        const std::map<Uint8, HatState>::const_iterator it =
            previousHatStates.find(m_hatID);
        if (it != previousHatStates.end())
        {
            //The key was already pressed before.
            if (it->second.state(m_hatMove))
                return false;
        }

        return HatState(event.jhat.value).state(m_hatMove);
    }

    return false;
}

bool JoyHatKey::keyReleased(const SDL_Event &event, int deviceID,
    const std::map<Uint8, HatState> &previousHatStates) const
{
    if (event.type == SDL_JOYHATMOTION && deviceID == event.jhat.which &&
        m_hatID == event.jhat.hat)
    {
        const std::map<Uint8, HatState>::const_iterator it =
            previousHatStates.find(m_hatID);
        if (it != previousHatStates.end())
        {
            //The key was not pressed before
            if (!it->second.state(m_hatMove))
                return false;

            //Starting from this point, the key was pressed at the last step
            return !HatState(event.jhat.value).state(m_hatMove);
        }
    }

    return false;
}

std::string JoyHatKey::getName() const
{
    const int hatNumber = m_hatID + 1;

    if (m_hatMove == JoyHatMove::Up)
        return _f(KeyJoyHatUp, std::make_pair("hat_id", hatNumber));
    else if (m_hatMove == JoyHatMove::Down)
        return _f(KeyJoyHatDown, std::make_pair("hat_id", hatNumber));
    else if (m_hatMove == JoyHatMove::Left)
        return _f(KeyJoyHatLeft, std::make_pair("hat_id", hatNumber));
    else if (m_hatMove == JoyHatMove::Right)
        return _f(KeyJoyHatRight, std::make_pair("hat_id", hatNumber));

    return "?";
}

std::string JoyHatKey::getKeyCode() const
{
    std::ostringstream code;
    code << "jh" << static_cast<short>(m_hatID) << hatMoveStringCode(m_hatMove);
    return code.str();
}

bool JoyHatKey::hatMoveFromSDLCode(Uint8 sdlValue, JoyHatMove &target)
{
    if (sdlValue == SDL_HAT_DOWN)
    {
        target = JoyHatMove::Down;
        return true;
    }
    else if (sdlValue == SDL_HAT_UP)
    {
        target = JoyHatMove::Up;
        return true;
    }
    else if (sdlValue == SDL_HAT_LEFT)
    {
        target = JoyHatMove::Left;
        return true;
    }
    else if (sdlValue == SDL_HAT_RIGHT)
    {
        target = JoyHatMove::Right;
        return true;
    }

    return false;
}

JoyHatMove JoyHatKey::hatMoveFromStringCode(const std::string &stringCode)
{
    if (stringCode == "dd")
        return JoyHatMove::Down;
    else if (stringCode == "uu")
        return JoyHatMove::Up;
    else if (stringCode == "ll")
        return JoyHatMove::Left;
    else if (stringCode == "rr")
        return JoyHatMove::Right;

    return JoyHatMove::Left;
}

std::string JoyHatKey::hatMoveStringCode(const JoyHatMove &code)
{
    if (code == JoyHatMove::Down)
        return "dd";
    else if (code == JoyHatMove::Up)
        return "uu";
    else if (code == JoyHatMove::Left)
        return "ll";
    else if (code == JoyHatMove::Right)
        return "rr";

    return "ll";
}
