//
//  SuperTuxKart - a fun racing game with go-kart
//  Copyright (C) 2010-2015 Marianne Gagnon
//
//  This program 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.
//
//  This program 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 this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

#include "input/binding.hpp"
#include "io/xml_node.hpp"
#include "utils/string_utils.hpp"
#include "utils/translation.hpp"
#include "utils/log.hpp"

#include <SKeyMap.h>

/** Convert this binding to XML attributes. The full XML node is actually
 *  written by device_config, so we only have to add the attributes here.
 */
void Binding::save(std::ofstream& stream) const
{
    stream << "event=\"" << m_type << "\" ";
    stream << "id=\"" << m_id << "\" ";

    if (m_type == Input::IT_KEYBOARD)
    {
        stream << "character=\"" << m_character << "\" ";
    }

    // Only serialize the direction and the range for stick motions
    if (m_type == Input::IT_STICKMOTION)
    {
        stream << "direction=\"" << m_dir    << "\" ";
        stream << "range=\""     << m_range    << "\" ";
    }
}   // save

// ----------------------------------------------------------------------------
bool Binding::load(const XMLNode *action)
{
    int n;
    if(!action->get("id", &m_id) || !action->get("event", &n)  )
    {
        Log::warn("Binding", "No id-string or event-string given - ignored.");
        return false;
    }
    m_type = (Input::InputType)n;

    m_character = 0;
    // XMLNode only supports stringw, not wchar_t*
    core::stringw s;
    action->get("character", &s);
    if(s.size()>0)
        m_character = s[0];

    // If the action is not a stick motion (button or key)
    if (m_type == Input::IT_STICKMOTION)
    {
        if(!action->get("direction", &n))
        {
            Log::warn("Binding", "IT_STICKMOTION without direction, ignoring.");
            return false;
        }
        m_dir = (Input::AxisDirection)n;

        if(!action->get("range", &n)) m_range = Input::AR_HALF;
        else                          m_range = (Input::AxisRange)n;

    }   // if m_type!=stickmotion
    return true;
}   // load

// ----------------------------------------------------------------------------
/** Returns a string representing this binding, which can be displayed on the
 *  screen.
 */
