#include "pch.h"
#include "app.h"
#include "node_panel_grid.h"
#include "node_panel_floating.h"
#include "app_core/brush_ui.h"
#include "app_core/document_layer.h"
#include "legacy_brush_ui_services.h"
#include "legacy_document_layer_services.h"
#include "legacy_ui_overlay_services.h"

namespace {

bool apply_brush_color_plan(App& app, glm::vec4 color, bool update_quick, bool update_color_panel)
{
    return pp::panopainter::apply_legacy_brush_color_plan(app, color, update_quick, update_color_panel);
}

bool apply_brush_texture_plan(App& app, pp::app::BrushUiTextureSlot slot, const std::string& path, const std::string& thumb)
{
    return pp::panopainter::apply_legacy_brush_texture_plan(app, slot, path, thumb);
}

bool apply_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
{
    return pp::panopainter::apply_legacy_brush_preset_plan(app, brush);
}

void execute_document_layer_operation_plan(
    App& app,
    const pp::app::DocumentLayerOperationPlan& plan,
    const std::shared_ptr<class Layer>& pending_layer = nullptr)
{
    const auto status = pp::panopainter::execute_legacy_document_layer_operation_plan(app, plan, pending_layer);
    if (!status.ok())
        LOG("Layer operation failed: %s", status.message);
}

void close_legacy_overlay_handle_ignoring_status(
    Node& anchor,
    pp::ui::NodeHandle overlay) noexcept
{
    (void)pp::panopainter::close_legacy_overlay_node(anchor, overlay);
}

template <typename PopupOverlay, typename TickOverlay>
void close_legacy_overlay_handles_if_open(
    Node& anchor,
    const PopupOverlay& popup_overlay,
    const TickOverlay& tick_overlay) noexcept
{
    if (popup_overlay) {
        close_legacy_overlay_handle_ignoring_status(anchor, popup_overlay.value());
    }
    if (tick_overlay) {
        close_legacy_overlay_handle_ignoring_status(anchor, tick_overlay.value());
    }
}

template <class T>
std::shared_ptr<T> create_panel(LayoutManager& manager)
{
    auto ret = std::make_shared<T>();
    ret->set_manager(&manager);
    ret->init();
    ret->create();
    ret->loaded();
    return ret;
}

} // namespace

