#include "pch.h"
#include "log.h"
#include "legacy_ui_gl_dispatch.h"
#include "legacy_ui_overlay_services.h"
#include "node_scroll.h"
#include "event.h"
#include "shader.h"

Node* NodeScroll::clone_instantiate() const
{
    return new NodeScroll;
}

void NodeScroll::fix_scroll()
{
    auto pad = GetPadding();
    glm::vec2 padoff = { pad[1] + pad[3], pad[0] + pad[2] };
    auto rect = get_children_rect();
    if (rect.w > 0 && rect.z > 0)
    {
        m_offset = glm::clamp(m_offset, -zw(rect) + zw(m_clip_uncut) - padoff, { 0, 0 });
        m_pos_offset_childred = m_offset;
    }
    else
    {
        m_pos_offset_childred = m_offset = { 0, 0 };
    }
}

void NodeScroll::clone_copy(Node* dest) const
{
    NodeBorder::clone_copy(dest);
    auto n = static_cast<NodeScroll*>(dest);
    n->m_scrollbar_color = m_scrollbar_color;
    n->m_mask = m_mask;
    n->m_direction = m_direction;
}

void NodeScroll::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
{
    NodeBorder::parse_attributes(ka, attr);
    switch (ka)
    {
    case kAttribute::ScrollColor:
    {
        glm::vec4 col;
        int n = sscanf(attr->Value(), "%f %f %f %f", &col.x, &col.y, &col.z, &col.w);
        if (n == 1)
            m_scrollbar_color = glm::vec4(col.x, col.x, col.x, 1);
        else
            m_scrollbar_color = col;
        break;
    }
    case kAttribute::ScrollDir:
        if (strcmp(attr->Value(), "vertical") == 0 || strcmp(attr->Value(), "v") == 0)
        {
            m_direction = kScrollDirection::Vertical;
            m_mask = { 0, 1 };
        }
        else if (strcmp(attr->Value(), "horizontal") == 0 || strcmp(attr->Value(), "h") == 0)
        {
            m_direction = kScrollDirection::Horizontal;
            m_mask = { 1, 0 };
        }
        break;
    default:
        break;
    }
}

void NodeScroll::on_tick(float dt)
{
    auto pad = GetPadding();
    auto rect = get_children_rect();
    if (rect.w > 0 && rect.z > 0)
    {
        // fix scrollbar
        if (m_direction == kScrollDirection::Vertical)
        {
            float szv = m_size.y - (pad[0] + pad[2]);
            float new_padv = rect.w == 0 || rect.w <= szv + 2 ? 5 : 35;
            if (pad[1] != new_padv)
            {
                YGNodeStyleSetPadding(y_node, YGEdgeRight, new_padv);
                app_redraw();
            }
        }
        else
        {
            float szh = m_size.x - (pad[3] + pad[1]);
            float new_padh = rect.z == 0 || rect.z <= szh + 2 ? 5 : 35;
            if (pad[2] != new_padh)
            {
                YGNodeStyleSetPadding(y_node, YGEdgeBottom, new_padh);
                app_redraw();
            }
        }
    }
    else
    {
        SetPadding(0, 0, 0, 0);
    }
}

void NodeScroll::draw()
{
    NodeBorder::draw();
    fix_scroll();
    
    glm::vec4 rect = get_children_rect();
    glm::vec4 pad = GetPadding();
    
    if (rect.w > 0 && rect.z > 0)
    {
        pp::legacy::ui_gl::set_blend_enabled(false, "NodeScroll");

        if (m_direction == kScrollDirection::Vertical)
        {
            float sz = m_size.y - (pad[0] + pad[2]);
            float h = glm::max(30.f, sz / rect.w * sz);
            float offset_percent = m_offset.y / (rect.w - sz);
            float pr = YGNodeLayoutGetPadding(y_node, YGEdgeRight) - 5;
            pp::panopainter::configure_legacy_ui_color_shader(
                m_proj
                * glm::translate(glm::vec3(m_pos.x + m_size.x - pr, m_pos.y - offset_percent * (sz - h) + pad[0], 0))
                * glm::scale(glm::vec3(pr, h, 1))
                * glm::translate(glm::vec3(.5, .5, 0)),
                m_scrollbar_color);
        }
        else
        {
            float sz = m_size.x - (pad[3] + pad[1]);
            float h = glm::max(30.f, sz / rect.z * sz);
            float offset_percent = m_offset.x / (rect.z - sz);
            float pr = YGNodeLayoutGetPadding(y_node, YGEdgeBottom) - 5;
            pp::panopainter::configure_legacy_ui_color_shader(
                m_proj
                * glm::translate(glm::vec3(m_pos.x - offset_percent * (sz - h) + pad[3], m_pos.y + m_size.y - pr, 0))
                * glm::scale(glm::vec3(h, pr, 1))
                * glm::translate(glm::vec3(.5, .5, 0)),
                m_scrollbar_color);
        }
        m_plane.draw_fill();
    }
}

kEventResult NodeScroll::handle_event(Event* e)
{
    NodeBorder::handle_event(e);
    auto me = static_cast<MouseEvent*>(e);
    auto ge = static_cast<GestureEvent*>(e);
    //auto loc = (me->m_pos - m_pos) * root()->m_zoom;
    switch (e->m_type)
    {
    case kEventType::MouseDownL:
        m_dragging = true;
        m_drag_start = me->m_pos;
        m_offset_start = m_offset;
        // if click on the scroll area use scrolling direction, otherwise natural
        if (m_direction == kScrollDirection::Vertical)
            m_drag_dir = (me->m_pos.x - m_pos.x) > (m_size.x - YGNodeLayoutGetPadding(y_node, YGEdgeRight)) ? -1 : 1;
        else
            m_drag_dir = (me->m_pos.y - m_pos.y) > (m_size.y - YGNodeLayoutGetPadding(y_node, YGEdgeBottom)) ? -1 : 1;
        mouse_capture();
        break;
    case kEventType::MouseMove:
        if (m_dragging)
        {
            glm::vec4 rect = get_children_rect();
            glm::vec4 pad = GetPadding();
            if (rect.w > 0 && rect.z > 0)
            {
                if (m_direction == kScrollDirection::Vertical)
                {
                    float speed = m_drag_dir < 0 ? rect.w / (m_size.y - (pad[0] + pad[2])) : 1;
                    m_offset = m_offset_start + (me->m_pos - m_drag_start) * m_mask * m_drag_dir * speed;
                }
                else
                {
                    float speed = m_drag_dir < 0 ? rect.z / (m_size.x - (pad[3] + pad[1])) : 1;
                    m_offset = m_offset_start + (me->m_pos - m_drag_start) * m_mask * m_drag_dir * speed;
                }
                fix_scroll();
            }
        }
        break;
    case kEventType::MouseUpL:
        pp::panopainter::release_legacy_mouse_capture(*this);
        m_dragging = false;
        break;
     case kEventType::MouseScroll:
        m_offset += me->m_scroll_delta * 50 * m_mask;
        fix_scroll();
        break;
    case kEventType::GestureStart:
        m_offset_start = m_offset;
        mouse_capture();
        break;
    case kEventType::GestureMove:
        m_offset = m_offset_start + ge->m_pos_delta * m_mask;
        fix_scroll();
        break;
    case kEventType::GestureEnd:
        pp::panopainter::release_legacy_mouse_capture(*this);
        break;
    case kEventType::MouseCancel:
        pp::panopainter::release_legacy_mouse_capture(*this);
        m_dragging = false;
        break;
    default:
        return kEventResult::Available;
        break;
    }
    return kEventResult::Consumed;
}
