#include "keymapper.h"

#include <imgui/imgui.h>
#include "engine.h"
#include "SparrowInput/input.h"
#include "defaultkeysmap.h"
#include <iostream>
#include <fstream>
#include <cereal/cereal.hpp>
#include <cereal/archives/json.hpp>

KeyMapper::KeyMapper() :
    m_enabled(false),
    m_keymap_file("keymap.json"),
    m_waiting_for_input(-1)
{
    // mapping between action numerical value and text value
    m_actions_list[DefaultKeysMap::MAIN_ACTION]      = "Main Action";
    m_actions_list[DefaultKeysMap::SECONDARY_ACTION] = "Secondary Action";
    m_actions_list[DefaultKeysMap::TERTIARY_ACTION]  = "Tertiary Action";
    m_actions_list[DefaultKeysMap::MOVE_FORWARD]      = "Move Forward";
    m_actions_list[DefaultKeysMap::MOVE_BACKWARD] = "Move Backward";
    m_actions_list[DefaultKeysMap::STRAFE_LEFT] = "Strafe Left";
    m_actions_list[DefaultKeysMap::STRAFE_RIGHT] = "Strafe Right";
    m_actions_list[DefaultKeysMap::RUN] = "Run";
    m_actions_list[DefaultKeysMap::MAIN_ACTION_HOLD] = "Main Action Hold";
    m_actions_list[DefaultKeysMap::SECONDARY_ACTION_HOLD] = "Secondary Action Hold";
    m_actions_list[DefaultKeysMap::JUMP] = "Jump";
    m_actions_list[DefaultKeysMap::TOGGLE_NOCLIP] = "Toggle NoClip";
    m_actions_list[DefaultKeysMap::TOGGLE_PHYSICS_DEBUG] = "Toggle Physics Debug";
    m_actions_list[DefaultKeysMap::TOGGLE_CONSOLE] = "Toggle Console";
    m_actions_list[DefaultKeysMap::MOVE_CURSOR_LEFT] = "Move Cursor Left";
    m_actions_list[DefaultKeysMap::MOVE_CURSOR_RIGHT] = "Move Cursor Right";
    m_actions_list[DefaultKeysMap::HISTORY_UP] = "History Up";
    m_actions_list[DefaultKeysMap::HISTORY_DOWN] = "History Down";
    m_actions_list[DefaultKeysMap::TOGGLE_MOUSE_CURSOR] = "Toggle Mouse Cursor";
    m_actions_list[DefaultKeysMap::EXIT_GAME] = "Exit Game";
    m_actions_list[DefaultKeysMap::LEFT_CLICK] = "Left Click";


    // mapping between mouse numerical value and text value
    m_mouse_buttons[sf::Mouse::Left]     = "Mouse Left";
    m_mouse_buttons[sf::Mouse::Middle]   = "Mouse Middle";
    m_mouse_buttons[sf::Mouse::Right]    = "Mouse Right";
    m_mouse_buttons[sf::Mouse::XButton1] = "Mouse Extra 1";
    m_mouse_buttons[sf::Mouse::XButton2] = "Mouse Extra 2";

    // mapping between keyboard numerical value and text value
    m_keyboard_keys[sf::Keyboard::A]     = "A";
    m_keyboard_keys[sf::Keyboard::Z]     = "Z";
    m_keyboard_keys[sf::Keyboard::E]     = "E";
    m_keyboard_keys[sf::Keyboard::R]     = "R";
    m_keyboard_keys[sf::Keyboard::T]     = "T";
    m_keyboard_keys[sf::Keyboard::Y]     = "Y";
    m_keyboard_keys[sf::Keyboard::U]     = "U";
    m_keyboard_keys[sf::Keyboard::I]     = "I";
    m_keyboard_keys[sf::Keyboard::O]     = "O";
    m_keyboard_keys[sf::Keyboard::P]     = "P";

    m_keyboard_keys[sf::Keyboard::Q]     = "Q";
    m_keyboard_keys[sf::Keyboard::S]     = "S";
    m_keyboard_keys[sf::Keyboard::D]     = "D";
    m_keyboard_keys[sf::Keyboard::F]     = "F";
    m_keyboard_keys[sf::Keyboard::G]     = "G";
    m_keyboard_keys[sf::Keyboard::H]     = "H";
    m_keyboard_keys[sf::Keyboard::J]     = "J";
    m_keyboard_keys[sf::Keyboard::K]     = "K";
    m_keyboard_keys[sf::Keyboard::L]     = "L";
    m_keyboard_keys[sf::Keyboard::M]     = "M";

    m_keyboard_keys[sf::Keyboard::W]     = "W";
    m_keyboard_keys[sf::Keyboard::X]     = "X";
    m_keyboard_keys[sf::Keyboard::C]     = "C";
    m_keyboard_keys[sf::Keyboard::V]     = "V";
    m_keyboard_keys[sf::Keyboard::B]     = "B";
    m_keyboard_keys[sf::Keyboard::N]     = "N";

    m_keyboard_keys[sf::Keyboard::F1]    = "F1";
    m_keyboard_keys[sf::Keyboard::F2]    = "F2";
    m_keyboard_keys[sf::Keyboard::F3]    = "F3";
    m_keyboard_keys[sf::Keyboard::F4]    = "F4";
    m_keyboard_keys[sf::Keyboard::F5]    = "F5";
    m_keyboard_keys[sf::Keyboard::F6]    = "F6";
    m_keyboard_keys[sf::Keyboard::F7]    = "F7";
    m_keyboard_keys[sf::Keyboard::F8]    = "F8";
    m_keyboard_keys[sf::Keyboard::F9]    = "F9";
    m_keyboard_keys[sf::Keyboard::F10]   = "F10";
    m_keyboard_keys[sf::Keyboard::F11]   = "F11";
    m_keyboard_keys[sf::Keyboard::F12]   = "F12";

    m_keyboard_keys[sf::Keyboard::Left]  = "Arrow Left";
    m_keyboard_keys[sf::Keyboard::Right] = "Arrow Right";
    m_keyboard_keys[sf::Keyboard::Up]    = "Arrow Up";
    m_keyboard_keys[sf::Keyboard::Down]  = "Arrow Down";


    m_keyboard_keys[sf::Keyboard::Escape]= "Escape";
    m_keyboard_keys[sf::Keyboard::Space] = "Space";
    m_keyboard_keys[sf::Keyboard::LShift]= "Left Shift";
    m_keyboard_keys[sf::Keyboard::LControl]  = "Left Control";

}

