#include <RuntimeStateCache/RuntimeStateCache.hpp>
#include <Widget/Layout/ScrollPanel/ScrollPanel.hpp>
#include <Color/Color.hpp>

ScrollPanel::ScrollPanel(int naturalHeight, Widget *child) :
    Layout(child),
    naturalHeight(naturalHeight),
    scrollPos(scrollPosInternal)
{}

ScrollPanel::ScrollPanel(int &scrollPos, int naturalHeight, Widget *child) :
    Layout(child),
    naturalHeight(naturalHeight),
    scrollPos(scrollPos)
{}

ScrollPanel::ScrollPanel(std::string stateUrl, int naturalHeight, Widget *child) :
    Layout(child),
    naturalHeight(naturalHeight),
    scrollPos(*RuntimeStateCache::get<int>(stateUrl))
{}

ScrollPanel* ScrollPanel::withBackgroundColor(glm::vec4 color)
{
    backgroundColor = color;
    return this;
}

void ScrollPanel::layout()
{
    ChildNode &childNode = m_children.back();
    childNode.pos = glm::ivec2(0);

    glm::ivec2 size = m_size;
    int minHeight = childNode.widget->getMinSize().y;
    if (minHeight > m_size.y)
    {
        size.x -= scrollbarWidth;
        size.y = minHeight;
    }

    childNode.widget->onResize(size);
}

glm::ivec2 ScrollPanel::getMinSize()
{
    glm::ivec2 size = m_children.back().widget->getMinSize();
    size.x += scrollbarWidth;
    size.y = naturalHeight;
    return size;
}

void ScrollPanel::draw(GraphicsContext &context)
{
    context.color = backgroundColor;
    context.drawRect(0, 0, m_size.x, m_size.y);

    int maxScrollPos = std::max(0, m_children.back().widget->getMinSize().y - m_size.y);
    scrollPos = std::max(0, std::min(scrollPos, maxScrollPos));

    glm::ivec2 offset = glm::ivec2(0, scrollPos);
    for (ChildNode &childNode : m_children)
    {
        context.pushViewport();
        context.clipViewport(childNode.pos - offset, childNode.widget->getCurrentSize() + offset);
        childNode.widget->widgetAbsPos = context.getAbsPos(glm::ivec2(0));
        childNode.widget->draw(context);
        context.popViewport();
    }

    int totalHeight = m_children.back().widget->getMinSize().y;
    if (totalHeight > m_size.y)
    {
        int scrollbarY = m_size.y * scrollPos / totalHeight;
        int scrollbarHeight = std::max(1, (m_size.y * m_size.y + totalHeight - 1) / totalHeight);

        context.color = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
        context.drawRect(m_size.x-scrollbarWidth+1, 0, scrollbarWidth-2, m_size.y);

        context.color = Color::action;
        context.drawRect(m_size.x-scrollbarWidth, scrollbarY, scrollbarWidth, scrollbarHeight);
    }
}

void ScrollPanel::onScroll(int delta)
{
    if (!isMouseInside())
    {
        return;
    }

    scrollPos -= scrollSpeed * delta;
    
    if (scrollPos < 0)
    {
        scrollPos = 0;
    }

    int maxScrollPos = std::max(0, m_children.back().widget->getMinSize().y - m_size.y);
    if (scrollPos > maxScrollPos)
    {
        scrollPos = maxScrollPos;
    }

    onMouseMoved(m_mousePos);
}

void ScrollPanel::onMouseMoved(glm::ivec2 pos)
{
    Widget::onMouseMoved(pos);
    pos.y += scrollPos;

    for (ChildNode &childNode : m_children)
    {
        if (isMouseInside())
        {
            childNode.widget->onMouseMoved(pos - childNode.pos);
        }
        else
        {
            childNode.widget->onMouseMoved(glm::ivec2(-1, -1));
        }
    }

    if (mousePivotY.has_value())
    {
        int totalHeight = m_children.back().widget->getMinSize().y;
        int mouseDelta = m_mousePos.y - *mousePivotY;
        int scrollDelta = mouseDelta * totalHeight / m_size.y;

        int maxScrollPos = std::max(0, totalHeight - m_size.y);
        scrollPos = std::max(0, std::min(maxScrollPos, scrollPivot + scrollDelta));
    }
}

void ScrollPanel::onMouseUp(Uint8 button)
{
    if (button == SDL_BUTTON_LEFT)
    {
        mousePivotY = std::nullopt;
    }

    Layout::onMouseUp(button);
}

void ScrollPanel::onMouseDown(Uint8 button)
{
    int totalHeight = m_children.back().widget->getMinSize().y;
    if (totalHeight <= m_size.y)
    {
        Layout::onMouseDown(button);
        return;
    }

    if (isMouseInside())
    {
        if (button == SDL_BUTTON_LEFT && m_mousePos.x >= m_size.x-scrollbarWidth)
        {
            mousePivotY = m_mousePos.y;
            scrollPivot = scrollPos;
            focus();
            return;
        }

        Layout::onMouseDown(button);
    }
}