/*Copyright (C) 2010-2020 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"

//////////////
///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(JOY_HAT_DOWN, true);
        
    if (sdlValue == SDL_HAT_UP || sdlValue == SDL_HAT_LEFTUP || sdlValue == SDL_HAT_RIGHTUP)
        setMove(JOY_HAT_UP, true);
        
    if (sdlValue == SDL_HAT_LEFT || sdlValue == SDL_HAT_LEFTUP || sdlValue == SDL_HAT_LEFTDOWN)
        setMove(JOY_HAT_LEFT, true);
        
    if (sdlValue == SDL_HAT_RIGHT || sdlValue == SDL_HAT_RIGHTUP || sdlValue == SDL_HAT_RIGHTDOWN)
        setMove(JOY_HAT_RIGHT, true);
}

void HatState::centerHat()
{
    m_state[JOY_HAT_UP] = false;
    m_state[JOY_HAT_DOWN] = false;
    m_state[JOY_HAT_LEFT] = false;
    m_state[JOY_HAT_RIGHT] = false;
}

bool HatState::centered() const
{
    std::map<JoyHatMove, bool>::const_iterator it;
    for (it = m_state.begin(); it != m_state.end(); it++)
    {
        if (it->second)
            return false; //At least one button is pressed.
    }
    
    return true;
}

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

bool HatState::state(const JoyHatMove &joyMove) 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() : AbstractJoystickKey()
{
    
}

JoyHatKey::JoyHatKey(const std::string &joystickName, int hatID, JoyHatMove hatMove) :
AbstractJoystickKey(), m_joystickName(joystickName), 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)
    {
        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;
        }
        
        HatState newState(event.jhat.value);
        return newState.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)
    {
        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 last step.
            HatState newState(event.jhat.value);
            return !newState.state(m_hatMove);
        }
    }
    
    return false;
}

std::string JoyHatKey::getJoystickName() const
{
    return m_joystickName;
}

std::string JoyHatKey::getName() const
{
    std::ostringstream code;
    code << "Hat " << m_hatID + 1;
    if (m_hatMove == JOY_HAT_UP) code << ": Up";
    else if (m_hatMove == JOY_HAT_DOWN) code << ": Down";
    else if (m_hatMove == JOY_HAT_LEFT) code << ": Left";
    else if (m_hatMove == JOY_HAT_RIGHT) code << ": Right";
    else code << ": Error";
    
    return code.str();
}

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

bool JoyHatKey::hatMoveFromSDLCode(Uint8 sdlValue, JoyHatMove &target)
{
    if (sdlValue == SDL_HAT_DOWN)
    {
        target = JOY_HAT_DOWN;
        return true;
    }
    else if (sdlValue == SDL_HAT_UP)
    {
        target = JOY_HAT_UP;
        return true;
    }
    else if (sdlValue == SDL_HAT_LEFT)
    {
        target = JOY_HAT_LEFT;
        return true;
    }
    else if (sdlValue == SDL_HAT_RIGHT)
    {
        target = JOY_HAT_RIGHT;
        return true;
    }
    
    return false;
}

JoyHatMove JoyHatKey::hatMoveFromStringCode(const std::string &stringCode)
{
    if (stringCode == "dd")
        return JOY_HAT_DOWN;
    else if (stringCode == "uu")
        return JOY_HAT_UP;
    else if (stringCode == "ll")
        return JOY_HAT_LEFT;
    else if (stringCode == "rr")
        return JOY_HAT_RIGHT;
    
    return JOY_HAT_LEFT;
}

std::string JoyHatKey::hatMoveStringCode(const JoyHatMove &code)
{
    if (code == JOY_HAT_DOWN)
        return "dd";
    else if (code == JOY_HAT_UP)
        return "uu";
    else if (code == JOY_HAT_LEFT)
        return "ll";
    else if (code == JOY_HAT_RIGHT)
        return "rr";
    
    return "ll";
}
