#include <KeyActionMap/KeyActionMap.hpp>
#include <sstream>
#include <Utilities/Utilities.hpp>

KeyActionMap::KeyActionMap()
{
    #define DECL_INPUT_ACTION(name, displayName, defaultScancode)\
        setActionScancode(#name, defaultScancode);\
        defaultMappings[#name] = defaultScancode;
    #include <InputAction/InputActionList.hpp>
}

void KeyActionMap::setActionScancode(const std::string &action, SDL_Scancode scancode)
{
    if (actionToScancode.contains(action))
    {
        scancodeToAction.erase(actionToScancode[action]);
    }

    if (scancodeToAction.contains(scancode))
    {
        actionToScancode.erase(scancodeToAction[scancode]);
    }

    actionToScancode[action] = scancode;
    scancodeToAction[scancode] = action;
}

std::optional<SDL_Scancode> KeyActionMap::getScancodeForAction(const std::string &action)
{
    auto it = actionToScancode.find(action);
    if (it == actionToScancode.end())
    {
        return std::nullopt;
    }

    return it->second;
}

std::optional<std::string> KeyActionMap::getActionForScancode(SDL_Scancode scancode)
{
    auto it = scancodeToAction.find(scancode);
    if (it == scancodeToAction.end())
    {
        return std::nullopt;
    }

    return it->second;
}

void KeyActionMap::deserialize(const std::string &str)
{
    std::istringstream stream(str);
    std::string pair;

    while (std::getline(stream, pair, ';'))
    {
        if (pair.empty())
        {
            continue;
        }

        auto colonPos = pair.find(':');
        if (colonPos == std::string::npos)
        {
            continue;
        }

        std::string action = trim(pair.substr(0, colonPos));
        std::string scancodeStr = trim(pair.substr(colonPos + 1));

        if (action.empty() || scancodeStr.empty())
        {
            continue;
        }

        try
        {
            SDL_Scancode scancode = static_cast<SDL_Scancode>(std::stoi(scancodeStr));
            setActionScancode(action, scancode);
        }
        catch (const std::exception &)
        {
            continue;
        }
    }
}

void KeyActionMap::resetActionMapping(const std::string &action)
{
    setActionScancode(action, defaultMappings[action]);
}

std::ostream& operator<<(std::ostream &os, const KeyActionMap &map)
{
    for (const auto &[action, scancode] : map.actionToScancode)
    {
        os << "\\\n    " << action << ":" << scancode << ";";
    }

    return os;
}