#include "pch.h"
#include "legacy_ui_node_event.h"
#include "app.h"
#include "node.h"

kEventResult Node::on_event(Event* e)
{
    return pp::panopainter::handle_legacy_ui_node_event(*this, e);
}

kEventResult Node::handle_event(Event* e)
{
    return kEventResult::Available;
}

void Node::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom)
{

}

void Node::handle_parent_resize(glm::vec2 old_size, glm::vec2 new_size)
{

}

void Node::mouse_capture()
{
    pp::panopainter::legacy_ui_node_mouse_capture(*this);
}

void Node::mouse_release()
{
    pp::panopainter::legacy_ui_node_mouse_release(*this);
}

void Node::key_capture()
{
    pp::panopainter::legacy_ui_node_key_capture(*this);
}

void Node::key_release()
{
    pp::panopainter::legacy_ui_node_key_release(*this);
}

void Node::restore_context()
{
    for (auto& c : m_children)
        c->restore_context();
}

void Node::clear_context()
{
    for (auto& c : m_children)
        c->clear_context();
}

void Node::app_redraw()
{
    App::I->redraw = true;
    App::I->runtime().notify_render_worker();
}

void Node::watch(std::function<bool(Node*)> observer)
{
    bool cont = observer(this);
    if (cont)
    {
        for (auto& c : m_children)
        {
            //if (!glm::any(glm::lessThanEqual(zw(c->m_clip), { 0, 0 })))
                c->watch(observer);
        }
    }
}

void Node::update(float width, float height, float zoom)
{
    m_zoom = zoom;
    YGNodeStyleSetWidth(y_node, (width / zoom));
    YGNodeStyleSetHeight(y_node, (height / zoom));
    YGNodeCalculateLayout(y_node, YGUndefined, YGUndefined, YGDirectionLTR);
    m_proj = glm::ortho(0.f, (width / zoom), (height / zoom), 0.f, -1.f, 1.f);
    update_internal({ 0, 0 }, m_proj, zoom);
}

void Node::update()
{
    YGNodeCalculateLayout(y_node, YGUndefined, YGUndefined, YGDirectionLTR);
    update_internal({ 0, 0 }, m_proj, m_zoom);
}

void Node::update_internal(const glm::vec2& origin, const glm::mat4& proj, float zoom)
{
    float x = YGNodeLayoutGetLeft(y_node);
    float y = YGNodeLayoutGetTop(y_node);
    float w = YGNodeLayoutGetWidth(y_node);
    float h = YGNodeLayoutGetHeight(y_node);
    auto old_size = m_size;

    if (YGNodeStyleGetWidth(y_node).unit == YGUnit::YGUnitAuto)
        w -= m_pos_offset.x;
    //if (YGNodeStyleGetHeight(y_node).unit == YGUnit::YGUnitAuto)
    //    h -= m_pos_offset.y;
    
    m_pos = /*glm::floor*/(origin + glm::vec2(x, y) + m_pos_offset);
    m_pos_origin = /*glm::floor*/(origin + glm::vec2(x, y));
    m_size = /*glm::floor*/(glm::vec2(w, h));

    if (m_parent)
    {
        // correct the padding clip
        // should not clip the padded area
        // useful to draw decorations
        float pt = YGNodeLayoutGetPadding(m_parent->y_node, YGEdgeTop);
        float pr = YGNodeLayoutGetPadding(m_parent->y_node, YGEdgeRight);
        float pb = YGNodeLayoutGetPadding(m_parent->y_node, YGEdgeBottom);
        float pl = YGNodeLayoutGetPadding(m_parent->y_node, YGEdgeLeft);
        glm::vec2 off_p(pl, pt);
        glm::vec2 off_s(pr, pb);
        glm::vec4 pclip = { xy(m_parent->m_clip) + off_p, zw(m_parent->m_clip) - (off_s + off_p) };
        m_clip_uncut = glm::vec4(m_pos - glm::vec2(1), m_size + glm::vec2(2));
        m_clip = rect_intersection(m_clip_uncut, m_parent->m_clip);
    }
    else
    {
        m_clip_uncut = m_clip = glm::vec4(m_pos, m_size);
    }

    glm::mat4 pivot = glm::translate(glm::vec3(.5f, .5f, 0.f));
    glm::mat4 scale = glm::scale(glm::vec3(m_size, 1.f));
    glm::mat4 prescale = glm::scale(glm::vec3(m_scale, 1.f));
    glm::mat4 pos = glm::translate(glm::vec3(m_pos, 0));
    m_mvp = proj * pos * scale * pivot * prescale;
    m_proj = proj;

    if (!glm::any(glm::isnan(m_size)) && m_size != old_size || m_zoom != zoom)
    {
        m_zoom = zoom;
        handle_resize(old_size, m_size, zoom);
        for (auto& c : m_children)
            c->handle_parent_resize(old_size, m_size);
    }

    for (auto& c : m_children)
    {
        c->m_pos_offset = m_pos_offset + m_pos_offset_childred;
        c->update_internal(m_pos_origin, proj, zoom);
    }

    m_children.erase(std::remove_if(m_children.begin(), m_children.end(),
        [](const auto& n) { return n->m_destroyed; }), m_children.end());
}

void Node::tick(float dt)
{
    for (auto& c : m_children)
        c->tick(dt);
    on_tick(dt);
}