void App::init_sidebar()
{
    sidebar = layout[main_id]->find<NodeBorder>("sidebar");
    canvas = layout[main_id]->find<NodeCanvas>("paint-canvas");
    quick = layout[main_id]->find<NodePanelQuick>("panel-quick");
    floatings_container = layout[main_id]->find<Node>("floatings");

    //brushes = layout[main_id]->find<NodePanelBrush>("panel-brush");
    //layers = layout[main_id]->find<NodePanelLayer>("panel-layer");
    //color = layout[main_id]->find<NodePanelColor>("panel-color");
    //stroke = layout[main_id]->find<NodePanelStroke>("panel-stroke");

    //brushes = find_or_create_panel<NodePanelBrush>(panels);
    layers = create_panel<NodePanelLayer>(layout);
    color = create_panel<NodePanelColor>(layout);
    stroke = create_panel<NodePanelStroke>(layout);
    grid = create_panel<NodePanelGrid>(layout);
    presets = create_panel<NodePanelBrushPreset>(layout);
    animation = create_panel<NodePanelAnimation>(layout);
    //presets = find_or_create_panel<NodePanelBrushPreset>(panels);

    canvas->m_canvas->on_mode_changed = [this](kCanvasMode prev, kCanvasMode mode) {
        quick_mode_state[prev] = quick->get_state();
        if (quick_mode_state.find(mode) != quick_mode_state.end())
            quick->set_state(quick_mode_state[mode], true);
        else
            quick->reset_state(true);
        brush_update(true, true);
    };
    color->on_color_changed = [this](Node* target, glm::vec4 color) {
        apply_brush_color_plan(*this, color, true, false);
    };

    stroke->on_brush_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
        apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::tip, path, thumb);
    };
    stroke->on_pattern_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
        apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::pattern, path, thumb);
    };
    stroke->on_dual_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
        apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::dual, path, thumb);
    };
    stroke->on_stroke_change = [this](Node*) {
        const auto status = pp::panopainter::execute_legacy_brush_stroke_changed_plan(*this);
        if (!status.ok())
            LOG("Brush stroke settings action failed: %s", status.message);
    };

    quick->on_color_change = [this](Node*, glm::vec3 c) {
        apply_brush_color_plan(*this, glm::vec4(c, 1.f), false, true);
    };
    quick->on_flow_change = [this](Node*, float value) {
        stroke->set_flow(value, true, true);
    };
    quick->on_size_change = [this](Node*, float value) {
        stroke->set_size(value, true, true);
    };
    quick->on_brush_change = [this](Node*, std::shared_ptr<Brush> b) {
        apply_brush_preset_plan(*this, b);
    };

    layers->on_layer_add = [this](Node*, std::shared_ptr<class Layer> layer, int index) {
        const auto plan = pp::app::plan_document_layer_add(
            static_cast<int>(Canvas::I->m_layers.size()),
            index,
            layers->m_layers.back()->m_label_text);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value(), layer);
    };

    layers->on_layer_duplicate = [this](Node*, int source_index) {
        const auto plan = pp::app::plan_document_layer_duplicate(
            static_cast<int>(Canvas::I->m_layers.size()),
            source_index);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value());
    };

    layers->on_layer_change = [this](Node*, int, int new_idx) {
        const auto plan = pp::app::plan_document_layer_select(
            static_cast<int>(canvas->m_canvas->m_layers.size()),
            new_idx);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value());
    };

    layers->on_layer_order = [this](Node*, int old_idx, int new_idx) {
        const auto plan = pp::app::plan_document_layer_reorder(
            static_cast<int>(canvas->m_canvas->m_layers.size()),
            old_idx,
            new_idx);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value());
    };

    layers->on_layer_delete = [this](Node*, int idx) {
        const auto plan = pp::app::plan_document_layer_remove(
            static_cast<int>(canvas->m_canvas->m_layers.size()),
            idx);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value());
    };

    layers->on_layer_opacity_changed = [this](Node*, int idx, float value) {
        const auto plan = pp::app::plan_document_layer_opacity(
            static_cast<int>(canvas->m_canvas->m_layers.size()),
            idx,
            value);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value());
    };

    layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) {
        const auto plan = pp::app::plan_document_layer_visibility(
            static_cast<int>(canvas->m_canvas->m_layers.size()),
            idx,
            visible);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value());
    };

    layers->on_layer_alpha_lock_changed = [this](Node*, int idx, bool locked) {
        const auto plan = pp::app::plan_document_layer_alpha_lock(
            static_cast<int>(canvas->m_canvas->m_layers.size()),
            idx,
            locked);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value());
    };

    layers->on_layer_blend_mode_changed = [this](Node*, int idx, int mode) {
        const auto plan = pp::app::plan_document_layer_blend_mode(
            static_cast<int>(canvas->m_canvas->m_layers.size()),
            idx,
            mode);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value());
    };

    layers->on_layer_highlight_changed = [this](Node*, int idx, bool highlight) {
        const auto plan = pp::app::plan_document_layer_highlight(
            static_cast<int>(canvas->m_canvas->m_layers.size()),
            idx,
            highlight);
        if (!plan)
            return;
        execute_document_layer_operation_plan(*this, plan.value());
    };
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-stroke"))
    {
        button->on_click = [this, button](Node*) {
            auto* popup_root = layout[main_id];
            if (!popup_root) {
                return;
            }
            auto screen = popup_root->m_size;
            glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
            if (stroke->m_parent)
            {
                if (auto fp = dynamic_cast<NodePanelFloating*>(stroke->m_parent->m_parent))
                {
                    pp::panopainter::detach_legacy_node_from_parent(*stroke);
                    pp::panopainter::close_legacy_dialog_node(*fp);
                }
            }
            const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, stroke);
            if (!popup_overlay) {
                return;
            }
            stroke->SetSize(350, glm::max(100.f, screen.y - pos.y - 50.f));
            stroke->find("title")->SetVisibility(true);
            auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
            tick->SetPositioning(YGPositionTypeAbsolute);
            tick->SetSize(32, 16);
            tick->SetPosition(pos.x - 16, pos.y);
            tick->set_image("data/ui/popup-tick-up.png");
            auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
            if (!popup_overlay || !tick_overlay)
            {
                close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
                return;
            }
            const auto popup_handle = popup_overlay.value();
            const auto tick_handle = tick_overlay.value();
            popup_root->update();

            stroke->SetPosition(pos.x - stroke->m_size.x / 2.f, pos.y + 16);
            stroke->SetPositioning(YGPositionTypeAbsolute);
            pp::panopainter::activate_legacy_popup_overlay(*stroke);
            auto scroll = stroke->find<NodeScroll>("scroller");
            (void)scroll;
            stroke->m_popup_overlay_handle = popup_handle;
            stroke->m_tick_overlay_handle = tick_handle;

        };
    }
    //if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-brush"))
    //{
    //    button->on_click = [this, button](Node*) {
    //        panels->get_child_index(brushes.get()) == -1 ? panels->add_child(brushes) : panels->remove_child(brushes.get());
    //        panels->fix_scroll();
    //        button->set_color(panels->get_child_index(brushes.get()) == -1 ? color_button_normal : color_button_hlight);
    //    };
    //}
    //if (auto* button = layout[main_id]->find<NodeButton>("btn-brush-preset"))
    //{
    //    button->on_click = [this, button](Node*) {
    //        panels->get_child_index(presets.get()) == -1 ? panels->add_child(presets) : panels->remove_child(presets.get());
    //        panels->fix_scroll();
    //        button->set_color(panels->get_child_index(presets.get()) == -1 ? color_button_normal : color_button_hlight);
    //    };
    //}
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-color"))
    {
        button->on_click = [this, button](Node*) {
            auto* popup_root = layout[main_id];
            if (!popup_root) {
                return;
            }
            glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
            const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, color);
            if (!popup_overlay)
            {
                LOG("Color popup overlay failed: %s", popup_overlay.status().message);
                return;
            }
            color->find("title")->SetVisibility(true);
            color->SetSize(350, 350);
            auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*layout[main_id]);
            tick->SetPositioning(YGPositionTypeAbsolute);
            tick->SetSize(32, 16);
            tick->SetPosition(pos.x - 16, pos.y);
            tick->set_image("data/ui/popup-tick-up.png");
            auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
            if (!popup_overlay || !tick_overlay)
            {
                close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
                return;
            }
            const auto popup_handle = popup_overlay.value();
            const auto tick_handle = tick_overlay.value();
            popup_root->update();

            color->SetPosition(pos.x - color->m_size.x / 2.f, pos.y + 16);
            color->SetPositioning(YGPositionTypeAbsolute);
            pp::panopainter::activate_legacy_popup_overlay(*color);
            color->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
                close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
                close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
            };
        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-layer"))
    {
        button->on_click = [this, button](Node*) {
            auto* popup_root = layout[main_id];
            if (!popup_root) {
                return;
            }
            auto screen = popup_root->m_size;
            glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
            layers->find("title")->SetVisibility(true);
            layers->SetSize(350, YGUndefined);
            if (layers->m_parent)
            {
                if (auto fp = dynamic_cast<NodePanelFloating*>(layers->m_parent->m_parent))
                {
                    pp::panopainter::detach_legacy_node_from_parent(*layers);
                    pp::panopainter::close_legacy_dialog_node(*fp);
                }
            }
            const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, layers);
            if (!popup_overlay) {
                return;
            }
            auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
            tick->SetPositioning(YGPositionTypeAbsolute);
            tick->SetSize(32, 16);
            tick->SetPosition(pos.x - 16, pos.y);
            tick->set_image("data/ui/popup-tick-up.png");
            auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
            if (!popup_overlay || !tick_overlay)
            {
                close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
                return;
            }
            const auto popup_handle = popup_overlay.value();
            const auto tick_handle = tick_overlay.value();
            popup_root->update();

            layers->SetPosition(pos.x - layers->m_size.x / 2.f, pos.y + 16);
            layers->SetPositioning(YGPositionTypeAbsolute);
            pp::panopainter::activate_legacy_popup_overlay(*layers);
            auto scroll = layers->find<NodeScroll>("layers-container");
            scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 200.f));
            layers->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
                close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
                close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
            };

        };
    }
    if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-grids-panel"))
    {
        button->on_click = [this, button](Node*) {
            auto* popup_root = layout[main_id];
            if (!popup_root) {
                return;
            }
            auto screen = popup_root->m_size;
            glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
            grid->find("title")->SetVisibility(true);
            grid->SetSize(350, YGUndefined);
            if (grid->m_parent)
            {
                if (auto fp = dynamic_cast<NodePanelFloating*>(grid->m_parent->m_parent))
                {
                    pp::panopainter::detach_legacy_node_from_parent(*grid);
                    pp::panopainter::close_legacy_dialog_node(*fp);
                }
            }
            const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, grid);
            if (!popup_overlay)
            {
                LOG("Grid popup overlay failed: %s", popup_overlay.status().message);
                return;
            }
            auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
            tick->SetPositioning(YGPositionTypeAbsolute);
            tick->SetSize(32, 16);
            tick->SetPosition(pos.x - 16, pos.y);
            tick->set_image("data/ui/popup-tick-up.png");
            auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
            if (!popup_overlay || !tick_overlay)
            {
                close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
                return;
            }
            const auto popup_handle = popup_overlay.value();
            const auto tick_handle = tick_overlay.value();
            popup_root->update();

            grid->SetPosition(pos.x - grid->m_size.x / 2.f, pos.y + 16);
            grid->SetPositioning(YGPositionTypeAbsolute);
            pp::panopainter::activate_legacy_popup_overlay(*grid);
            auto scroll = grid->find<NodeScroll>("scroller");
            scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 250.f));

            grid->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
                close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
                close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
            };
        };
    }
}
