#include "pch.h"

#include "app.h"
#include "app_core/app_frame.h"
#include "app_core/canvas_tool_ui.h"
#include "legacy_ui_gl_dispatch.h"
#include "renderer_gl/opengl_capabilities.h"

namespace {

pp::app::CanvasToolMode canvas_tool_mode_from_canvas_mode(kCanvasMode mode) noexcept
{
    switch (mode) {
    case kCanvasMode::Draw:
        return pp::app::CanvasToolMode::draw;
    case kCanvasMode::Erase:
        return pp::app::CanvasToolMode::erase;
    case kCanvasMode::Line:
        return pp::app::CanvasToolMode::line;
    case kCanvasMode::Camera:
        return pp::app::CanvasToolMode::camera;
    case kCanvasMode::Grid:
        return pp::app::CanvasToolMode::grid;
    case kCanvasMode::Copy:
        return pp::app::CanvasToolMode::copy;
    case kCanvasMode::Cut:
        return pp::app::CanvasToolMode::cut;
    case kCanvasMode::Fill:
        return pp::app::CanvasToolMode::fill;
    case kCanvasMode::MaskFree:
        return pp::app::CanvasToolMode::mask_free;
    case kCanvasMode::MaskLine:
        return pp::app::CanvasToolMode::mask_line;
    case kCanvasMode::FloodFill:
        return pp::app::CanvasToolMode::flood_fill;
    default:
        return pp::app::CanvasToolMode::draw;
    }
}

void apply_legacy_app_scissor(pp::renderer::gl::OpenGlScissorRect scissor)
{
    const auto status = pp::renderer::gl::apply_opengl_scissor_rect(
        scissor,
        pp::renderer::gl::OpenGlScissorDispatch {
            .enable = pp::legacy::ui_gl::enable_opengl_state,
            .disable = pp::legacy::ui_gl::disable_opengl_state,
            .scissor = pp::legacy::ui_gl::set_opengl_scissor,
        });
    if (!status.ok())
        LOG("OpenGL scissor failed: %s", status.message);
}

void watch_layout_children(LayoutManager* layout, uint16_t main_id, int start_index, const std::function<bool(Node*)>& observer)
{
    if (!layout)
        return;

    if (auto* root = layout->get(main_id)) {
        for (int i = start_index; i < static_cast<int>(root->m_children.size()); ++i)
            root->m_children[i]->watch(observer);
    }
}

}

namespace pp::panopainter
{

bool update_legacy_app_ui_observer(App& app, Node* n)
{
    std::vector<pp::app::AppUiObserverParentClip> parent_clips;
    if (n) {
        for (Node* p = n->m_parent; p; p = p->m_parent) {
            parent_clips.push_back(pp::app::AppUiObserverParentClip {
                .clip = pp::app::AppUiObserverRect {
                    .x = p->m_clip_uncut.x,
                    .y = p->m_clip_uncut.y,
                    .width = p->m_clip_uncut.z,
                    .height = p->m_clip_uncut.w,
                },
                .padding_top = YGNodeLayoutGetPadding(p->y_node, YGEdgeTop),
                .padding_right = YGNodeLayoutGetPadding(p->y_node, YGEdgeRight),
                .padding_bottom = YGNodeLayoutGetPadding(p->y_node, YGEdgeBottom),
                .padding_left = YGNodeLayoutGetPadding(p->y_node, YGEdgeLeft),
            });
        }
    }

    const auto plan = pp::app::plan_app_ui_observer(
        n != nullptr,
        n && n->m_display,
        n && n->m_on_screen,
        n
            ? pp::app::AppUiObserverRect {
                .x = n->m_clip_uncut.x,
                .y = n->m_clip_uncut.y,
                .width = n->m_clip_uncut.z,
                .height = n->m_clip_uncut.w,
            }
            : pp::app::AppUiObserverRect {},
        parent_clips,
        app.height,
        app.zoom,
        app.off_x,
        app.off_y);
    if (!plan) {
        LOG("UI observer plan failed: %s", plan.status().message);
        return false;
    }

    if (!n)
        return false;

    if (plan.value().notify_leave_screen)
        n->handle_on_screen(true, false);
    if (plan.value().notify_enter_screen)
        n->handle_on_screen(false, true);
    n->m_on_screen = plan.value().next_on_screen;

    if (!plan.value().draw_node)
        return false;

    apply_legacy_app_scissor(pp::renderer::gl::OpenGlScissorRect {
        .enabled = 1U,
        .x = plan.value().scissor_x,
        .y = plan.value().scissor_y,
        .width = plan.value().scissor_width,
        .height = plan.value().scissor_height,
    });
    n->draw();
    return true;
}

void watch_legacy_app_ui_children(App& app, const std::function<bool(Node*)>& observer, bool skip_first_main_child)
{
    watch_layout_children(&app.layout, app.main_id, skip_first_main_child ? 1 : 0, observer);
    watch_layout_children(&app.layout_designer, app.main_id, 0, observer);
}

void update_legacy_canvas_toolbar(App& app)
{
    const auto mode = Canvas::I->m_current_mode;
    auto* pm = app.canvas->m_canvas->get_mode<CanvasModePen>();
    const auto toolbar = pp::app::plan_canvas_tool_button_state(
        canvas_tool_mode_from_canvas_mode(mode),
        pm && pm->m_picking,
        app.canvas->m_canvas->m_touch_lock);
    app.layout[app.main_id]->find<NodeButtonCustom>("btn-pick")->set_active(toolbar.pick_active);
    app.layout[app.main_id]->find<NodeButtonCustom>("btn-touchlock")->set_active(toolbar.touch_lock_active);

    app.layout[app.main_id]->find<NodeButtonCustom>("btn-pen")->set_active(toolbar.pen_active);
    app.layout[app.main_id]->find<NodeButtonCustom>("btn-erase")->set_active(toolbar.erase_active);
    app.layout[app.main_id]->find<NodeButton>("btn-cam")->set_active(toolbar.camera_active);
    app.layout[app.main_id]->find<NodeButtonCustom>("btn-line")->set_active(toolbar.line_active);
    app.layout[app.main_id]->find<NodeButton>("btn-grid")->set_active(toolbar.grid_active);
    app.layout[app.main_id]->find<NodeButton>("btn-copy")->set_active(toolbar.copy_active);
    app.layout[app.main_id]->find<NodeButton>("btn-cut")->set_active(toolbar.cut_active);
    app.layout[app.main_id]->find<NodeButtonCustom>("btn-mask-free")->set_active(toolbar.mask_free_active);
    app.layout[app.main_id]->find<NodeButtonCustom>("btn-mask-line")->set_active(toolbar.mask_line_active);
    app.layout[app.main_id]->find<NodeButtonCustom>("btn-bucket")->set_active(toolbar.flood_fill_active);
}

}
