#include "pch.h"

#include "legacy_quick_ui_services.h"

#include "app.h"
#include "legacy_ui_overlay_services.h"
#include "node_image.h"
#include "node_stroke_preview.h"

namespace pp::panopainter {
namespace {

void close_legacy_overlay_handle_ignoring_status(
    Node& anchor,
    const pp::foundation::Result<pp::ui::NodeHandle>& overlay) noexcept
{
    if (!overlay) {
        return;
    }
    (void)close_legacy_overlay_node(anchor, overlay.value());
}

void close_legacy_overlay_handles_if_open(
    Node& anchor,
    const pp::foundation::Result<pp::ui::NodeHandle>& popup,
    const pp::foundation::Result<pp::ui::NodeHandle>& tick) noexcept
{
    if (popup) {
        close_legacy_overlay_handle_ignoring_status(anchor, popup);
    }
    if (tick) {
        close_legacy_overlay_handle_ignoring_status(anchor, tick);
    }
}

struct CheckedPopupAndTickOverlays {
    bool opened;
    pp::foundation::Result<pp::ui::NodeHandle> popup;
    pp::foundation::Result<pp::ui::NodeHandle> tick;
};

template <typename PopupT>
void set_popup_ticker_overlay_close_handler(
    Node& anchor,
    PopupT& popup,
    const CheckedPopupAndTickOverlays& popup_and_tick) noexcept
{
    popup.on_popup_close = [&anchor, popup_and_tick](Node*) {
        close_legacy_overlay_handles_if_open(anchor, popup_and_tick.popup, popup_and_tick.tick);
    };
}

CheckedPopupAndTickOverlays open_checked_popup_and_tick_overlays(
    Node& anchor,
    const std::shared_ptr<Node>& popup,
    const std::shared_ptr<Node>& tick) noexcept
{
    auto popup_overlay = open_legacy_overlay_node_with_handle(anchor, popup);
    auto tick_overlay = open_legacy_overlay_node_with_handle(anchor, tick);
    if (!popup_overlay || !tick_overlay) {
        close_legacy_overlay_handles_if_open(anchor, popup_overlay, tick_overlay);
        return { false, popup_overlay, tick_overlay };
    }
    return { true, popup_overlay, tick_overlay };
}

std::shared_ptr<NodeImage> create_checked_popup_tick(
    Node& anchor,
    const glm::vec2& tick_pos,
    float anchor_height,
    const glm::vec2& tick_sz) noexcept
{
    auto tick = make_legacy_overlay_node_for_anchor<NodeImage>(anchor);
    tick->SetPositioning(YGPositionTypeAbsolute);
    tick->SetPosition(tick_pos.x, tick_pos.y + (anchor_height - tick_sz.y) * 0.5f);
    tick->SetSize(tick_sz);
    tick->set_image("data/ui/popup-tick.png");
    tick->m_scale = { 1, 1 };
    return tick;
}

class LegacyQuickUiServices final : public pp::app::QuickUiServices {
public:
    LegacyQuickUiServices(NodePanelQuick& panel, const NodePanelQuick::MiniState* restore_state = nullptr) noexcept
        : panel_(panel)
        , restore_state_(restore_state)
    {
    }

    void select_slot(pp::app::QuickUiSlotKind slot_kind, int slot_index, bool fire_event) override
    {
        if (slot_kind == pp::app::QuickUiSlotKind::brush) {
            panel_.set_selected_brush_index(slot_index, fire_event);
            return;
        }

        panel_.set_selected_color_index(slot_index, fire_event);
    }

    void open_slot_popup(pp::app::QuickUiSlotKind slot_kind, int slot_index) override
    {
        if (slot_kind == pp::app::QuickUiSlotKind::brush) {
            open_brush_popup(slot_index);
            return;
        }

        open_color_picker(slot_index);
    }

    void restore_state(int brush_index, int color_index, bool fire_event) override
    {
        if (!restore_state_)
            return;

        for (int i = 0; i < static_cast<int>(panel_.m_button_brushes.size()); i++)
        {
            auto b = static_cast<NodeStrokePreview*>(panel_.m_button_brushes[i]->m_children[0].get());
            b->m_brush = restore_state_->brushes[i];
            b->draw_stroke();
            auto c = static_cast<NodeBorder*>(panel_.m_button_colors[i]->m_children[0].get());
            c->m_color = restore_state_->colors[i];
        }
        panel_.set_selected_color_index(color_index, fire_event);
        panel_.set_selected_brush_index(brush_index, fire_event);
    }

