#include "pch.h"
#include "app.h"
#include "app_core/app_preferences.h"
#include "legacy_brush_ui_services.h"
#include "legacy_ui_overlay_services.h"
#include "legacy_preference_storage.h"
#include "node_dialog_picker.h"
#include "node_panel_brush.h"
#include "node_panel_color.h"
#include "node_panel_grid.h"
#include "node_panel_floating.h"
#include "serializer.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_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
{
    return pp::panopainter::apply_legacy_brush_preset_plan(app, brush);
}

void save_floating_panel_state(Serializer::List& list, const std::shared_ptr<Node>& child)
{
    if (auto const& f = std::dynamic_pointer_cast<NodePanelFloating>(child))
    {
        auto fd = list.add<Serializer::Descriptor>();
        fd->class_id = "ui-flt";
        fd->set("pos", Serializer::Vec2(f->GetPosition()));
        fd->set("size", Serializer::Vec2(f->m_size));
        fd->set("class", Serializer::Integer((int)f->m_class));
        fd->set("title", Serializer::CString(f->m_title->m_text));
    }
}

void save_docked_panel_state(Serializer::List& list, const std::shared_ptr<Node>& child, const char* class_id)
{
    if (auto const& f = std::dynamic_pointer_cast<NodePanelFloating>(child))
    {
        auto fd = list.add<Serializer::Descriptor>();
        fd->class_id = class_id;
        fd->set("size", Serializer::Vec2(f->m_size));
        fd->set("class", Serializer::Integer((int)f->m_class));
        fd->set("title", Serializer::CString(f->m_title->m_text));
    }
}

template <class TList>
void restore_panel_children(
    App& app,
    std::shared_ptr<Node> container,
    const TList& items,
    bool docked)
{
    for (auto const& l : items)
    {
        auto ld = std::static_pointer_cast<Serializer::Descriptor>(l);
        auto size = ld->value<Serializer::Vec2>("size");
        auto cls = static_cast<NodePanelFloating::kClass>(ld->value<Serializer::Integer>("class"));
        auto f = container->add_child<NodePanelFloating>();
        std::string title = "Floating Panel";
        ld->value<Serializer::CString>("title", title);
        f->m_title->set_text(title.c_str());
        switch (cls)
        {
        case NodePanelFloating::kClass::Presets:
        {
            auto floating_presets = f->m_container->add_child_ref<NodePanelBrushPreset>();
            if (!docked)
                app.floating_presets = floating_presets;
            floating_presets->SetHeightP(100);
            floating_presets->on_brush_changed = [&app](Node*, std::shared_ptr<Brush>& b) {
                apply_brush_preset_plan(app, b);
            };
            break;
        }
        case NodePanelFloating::kClass::Color:
        {
            auto floating_color = f->m_container->add_child_ref<NodePanelColor>();
            if (!docked)
                app.floating_color = floating_color;
            floating_color->SetHeightP(100);
            if (docked)
                pp::panopainter::destroy_legacy_node(*floating_color->find("title"));
            else
                floating_color->find("title")->SetVisibility(false);
            floating_color->on_color_changed = [&app](Node*, glm::vec4 color) {
                apply_brush_color_plan(app, color, false, false);
            };
            break;
        }
        case NodePanelFloating::kClass::ColorAdv:
        {
            app.floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
            app.floating_picker->m_autohide = false;
            app.floating_picker->on_color_change = [&app](Node*, glm::vec3 color) {
                apply_brush_color_plan(app, glm::vec4(color, 1.f), false, false);
            };
            break;
        }
        case NodePanelFloating::kClass::Layers:
            f->m_container->add_child(app.layers);
            if (!docked)
                f->SetMinHeight(100);
            if (!docked)
                f->SetHeight(300);
            if (!docked)
                app.layers->find("title")->SetVisibility(false);
            app.layers->SetPositioning(YGPositionTypeRelative);
            app.layers->SetPosition(0, 0);
            app.layers->SetWidthP(100);
            app.layers->SetHeightP(100);
            app.layers->SetFlexShrink(0);
            break;
        case NodePanelFloating::kClass::Brush:
            f->m_container->add_child(app.stroke);
            if (!docked)
                app.stroke->find("title")->SetVisibility(false);
            app.stroke->SetPositioning(YGPositionTypeRelative);
            app.stroke->SetPosition(0, 0);
            app.stroke->SetWidthP(100);
            app.stroke->SetHeightP(100);
            break;
        case NodePanelFloating::kClass::Grids:
            f->m_container->add_child(app.grid);
            if (!docked)
                app.grid->find("title")->SetVisibility(false);
            app.grid->SetPositioning(YGPositionTypeRelative);
            app.grid->SetPosition(0, 0);
            app.grid->SetWidthP(100);
            app.grid->SetHeightP(100);
            break;
        case NodePanelFloating::kClass::Animation:
            f->m_container->add_child(app.animation);
            f->m_droppable = false;
            app.animation->SetPositioning(YGPositionTypeRelative);
            app.animation->SetPosition(0, 0);
            app.animation->SetWidthP(100);
            app.animation->SetHeightP(100);
            break;
        case NodePanelFloating::kClass::Generic:
        default:
            f->m_container->add_child<Node>();
            break;
        }
        f->m_class = cls;
        if (docked)
            f->m_dock = container;
        f->SetPositioning(docked ? YGPositionTypeRelative : YGPositionTypeAbsolute);
        f->SetPosition(0, 0);
        f->SetSize(size);
    }
}

} // namespace

void App::set_ui_rtl(bool rtl)
{
    const auto plan = pp::app::plan_interface_direction(rtl);
    ui_rtl = plan.direction == pp::app::InterfaceDirection::right_to_left;
    layout[main_id]->find("central-row")->SetRTL(
        ui_rtl ? YGDirectionRTL : YGDirectionLTR);
}

bool App::get_ui_rtl() const
{
    return ui_rtl;
}

void App::ui_save()
{
    Serializer::Descriptor d;
    d.class_id = "ui-state";

    Serializer::List list_floatings;
    for (auto const& c : layout[main_id]->find("floatings")->m_children)
        save_floating_panel_state(list_floatings, c);
    d.set("floatings", list_floatings);

    Serializer::List list_drop_left;
    for (auto const& c : layout[main_id]->find("drop-left")->m_children)
        save_docked_panel_state(list_drop_left, c, "ui-dpl");
    d.set("drop-left", list_drop_left);

    Serializer::List list_drop_right;
    for (auto const& c : layout[main_id]->find("drop-right")->m_children)
        save_docked_panel_state(list_drop_right, c, "ui-dpr");
    d.set("drop-right", list_drop_right);

    pp::panopainter::set_legacy_ui_state_preferences(d, ui_rtl);
    save_platform_ui_state();

    pp::panopainter::save_legacy_preferences();
}

void App::ui_restore()
{
    const auto preferences = pp::panopainter::read_legacy_ui_preferences();
    if (preferences.has_rtl)
        set_ui_rtl(preferences.rtl);

    if (!preferences.state)
        return;

    auto floatings = layout[main_id]->find_ref("floatings");
    auto drop_left = layout[main_id]->find_ref("drop-left");
    auto drop_right = layout[main_id]->find_ref("drop-right");
    auto d = preferences.state;

    restore_panel_children(*this, floatings, d->get<Serializer::List>("floatings")->items, false);
    restore_panel_children(*this, drop_left, d->get<Serializer::List>("drop-left")->items, true);
    restore_panel_children(*this, drop_right, d->get<Serializer::List>("drop-right")->items, true);
}
