#include "InputGlfw.hpp"

void TSE::GLFW::InputGlfw::Init()
{
    Instance = new InputGlfw();
}

void TSE::GLFW::InputGlfw::Delete()
{
    delete(Instance);
}

void TSE::GLFW::InputGlfw::KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
{
    switch (action)
    {
    case 1: //down
        for(auto handler : ((InputGlfw*)instance())->keyHandler)
        {
            if(handler->enabled())
                handler->OnKeyDown((Key)key, (Modifier)mods);
        }
        break;
    case 0: //up
        for(auto handler : ((InputGlfw*)instance())->keyHandler)
        {
            if(handler->enabled())
                handler->OnKeyUp((Key)key, (Modifier)mods);
        }
        break;
    case 2: //hold
        for(auto handler : ((InputGlfw*)instance())->keyHandler)
        {
            if(handler->enabled())
                handler->OnKeyHold((Key)key, (Modifier)mods);
        }
        break;
    }
}

void TSE::GLFW::InputGlfw::MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
{
    switch (action)
    {
    case 1: //down
        for(auto handler : ((InputGlfw*)instance())->cursorHandler)
        {
            if(handler->enabled())
                handler->OnMouseButtonDown((MouseBtn)button, (Modifier)mods);
        }
        break;
    case 0: //up
        for(auto handler : ((InputGlfw*)instance())->cursorHandler)
        {
            if(handler->enabled())
                handler->OnMouseButtonUp((MouseBtn)button, (Modifier)mods);
        }
        break;
    case 2: //hold
        for(auto handler : ((InputGlfw*)instance())->cursorHandler)
        {
            if(handler->enabled())
                handler->OnMouseButtonHold((MouseBtn)button, (Modifier)mods);
        }
        break;
    }
}

void TSE::GLFW::InputGlfw::CursorPosCallback(GLFWwindow *window, double xpos, double ypos)
{
    for(auto handler : ((InputGlfw*)instance())->cursorHandler)
    {
        if(handler->enabled())
            handler->OnMousePosition(Vector2(xpos, ypos));
    }
}

void TSE::GLFW::InputGlfw::ScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
{
    for(auto handler : ((InputGlfw*)instance())->cursorHandler)
    {
        if(handler->enabled())
            handler->OnMouseScroll(Vector2(xoffset, yoffset));
    }
}

std::string UTF32ToUTF8(char32_t ch)
{
    std::string result;
    if (ch <= 0x7F)
        result += static_cast<char>(ch);
    else if (ch <= 0x7FF) {
        result += static_cast<char>(0xC0 | ((ch >> 6) & 0x1F));
        result += static_cast<char>(0x80 | (ch & 0x3F));
    }
    else if (ch <= 0xFFFF) {
        result += static_cast<char>(0xE0 | ((ch >> 12) & 0x0F));
        result += static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
        result += static_cast<char>(0x80 | (ch & 0x3F));
    }
    else if (ch <= 0x10FFFF) {
        result += static_cast<char>(0xF0 | ((ch >> 18) & 0x07));
        result += static_cast<char>(0x80 | ((ch >> 12) & 0x3F));
        result += static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
        result += static_cast<char>(0x80 | (ch & 0x3F));
    }
    return result;
}

void TSE::GLFW::InputGlfw::CharCallback(GLFWwindow *window, unsigned int codepoint)
{
        char32_t c = static_cast<char32_t>(codepoint);
        std::string msg = UTF32ToUTF8(c);

        for(auto handler : ((InputGlfw*)instance())->keyHandler)
        {
            if(handler->enabled())
                handler->OnChar(msg);
        }
}