    void reset_state(bool fire_event) override
    {
        for (int i = 0; i < static_cast<int>(panel_.m_button_brushes.size()); i++)
        {
            auto b = static_cast<NodeStrokePreview*>(panel_.m_button_brushes[i]->m_children[0].get());
            b->m_brush = std::make_shared<Brush>();
            b->m_brush->load_tip("data/brushes/Round-Hard.png", "data/brushes/thumbs/Round-Hard.png");
            b->draw_stroke();
        }
        static_cast<NodeBorder*>(panel_.m_button_colors[0]->m_children[0].get())->m_color = glm::vec4(0, 0, 0, 1);
        static_cast<NodeBorder*>(panel_.m_button_colors[1]->m_children[0].get())->m_color = glm::vec4(.5, .5, .5, 1);
        static_cast<NodeBorder*>(panel_.m_button_colors[2]->m_children[0].get())->m_color = glm::vec4(1, 1, 1, 1);
        panel_.set_selected_brush_index(0, fire_event);
        panel_.set_selected_color_index(0, fire_event);
    }

private:
    void open_brush_popup(int slot_index)
    {
        auto button = panel_.m_button_brushes[slot_index];
        if (!button)
            return;

        auto popup = App::I->presets;
        auto screen = panel_.root()->m_size;
        glm::vec2 tick_sz = { 16, 32 };
        glm::vec2 tick_pos = button->m_pos + glm::vec2(button->m_size.x, 0);
        glm::vec2 popup_pos = { tick_pos.x + tick_sz.x, tick_pos.y };

        auto tick = create_checked_popup_tick(panel_, tick_pos, button->m_size.y, tick_sz);

        float hh = popup->m_container->m_children.size() > 10 ? (screen.y - 90.f) : 400.f;
        popup->SetWidth(350);
        popup->SetHeight(glm::max(hh, 400.f));
        popup->SetPositioning(YGPositionTypeAbsolute);
        popup->SetPosition(popup_pos);

        const auto popup_overlays = open_checked_popup_and_tick_overlays(panel_, popup, tick);
        if (!popup_overlays.opened) {
            return;
        }

        panel_.root()->update();
        popup->tick(0);
        popup->update();

        if (tick_pos.x + popup->m_size.x > screen.x)
        {
            tick_pos = button->m_pos - glm::vec2(tick_sz.x, 0);
            popup_pos = { tick_pos.x - popup->GetWidth(), tick_pos.y };
            tick->m_scale.x = -1.f;
        }
        popup_pos = glm::clamp(popup_pos, { 0, 0 }, screen - popup->m_size);
        popup->SetPosition(popup_pos);
        tick->SetPosition(tick_pos.x, tick_pos.y + (button->m_size.y - tick_sz.y) * 0.5f);
        popup->update();

        activate_legacy_popup_overlay(*popup);
        set_popup_ticker_overlay_close_handler(panel_, *popup, popup_overlays);

        auto* panel = &panel_;
        popup->on_brush_changed = [panel, button](Node*, std::shared_ptr<Brush>& b) {
            auto pr = static_cast<NodeStrokePreview*>(button->m_children[0].get());
            *pr->m_brush = *b;
            pr->m_brush->load();
            pr->draw_stroke();
            if (panel->on_brush_change)
                panel->on_brush_change(button, pr->m_brush);
        };
    }

    void open_color_picker(int slot_index)
    {
        auto target = panel_.m_button_colors[slot_index];
        if (!target)
            return;

        auto popup = panel_.m_picker;
        auto screen = panel_.root()->m_size;
        glm::vec2 tick_sz = { 16, 32 };
        glm::vec2 tick_pos = target->m_pos + glm::vec2(target->m_size.x, 0);
        glm::vec2 popup_pos = { tick_pos.x + tick_sz.x, tick_pos.y - 140.f };

        auto tick = create_checked_popup_tick(panel_, tick_pos, target->m_size.y, tick_sz);

        popup->SetPositioning(YGPositionTypeAbsolute);
        popup->SetPosition(popup_pos);
        const auto popup_overlays = open_checked_popup_and_tick_overlays(panel_, popup, tick);
        if (!popup_overlays.opened) {
            return;
        }

        panel_.root()->update();
        popup->tick(0);
        popup->update();

        if (tick_pos.x + popup->m_size.x > screen.x)
        {
            tick_pos = target->m_pos - glm::vec2(tick_sz.x, 0);
            popup_pos = { tick_pos.x - popup->GetWidth(), tick_pos.y - 140.f };
            tick->m_scale.x = -1.f;
        }
        popup_pos = glm::clamp(popup_pos, { 0, 0 }, screen - popup->m_size);
        popup->SetPosition(popup_pos);
        tick->SetPosition(tick_pos.x, tick_pos.y + (target->m_size.y - tick_sz.y) * 0.5f);
        popup->update();

        activate_legacy_popup_overlay(*popup);

        auto c = static_cast<NodeBorder*>(target->m_children[0].get());
        panel_.m_picker->set_color(c->m_color);
        set_popup_ticker_overlay_close_handler(panel_, *popup, popup_overlays);

        auto* panel = &panel_;
        panel_.m_picker->on_color_change = [panel, c](Node*, glm::vec3 rgb) {
            c->m_color = glm::vec4(rgb, 1.f);
            if (panel->on_color_change)
                panel->on_color_change(panel, rgb);
        };
    }

    NodePanelQuick& panel_;
    const NodePanelQuick::MiniState* restore_state_ = nullptr;
};

} // namespace

pp::foundation::Status execute_legacy_quick_ui_plan(
    NodePanelQuick& panel,
    const pp::app::QuickUiPlan& plan,
    const NodePanelQuick::MiniState* restore_state)
{
    LegacyQuickUiServices services(panel, restore_state);
    return pp::app::execute_quick_ui_plan(plan, services);
}

} // namespace pp::panopainter