void KeyMapper::update()
{
    if(m_enabled)
        gui();
}

void KeyMapper::gui()
{
    std::vector<std::string> contexts = getEngine().getInput()->getContextsList();
    std::vector<std::string> types_binding = {"pressed","released","hold"};

    ImGui::Begin("KeyMapper");
    std::vector<Binding>& keys = m_keysmap.data();
    for(unsigned int i = 0; i < m_keysmap.data().size() ;i++)
    {
        Binding& b = keys[i];
        std::string action_label;
        if(m_actions_list.count(b.action.action))
            action_label = m_actions_list[b.action.action];
        else
            action_label = "Unknown Action";

        std::string key_label;
        if (b.action.source == input::MOUSE && m_mouse_buttons.count(b.key))
            key_label = m_mouse_buttons[b.key];
        else if(b.action.source == input::KEYBOARD && m_keyboard_keys.count(b.key))
            key_label = m_keyboard_keys[b.key];
        else
            key_label = "UNDEFINED";

        ImGui::Text("%s",action_label.data());
        ImGui::SameLine(200);
        ImGui::PushID(action_label.data());
        if(m_waiting_for_input == int(i))
            ImGui::Button("...",ImVec2(100,30));
        else
        {
            if(ImGui::Button(key_label.data(),ImVec2(100,30)))
                m_waiting_for_input = i;
        }
        ImGui::SameLine(400);
        ImGui::PushItemWidth(100);
        if(ImGui::BeginCombo("Type :",types_binding[b.type].data()))
        {
            for(unsigned int j = 0;j < types_binding.size();j++)
            {
                bool is_selected = (b.type == int(j));
                if(ImGui::Selectable(types_binding[j].data(),is_selected))
                    b.type = j;
                if(is_selected)
                    ImGui::SetItemDefaultFocus();
            }
            ImGui::EndCombo();
        }
        ImGui::PopID();
        ImGui::PopItemWidth();
        ImGui::Separator();
    }

    /*if(ImGui::Button("Test 1"))
        std::cout << "Test 1" << std::endl;

    if(ImGui::Button("Test 1"))
        std::cout << "Test 2" << std::endl;*/

    if(m_waiting_for_input != -1)
    {
        ImGuiIO io = ImGui::GetIO();
        for(int i = sf::Keyboard::A ; i < sf::Keyboard::KeyCount;i++)
        {
            if(io.KeysDown[i])
            {
                keys[m_waiting_for_input].key = i;
                keys[m_waiting_for_input].action.source = input::KEYBOARD;
                m_waiting_for_input = -1;
            }
        }
        for(int j = sf::Mouse::Left; j < sf::Mouse::ButtonCount;j++)
        {
            if(io.MouseDown[j])
            {
                keys[m_waiting_for_input].key = j;
                keys[m_waiting_for_input].action.source = input::MOUSE;
                m_waiting_for_input = -1;
            }
        }
    }
    //apply
    if(ImGui::Button("Apply changes")){
        getEngine().getInput()->setKeysMap(m_keysmap);
        getEngine().getInput()->updateKeyBindings();
    }
    //save
    if(ImGui::Button("Save Keymap")){
        std::ofstream os(m_keymap_file);
        cereal::JSONOutputArchive output(os);
        output(m_keysmap);
    }
    //load
    if(ImGui::Button("Load Keymap")){
        std::ifstream is(m_keymap_file);
        cereal::JSONInputArchive input(is);
 //       input(m_keysmap);
    }
    ImGui::End();
}



void KeyMapper::toggle()
{
    m_enabled = !m_enabled;
    if(m_enabled){
        m_keysmap = getEngine().getInput()->getKeysMap();
    }
}