irr::core::stringw Binding::getAsString() const
{
    irr::core::stringw s;
    switch (m_type)
    {
        case Input::IT_NONE:
            //I18N: Unbound key binding
            s = _("[none]");
            break;
        case Input::IT_KEYBOARD:
            s = "?";
            if(m_character)
                s[0]=m_character;

            switch(m_id)
            {
#if defined(WIN32) || (defined(__linux__) && !defined(ANDROID))
            // Windows codes certain special keys, which have atm no defined
            // value in irr::IRR_KEY_*. So for now hardcode the values.
            // FIXME: what happens with international keyboards? E.g. A [
            // might be an Umlaut on a German keyboard. How do we get
            // the character to display in this case??
            case 186: s=";";  break;
            case 191: s="/";  break;
            case 192: s="`";  break;
            case 219: s="[";  break;
            case 220: s="\\"; break;
            case 221: s="]";  break;
            case 222: s="'";  break;
#endif
             //I18N: input configuration screen: mouse button
            case irr::IRR_KEY_LBUTTON    : s = _C("input_key", "Left Mouse Button");  break;
             //I18N: input configuration screen: mouse button
            case irr::IRR_KEY_RBUTTON    : s = _C("input_key", "Right Mouse Button"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_CANCEL     : s = _C("input_key", "Cancel"); break;
             //I18N: input configuration screen: mouse button
            case irr::IRR_KEY_MBUTTON    : s = _C("input_key", "Middle Mouse Button"); break;
             //I18N: input configuration screen: mouse button
            case irr::IRR_KEY_XBUTTON1   : s = _C("input_key", "X1 Mouse Button"); break;
             //I18N: input configuration screen: mouse button
            case irr::IRR_KEY_XBUTTON2   : s = _C("input_key", "X2 Mouse Button"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_BACK       : s = _C("input_key", "Backspace"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_TAB        : s = _C("input_key", "Tab"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_CLEAR      : s = _C("input_key", "Clear"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_RETURN     : s = _C("input_key", "Return"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_SHIFT      : s = _C("input_key", "Shift"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_CONTROL    : s = _C("input_key", "Control"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_MENU       : s = _C("input_key", "Alt/Menu"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_PAUSE      : s = _C("input_key", "Pause"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_CAPITAL    : s = _C("input_key", "Caps Lock"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_KANA       : s = _C("input_key", "Kana"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_JUNJA      : s = _C("input_key", "Junja"); break;
             //I18N: input configuration screen: keyboard key
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_FINAL      : s = _C("input_key", "Final"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_ESCAPE     : s = _C("input_key", "Escape"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_CONVERT    : s = _C("input_key", "Convert"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NONCONVERT : s = _C("input_key", "Nonconvert"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_ACCEPT     : s = _C("input_key", "Accept"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_MODECHANGE : s = _C("input_key", "Modechange"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_SPACE      : s = _C("input_key", "Space"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_PRIOR      : s = _C("input_key", "Page Up"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NEXT       : s = _C("input_key", "Page Down"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_END        : s = _C("input_key", "End"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_HOME       : s = _C("input_key", "Home"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_LEFT       : s = _C("input_key", "Left"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_UP         : s = _C("input_key", "Up"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_RIGHT      : s = _C("input_key", "Right"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_DOWN       : s = _C("input_key", "Down"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_SELECT     : s = _C("input_key", "Select"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_PRINT      : s = _C("input_key", "Print"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_EXECUT     : s = _C("input_key", "Exec"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_SNAPSHOT   : s = _C("input_key", "Print Screen"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_INSERT     : s = _C("input_key", "Insert"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_DELETE     : s = _C("input_key", "Delete"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_HELP       : s = _C("input_key", "Help"); break;
            case irr::IRR_KEY_0          : s = "0"; break;
            case irr::IRR_KEY_1          : s = "1"; break;
            case irr::IRR_KEY_2          : s = "2"; break;
            case irr::IRR_KEY_3          : s = "3"; break;
            case irr::IRR_KEY_4          : s = "4"; break;
            case irr::IRR_KEY_5          : s = "5"; break;
            case irr::IRR_KEY_6          : s = "6"; break;
            case irr::IRR_KEY_7          : s = "7"; break;
            case irr::IRR_KEY_8          : s = "8"; break;
            case irr::IRR_KEY_9          : s = "9"; break;
            case irr::IRR_KEY_A          : s = "A"; break;
            case irr::IRR_KEY_B          : s = "B"; break;
            case irr::IRR_KEY_C          : s = "C"; break;
            case irr::IRR_KEY_D          : s = "D"; break;
            case irr::IRR_KEY_E          : s = "E"; break;
            case irr::IRR_KEY_F          : s = "F"; break;
            case irr::IRR_KEY_G          : s = "G"; break;
            case irr::IRR_KEY_H          : s = "H"; break;
            case irr::IRR_KEY_I          : s = "I"; break;
            case irr::IRR_KEY_J          : s = "J"; break;
            case irr::IRR_KEY_K          : s = "K"; break;
            case irr::IRR_KEY_L          : s = "L"; break;
            case irr::IRR_KEY_M          : s = "M"; break;
            case irr::IRR_KEY_N          : s = "N"; break;
            case irr::IRR_KEY_O          : s = "O"; break;
            case irr::IRR_KEY_P          : s = "P"; break;
            case irr::IRR_KEY_Q          : s = "Q"; break;
            case irr::IRR_KEY_R          : s = "R"; break;
            case irr::IRR_KEY_S          : s = "S"; break;
            case irr::IRR_KEY_T          : s = "T"; break;
            case irr::IRR_KEY_U          : s = "U"; break;
            case irr::IRR_KEY_V          : s = "V"; break;
            case irr::IRR_KEY_W          : s = "W"; break;
            case irr::IRR_KEY_X          : s = "X"; break;
            case irr::IRR_KEY_Y          : s = "Y"; break;
            case irr::IRR_KEY_Z          : s = "Z"; break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_LWIN       : s = _C("input_key", "Left Logo"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_RWIN       : s = _C("input_key", "Right Logo"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_APPS       : s = _C("input_key", "Apps"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_SLEEP      : s = _C("input_key", "Sleep"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD0    : s = _C("input_key", "Numpad 0"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD1    : s = _C("input_key", "Numpad 1"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD2    : s = _C("input_key", "Numpad 2"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD3    : s = _C("input_key", "Numpad 3"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD4    : s = _C("input_key", "Numpad 4"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD5    : s = _C("input_key", "Numpad 5"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD6    : s = _C("input_key", "Numpad 6"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD7    : s = _C("input_key", "Numpad 7"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD8    : s = _C("input_key", "Numpad 8"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMPAD9    : s = _C("input_key", "Numpad 9"); break;
            case irr::IRR_KEY_MULTIPLY   : s = "*"; break;
            case irr::IRR_KEY_ADD        : s = "+"; break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_SEPARATOR  : s = _C("input_key", "Separator"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_SUBTRACT   : s = _C("input_key", "- (Subtract)"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_DECIMAL    : s = _C("input_key", "Decimal"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_DIVIDE     : s = _C("input_key", "/ (Divide)"); break;
            case irr::IRR_KEY_F1         : s = "F1"; break;
            case irr::IRR_KEY_F2         : s = "F2"; break;
            case irr::IRR_KEY_F3         : s = "F3"; break;
            case irr::IRR_KEY_F4         : s = "F4"; break;
            case irr::IRR_KEY_F5         : s = "F5"; break;
            case irr::IRR_KEY_F6         : s = "F6"; break;
            case irr::IRR_KEY_F7         : s = "F7"; break;
            case irr::IRR_KEY_F8         : s = "F8"; break;
            case irr::IRR_KEY_F9         : s = "F9"; break;
            case irr::IRR_KEY_F10        : s = "F10"; break;
            case irr::IRR_KEY_F11        : s = "F11"; break;
            case irr::IRR_KEY_F12        : s = "F12"; break;
            case irr::IRR_KEY_F13        : s = "F13"; break;
            case irr::IRR_KEY_F14        : s = "F14"; break;
            case irr::IRR_KEY_F15        : s = "F15"; break;
            case irr::IRR_KEY_F16        : s = "F16"; break;
            case irr::IRR_KEY_F17        : s = "F17"; break;
            case irr::IRR_KEY_F18        : s = "F18"; break;
            case irr::IRR_KEY_F19        : s = "F19"; break;
            case irr::IRR_KEY_F20        : s = "F20"; break;
            case irr::IRR_KEY_F21        : s = "F21"; break;
            case irr::IRR_KEY_F22        : s = "F22"; break;
            case irr::IRR_KEY_F23        : s = "F23"; break;
            case irr::IRR_KEY_F24        : s = "F24"; break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_NUMLOCK    : s = _C("input_key", "Num Lock"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_SCROLL     : s = _C("input_key", "Scroll Lock"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_LSHIFT     : s = _C("input_key", "Left Shift"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_RSHIFT     : s = _C("input_key", "Right Shift"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_LCONTROL   : s = _C("input_key", "Left Control"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_RCONTROL   : s = _C("input_key", "Right Control"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_LMENU      : s = _C("input_key", "Left Menu"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_RMENU      : s = _C("input_key", "Right Menu"); break;
            case irr::IRR_KEY_PLUS       : s = "+"; break;
            case irr::IRR_KEY_COMMA      : s = ","; break;
            case irr::IRR_KEY_MINUS      : s = "-"; break;
            case irr::IRR_KEY_PERIOD     : s = "."; break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_ATTN       : s = _C("input_key", "Attn"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_CRSEL      : s = _C("input_key", "Crsel"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_EXSEL      : s = _C("input_key", "Exsel"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_EREOF      : s = _C("input_key", "Ereof"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_PLAY       : s = _C("input_key", "Play"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_ZOOM       : s = _C("input_key", "Zoom"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_PA1        : s = _C("input_key", "Pa1"); break;
             //I18N: input configuration screen: keyboard key
            case irr::IRR_KEY_OEM_CLEAR  : s = _C("input_key", "Oem Clear"); break;
        }

            break;
        case Input::IT_STICKMOTION:

            if (m_id == Input::HAT_H_ID)
            {
                //I18N: to appear in input configuration screen, for gamepad hats
                s = _("Gamepad hat %d", (m_dir == Input::AD_NEGATIVE ? L"0-" : L"0+"));
            }
            else if (m_id == Input::HAT_V_ID)
            {
                //I18N: to appear in input configuration screen, for gamepad hats
                s = _("Gamepad hat %d", (m_dir == Input::AD_NEGATIVE ? L"1-" : L"1+"));
            }
            else
            {
                if (m_range == Input::AR_HALF)
                {
                    //I18N: to appear in input configuration screen, for gamepad axes
                    s = _("Axis %d %s", m_id, (m_dir == Input::AD_NEGATIVE) ? L"-" : L"+");
                }
                else
                {
                    if(m_dir == Input::AD_NEGATIVE)
                    {
                        //I18N: to appear in input configuration screen, for gamepad axes
                        s = _("Axis %d inverted", m_id);
                    }
                    else
                    {
                        //I18N: to appear in input configuration screen, for gamepad axes
                        s = _("Axis %d", m_id);
                    }
                }

            }
            break;
        case Input::IT_STICKBUTTON:
            //I18N: to appear in input configuration screen, for gamepad buttons
            s = ( _("Gamepad button %d", m_id+1));
            break;        case Input::IT_MOUSEBUTTON:
            //I18N: to appear in input configuration screen, for mouse (might not be used at all)
            s = _("Mouse button %d", (m_id+1));
            break;
        case Input::IT_MOUSEMOTION: // FIXME : I don't reckon this is used at all
            //I18N: to appear in input configuration screen, for mouse (might not be used at all)
            s = _("Mouse axis %d %s", (m_id+1), (m_dir == Input::AD_NEGATIVE) ? '-': '+');
            break;
        default:
            s = "?";
    }

    return s;
}   // GetKeyAsString