#include <Display/WidgetDisplay/WidgetDisplay.hpp>
#include <Color/Color.hpp>
#include <GraphicsContext/GraphicsContext.hpp>
#include <Log/Log.hpp>
#include <Utilities/Utilities.hpp>
#include <Vk/ShaderConst.hpp>

bool WidgetDisplay::m_isTextInputActive = false;

void WidgetDisplay::handleTab(SDL_Event &event)
{
    bool shiftPressed = event.key.mod & SDL_KMOD_SHIFT;

    if (currentWidget == nullptr)
    {
        return;
    }

    std::vector<Widget*> tabOrder;
    currentWidget->getInternalTabOrder(tabOrder);

    if (tabOrder.empty())
    {
        return;
    }

    auto currentIt = Widget::focused == nullptr ?
        tabOrder.end() :
        std::find(tabOrder.begin(), tabOrder.end(), Widget::focused);
    
    if (currentIt == tabOrder.end())
    {
        Widget::focused = shiftPressed ? tabOrder.back() : tabOrder.front();
        return;
    }

    if (shiftPressed)
    {
        if (currentIt == tabOrder.begin())
        {
            Widget::focused = tabOrder.back();
        }
        else
        {
            currentIt--;
            Widget::focused = *currentIt;
        }
    }
    else
    {
        currentIt++;
        if (currentIt == tabOrder.end())
        {
            currentIt = tabOrder.begin();
        }

        Widget::focused = *currentIt;
    }
}

WidgetDisplay::WidgetDisplay() :
    Display(),
    currentSize(0, 0),
    currentWidget(nullptr),
    mousePos(0, 0),
    showMouse(true)
{}

WidgetDisplay::~WidgetDisplay()
{
    delete currentWidget;
}

void WidgetDisplay::setWidget(Widget *widget)
{
    if (currentWidget != nullptr)
    {
        delete currentWidget;
    }

    currentWidget = widget;
    currentSize = glm::ivec2(0, 0);

    Widget::closePopup();
}

bool WidgetDisplay::isWidgetSet()
{
    return currentWidget != nullptr;
}

void WidgetDisplay::onEvent(SDL_Event &event)
{
    if (currentWidget == nullptr)
    {
        Display::onEvent(event);
        return;
    }

    Widget *topWidget = Widget::popup != nullptr ? Widget::popup : currentWidget;
    if (event.type == SDL_EVENT_MOUSE_MOTION)
    {
        mousePos = glm::ivec2(event.motion.x, event.motion.y);
        mousePos /= GraphicsContext::pixelSize;

        currentWidget->onMouseMoved(mousePos);
        if (Widget::popup != nullptr)
        {
            Widget::popup->onMouseMoved(mousePos);
        }
    }
    else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP)
    {
        if (Widget::dragged != nullptr)
        {
            Widget::dragged->onMouseUp(event.button.button);
        }
        else
        {
            topWidget->onMouseUp(event.button.button);
        }
    }
    else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN)
    {
        topWidget->onMouseDown(event.button.button);
    }
    else if (event.type == SDL_EVENT_KEY_DOWN)
    {
        if (event.key.key == SDLK_F11)
        {
            Display::onEvent(event);
            return;
        }

        if (event.key.key == SDLK_TAB)
        {
            handleTab(event);
            return;
        }

        if (Widget::focused != nullptr)
        {
            Widget::focused->onKeyDown(event.key.key);
        }
    }
    else if (event.type == SDL_EVENT_KEY_UP)
    {
        if (Widget::focused != nullptr)
        {
            Widget::focused->onKeyUp(event.key.key);
        }
    }
    else if (event.type == SDL_EVENT_MOUSE_WHEEL)
    {
        topWidget->onScroll(event.wheel.y);
    }
    else if (event.type == SDL_EVENT_TEXT_INPUT)
    {
        if (Widget::focused != nullptr)
        {
            Widget::focused->onTextInput(event.text.text);
        }
    }
    else if (event.type == SDL_EVENT_WINDOW_FOCUS_LOST)
    {
        Widget::focused = nullptr;
    }
    else
    {
        Display::onEvent(event);
    }
}

void WidgetDisplay::update()
{
    // NOP
}

void WidgetDisplay::fastUpdate()
{
    Widget::doPostUpdateCleanup();
}

void WidgetDisplay::drawPartial(GraphicsContext &context)
{
    if (currentWidget != nullptr)
    {
        Widget::currentCursor = &Material::cursor;
        Widget::currentHotPoint = glm::ivec2(0, 0);
    
        int screenWidth = context.getVirtualWidth();
        int screenHeight = context.getVirtualHeight();

        if (screenWidth != currentSize[0] || screenHeight != currentSize[1])
        {
            currentSize = glm::ivec2(screenWidth, screenHeight);
        }

        currentWidget->onResize(currentSize);

        if (Widget::isRequestingMousePosUpdate)
        {
            Widget::isRequestingMousePosUpdate = false;
            currentWidget->onMouseMoved(mousePos);
        }

        currentWidget->draw(context);

        bool isTextInputWidget = Widget::focused == nullptr ? false : Widget::focused->isTextInput();
        if (m_isTextInputActive != isTextInputWidget)
        {
            m_isTextInputActive = isTextInputWidget;
            
            if (m_isTextInputActive)
            {
                SDL_StartTextInput(context.m_sdlwin);
            }
            else
            {
                SDL_StopTextInput(context.m_sdlwin);
            }
        }

        if (Widget::popup != nullptr)
        {
            Widget::popup->onResize(currentSize);
            Widget::popup->draw(context);
        }

        glm::ivec2 mousePhys = context.getPhysicalMousePos();

        context.color = glm::vec4(1.0f);

        int virtualMouseX = mousePhys.x/GraphicsContext::pixelSize;
        int virtualMouseY = mousePhys.y/GraphicsContext::pixelSize;

        if (showMouse)
        {
            context.drawMaterial(
                *Widget::currentCursor,
                virtualMouseX - Widget::currentHotPoint.x,
                virtualMouseY - Widget::currentHotPoint.y
            );
        }

        context.drawToolTip(virtualMouseX+8, virtualMouseY+10);
    }
}

void WidgetDisplay::draw(GraphicsContext &context)
{
    context.clearGui(glm::vec4(1.0f));
    context.beginGui();
    
    context
        .cmdbuf()
        .bindDescriptorSet(
            VK_PIPELINE_BIND_POINT_GRAPHICS,
            *context.m_guiPipeline,
            GLSL_GUI_DESCRIPTOR_SET,
            Material::menuBackground.descriptorSet()
        );
    
    GraphicsContext::GuiRectInfo info;
    info.rectMatrix = glm::mat4(
        2.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 2.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f, 0.0f,
        -1.0f, -1.0f, 0.0f, 1.0
    );
    info.texMatrix = glm::mat4(
        1.0f, 0.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f, 0.0f,
        0.0f, 0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 0.0f, 1.0
    );
    info.color = glm::vec4(1.0f);
    context.drawGuiRect(info);
    
    drawPartial(context);

    context.endGui();
}

void WidgetDisplay::onExposed()
{
    mousePos = GraphicsContext::instance->getPhysicalMousePos();

    currentWidget->onMouseMoved(mousePos);
    if (Widget::popup != nullptr)
    {
        Widget::popup->onMouseMoved(mousePos);
    }
}