#include "pch.h"
#include "legacy_ui_node_event.h"

#include "node.h"
#include "layout.h"

namespace pp::panopainter {
namespace {

void transfer_mouse_focus(Node& node, Event* e)
{
    if (e->m_cat != kEventCategory::MouseEvent)
        return;

    if (node.child_mouse_focus == node.current_mouse_capture)
        return;

    if (!node.is_child(node.current_mouse_capture.get()))
        return;

    MouseEvent* me = static_cast<MouseEvent*>(e);
    if (node.child_mouse_focus)
    {
        MouseEvent e2 = *me;
        e2.m_type = kEventType::MouseUnfocus;
        node.child_mouse_focus->handle_event(&e2);
        node.child_mouse_focus->m_mouse_focus = false;
    }
    MouseEvent e2 = *me;
    e2.m_type = kEventType::MouseFocus;
    node.current_mouse_capture->handle_event(&e2);
    node.child_mouse_focus = node.current_mouse_capture;
    node.child_mouse_focus->m_mouse_focus = true;
}

bool should_skip_children(Node& node, Event* e)
{
    bool skip_children = false;

    if (e->m_cat == kEventCategory::MouseEvent)
    {
        MouseEvent* me = static_cast<MouseEvent*>(e);
        skip_children |= !node.m_mouse_inside && !point_in_rect(me->m_pos, node.m_clip);
    }

    skip_children |= (e->m_cat == kEventCategory::MouseEvent || e->m_cat == kEventCategory::GestureEvent) &&
        (node.m_mouse_captured) && (node.root()->current_mouse_capture.get() == &node) && node.m_capture_children; // <-- THIS IS WRONG "!m_capture_children" is correct, but it breaks everything if changed

    return skip_children;
}

void handle_mouse_focus_transition(Node& node, Event* e, const std::shared_ptr<Node>& child)
{
    if (e->m_cat != kEventCategory::MouseEvent || node.child_mouse_focus.get() == child.get())
        return;

    MouseEvent* me = static_cast<MouseEvent*>(e);
    if (node.child_mouse_focus && !node.child_mouse_focus->m_destroyed)
    {
        MouseEvent e2 = *me;
        e2.m_type = kEventType::MouseUnfocus;
        node.child_mouse_focus->handle_event(&e2);
        node.child_mouse_focus->m_mouse_focus = false;
    }
    MouseEvent e2 = *me;
    e2.m_type = kEventType::MouseFocus;
    child->handle_event(&e2);
    if (!child->m_destroyed)
    {
        node.child_mouse_focus = child;
        node.child_mouse_focus->m_mouse_focus = true;
    }
}

void update_mouse_inside(Node& node, MouseEvent* me)
{
    bool old_inside = node.m_mouse_inside;
    node.m_mouse_inside = point_in_rect(me->m_pos, node.m_clip);
    if (old_inside == false && node.m_mouse_inside == true)
    {
        MouseEvent e2 = *me;
        e2.m_type = kEventType::MouseEnter;
        node.handle_event(&e2);
    }
    if (old_inside == true && node.m_mouse_inside == false)
    {
        MouseEvent e2 = *me;
        e2.m_type = kEventType::MouseLeave;
        node.handle_event(&e2);
    }
}

} // namespace

kEventResult handle_legacy_ui_node_event(Node& node, Event* e)
{
    kEventResult ret = kEventResult::Available;

    if (e->m_cat == kEventCategory::KeyEvent && node.current_key_capture)
        return node.current_key_capture->on_event(e);
    
    if (node.current_mouse_capture && node.current_mouse_capture.get() != &node)
    {
        transfer_mouse_focus(node, e);
        return node.current_mouse_capture->on_event(e);
    }

    // skip mouse events if outside
    if (!node.m_display || glm::any(glm::lessThanEqual(zw(node.m_clip), { 0, 0 })))
        return kEventResult::Available;

    if (!should_skip_children(node, e))
    {
        // make a copy because any handler can change the children vector
        auto children_copy = node.m_children;
        for (auto it = children_copy.rbegin(); it != children_copy.rend(); ++it)
        {
            if ((*it)->on_event(e) == kEventResult::Consumed)
            {
                if (node.m_flood_events)
                {
                    ret = kEventResult::Consumed;
                }
                else
                {
                    handle_mouse_focus_transition(node, e, *it);
                    ret = kEventResult::Consumed;
                    break;
                }
            }
        }
        if (ret == kEventResult::Consumed)
        {
            if (e->m_cat == kEventCategory::MouseEvent)
                update_mouse_inside(node, static_cast<MouseEvent*>(e));
            return kEventResult::Consumed;
        }
    }

    switch (e->m_cat)
    {
    case kEventCategory::MouseEvent:
    {
        if (node.m_mouse_ignore)
            break;
        MouseEvent* me = static_cast<MouseEvent*>(e);
        bool inside = point_in_rect(me->m_pos, node.m_clip);
        bool inside_old = node.m_mouse_inside;
        node.m_mouse_inside = inside;
        switch (e->m_type)
        {
        case kEventType::MouseScroll:
        case kEventType::MouseDownL:
        case kEventType::MouseDownR:
        case kEventType::MouseUpL:
        case kEventType::MouseUpR:
            if ((inside || node.m_mouse_captured) && ((node.handle_event(e) == kEventResult::Consumed) || node.m_force_mouse_capture))
                return kEventResult::Consumed;
            break;
        case kEventType::MouseMove:
            if (inside_old == false && inside == true)
            {
                MouseEvent e2 = *me;
                e2.m_type = kEventType::MouseEnter;
                node.handle_event(&e2);
            }
            if (inside || node.m_mouse_captured)
            {
                ret = node.handle_event(e);
                if (node.m_force_mouse_capture)
                    ret = kEventResult::Consumed;
            }
            if (inside_old == true && inside == false)
            {
                MouseEvent e2 = *me;
                e2.m_type = kEventType::MouseLeave;
                node.handle_event(&e2);
            }
            break;
        default:
            if (node.handle_event(e) == kEventResult::Consumed)
                return kEventResult::Consumed;
            break;
        }
        break;
    }
    case kEventCategory::GestureEvent:
    {
        if (node.m_mouse_ignore)
            break;
        GestureEvent* ge = static_cast<GestureEvent*>(e);
        bool inside = point_in_rect(ge->m_pos, node.m_clip);
        node.m_mouse_inside = inside;
        if ((inside || node.m_mouse_captured) && node.handle_event(e) == kEventResult::Consumed)
            return kEventResult::Consumed;
        break;
    }
    default:
        if (node.handle_event(e) == kEventResult::Consumed)
            return kEventResult::Consumed;
        break;
    }
    return ret;
}

void legacy_ui_node_mouse_capture(Node& node)
{
    if (!node.m_parent || !node.m_manager)
        return;

    auto root = node.m_manager->get_ref("main");
    if (!root)
        return;
    
    auto& c = root->current_mouse_capture;
    auto& s = root->m_capture_stack;

    // already owner of capture
    if (c.get() == &node || std::find_if(s.begin(), s.end(),
        [&node](const auto& a) { return a.get() == &node; }) != s.end())
        return;

    if (c)
    {
        if (c->is_child_recursive(&node))
        {
            // save on stack
            s.push_back(c);
        }
        else
        {
            // cancel previous owner
            MouseEvent e;
            e.m_type = kEventType::MouseCancel;
            c->handle_event(&e);

            // TODO: only delete nodes on a different tree, 
            // so preserve direct parents of this

            // also clear the whole stack
            //s.clear();

            s.push_back(c);
        }
    }
    
    // make current
    c = node.shared_from_this();
    node.m_mouse_captured = true;
}

void legacy_ui_node_mouse_release(Node& node)
{
    if (!node.m_parent || !node.m_manager)
        return;

    auto root = node.m_manager->get_ref("main");
    if (!root)
        return;

    auto& c = root->current_mouse_capture;
    auto& s = root->m_capture_stack;

    s.erase(std::remove_if(s.begin(), s.end(),
        [&node](const auto& a) { return a.get() == &node; }), s.end());
    if (c.get() == &node)
    {
        if (s.empty())
        {
            c = nullptr;
        }
        else
        {
            c = s.back();
            s.pop_back();
        }
    }

    node.m_mouse_captured = false;
}

void legacy_ui_node_key_capture(Node& node)
{
    if (!node.m_parent || !node.m_manager)
        return;

    auto root = node.m_manager->get_ref("main");
    if (!root)
        return;

    root->current_key_capture = node.shared_from_this();
    node.m_key_captured = true;
}

void legacy_ui_node_key_release(Node& node)
{
    if (!node.m_parent || !node.m_manager)
        return;

    auto root = node.m_manager->get_ref("main");
    if (!root)
        return;

    if (root->current_key_capture.get() == &node)
        root->current_key_capture = nullptr;
    node.m_key_captured = false;
}

} // namespace pp::panopainter
