#include "pch.h"

#include "legacy_app_shell_services.h"

#include "app.h"
#include "app_core/document_import.h"
#include "legacy_app_dialog_services.h"
#include "legacy_canvas_view_services.h"
#include "legacy_document_canvas_services.h"
#include "legacy_history_services.h"
#include "legacy_ui_overlay_services.h"

namespace pp::panopainter {
namespace {

class LegacyDocumentExportMenuServices final : public pp::app::DocumentExportMenuServices {
public:
    explicit LegacyDocumentExportMenuServices(App& app) noexcept
        : app_(app)
    {
    }

    void show_jpeg_dialog() override { app_.dialog_export(".jpg"); }
    void show_png_dialog() override { app_.dialog_export(".png"); }
    void show_layers_dialog() override { app_.dialog_export_layers(); }
    void show_cube_faces_dialog() override { app_.dialog_export_cube_faces(); }
    void show_depth_dialog() override { app_.dialog_export_depth(); }
    void show_animation_frames_dialog() override { app_.dialog_export_anim_frames(); }
    void show_animation_mp4_dialog() override { app_.dialog_export_mp4(); }
    void show_timelapse_dialog() override { app_.dialog_timelapse_export(); }
    void show_license_disabled() override
    {
        app_.message_box("License", "This function is disabled in demo mode.");
    }

private:
    App& app_;
};

class LegacyFileMenuServices final : public pp::app::FileMenuServices {
public:
    explicit LegacyFileMenuServices(App& app) noexcept
        : app_(app)
    {
    }

    void show_new_document_dialog() override
    {
        app_.dialog_newdoc();
    }

    void pick_image_for_import() override
    {
        auto* app_ptr = &app_;
        app_.pick_image([app_ptr](std::string path) {
            Image img;
            img.load_file(path);
            const auto import_plan = pp::app::plan_document_image_import(img.width, img.height);
            if (!import_plan)
                return;

            class LegacyDocumentImageImportServices final : public pp::app::DocumentImageImportServices {
            public:
                LegacyDocumentImageImportServices(App& app, Image& image) noexcept
                    : app_(app)
                    , image_(image)
                {
                }

                void import_equirectangular(std::string_view import_path) override
                {
                    if (Canvas::I)
                        Canvas::I->import_equirectangular(std::string(import_path));
                }

                void enter_transform_import(std::string_view) override
                {
                    if (!app_.canvas || !app_.canvas->m_canvas)
                        return;

                    auto* mode = static_cast<CanvasModeTransform*>(
                        app_.canvas->m_canvas->modes[(int)kCanvasMode::Import][0]);
                    mode->m_action = CanvasModeTransform::ActionType::Import;
                    mode->m_source_image = std::move(image_);
                    Canvas::set_mode(kCanvasMode::Import);
                }

            private:
                App& app_;
                Image& image_;
            };

            LegacyDocumentImageImportServices services(*app_ptr, img);
            const auto status = pp::app::execute_document_image_import_plan(
                import_plan.value(),
                path,
                services);
            if (!status.ok())
                LOG("Image import failed: %s", status.message);
        });
    }

    void pick_project_file() override
    {
        auto* app_ptr = &app_;
        app_.pick_file({ "ppi" }, [app_ptr](std::string path) {
            app_ptr->open_document(path);
        });
    }

    void show_cloud_browser_dialog() override
    {
        app_.dialog_browse();
    }

    void save_document(pp::app::DocumentSaveIntent intent) override
    {
        app_.save_document(intent);
    }

    void show_export_jpeg_dialog(pp::app::DocumentExportMenuKind kind) override
    {
        (void)apply_legacy_document_export_menu_plan(app_, kind);
    }

    void show_export_submenu() override
    {
    }

    void share_document() override
    {
        app_.share_file(app_.doc_path);
    }

    void show_resize_dialog() override
    {
        app_.dialog_resize();
    }

    void upload_to_cloud() override
    {
        app_.cloud_upload();
    }

    void browse_cloud_documents() override
    {
        app_.cloud_browse();
    }

private:
    App& app_;
};

class LegacyMainToolbarServices final : public pp::app::MainToolbarServices {
public:
    explicit LegacyMainToolbarServices(App& app) noexcept
        : app_(app)
    {
    }

    void show_open_dialog() override
    {
        app_.dialog_open();
    }

    void show_save_dialog() override
    {
        app_.dialog_save();
    }

    void invoke_undo(const pp::app::HistoryUiPlan& plan) override
    {
        execute_history_plan(plan);
    }

    void invoke_redo(const pp::app::HistoryUiPlan& plan) override
    {
        execute_history_plan(plan);
    }

    void clear_history(const pp::app::HistoryUiPlan& plan) override
    {
        execute_history_plan(plan);
    }

    void clear_canvas(const pp::app::DocumentCanvasClearPlan& plan) override
    {
        const auto status = execute_legacy_document_canvas_clear_plan(app_, plan);
        if (!status.ok())
            LOG("Canvas clear failed: %s", status.message);
    }

    void show_message_box() override
    {
        app_.msgbox = pp::panopainter::create_legacy_app_message_dialog(
            app_,
            pp::app::plan_main_toolbar_message_dialog()).get();
    }

    void show_settings_dialog() override
    {
        app_.settings = pp::panopainter::create_legacy_settings_dialog_overlay(app_).get();
    }

private:
    void execute_history_plan(const pp::app::HistoryUiPlan& plan)
    {
        const auto status = execute_legacy_history_plan(plan);
        if (!status.ok())
            LOG("History action failed: %s", status.message);
    }

    App& app_;
};

class LegacyAboutMenuServices final : public pp::app::AboutMenuServices {
public:
    explicit LegacyAboutMenuServices(App& app) noexcept
        : app_(app)
    {
    }

    void show_user_manual() override
    {
        app_.dialog_usermanual();
    }

    void show_about_dialog() override
    {
        app_.dialog_about();
    }

    void show_whats_new_dialog() override
    {
        app_.dialog_whatsnew(true);
    }

    void trigger_crash_test() override
    {
        LOG("crashing");
        app_.crash_test();
    }

    void run_performance_test(const pp::app::AboutMenuPlan& plan) override
    {
        if (!Canvas::I)
            return;

        LOG("perf");
        std::string message;
        const int performance_iterations = plan.performance_iterations;
        app_.render_task([&]
        {
            auto start = std::chrono::high_resolution_clock::now();
            Canvas::I->stroke_start({ 0, 0, 0 }, 0.9f);
            for (int i = 0; i < performance_iterations; i++)
            {
                Canvas::I->stroke_update({ 100, 100, 0 }, 0.9f);
                Canvas::I->stroke_update({ 200, 200, 0 }, 0.9f);
                Canvas::I->stroke_update({ 200, 100, 0 }, 0.9f);
                Canvas::I->stroke_update({ 100, 200, 0 }, 0.9f);
                Canvas::I->stroke_update({ 300, 300, 0 }, 0.9f);
                Canvas::I->stroke_update({ 200, 500, 0 }, 0.9f);
                Canvas::I->stroke_update({ 500, 500, 0 }, 0.9f);
                Canvas::I->stroke_update({ 400, 400, 0 }, 0.9f);
                Canvas::I->stroke_update({ 0, 200, 0 }, 0.9f);
                Canvas::I->stroke_update({ 200, 0, 0 }, 0.9f);
                Canvas::I->stroke_draw();
            }
            Canvas::I->stroke_end();
            auto diff = std::chrono::high_resolution_clock::now() - start;
            auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(diff).count();
            LOG("%lld ms", ms);
            message = "Time " + std::to_string(ms) + " ms";
        });
        app_.message_box("Performance test", message);
    }

private:
    App& app_;
};

class LegacyToolsMenuServices final : public pp::app::ToolsMenuServices {
public:
    explicit LegacyToolsMenuServices(App& app) noexcept
        : app_(app)
    {
    }

    void show_panels_submenu() override
    {
    }

    void show_options_submenu() override
    {
    }

    void clear_grid_overlays() override
    {
        auto* mode = static_cast<CanvasModeGrid*>(Canvas::modes[(int)kCanvasMode::Grid][0]);
        mode->clear();
    }

    void reset_camera() override
    {
        const auto status = execute_legacy_canvas_camera_reset(app_);
        if (!status.ok())
            LOG("Canvas camera reset failed: %s", status.message);
    }

    void show_shortcuts_dialog() override
    {
        app_.dialog_shortcuts();
    }

    void start_sonarpen() override
    {
        app_.start_platform_sonarpen();
    }

private:
    App& app_;
};

} // namespace

bool apply_legacy_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind)
{
    const auto requires_license = pp::app::document_export_menu_requires_license(kind);
    const auto plan = pp::app::plan_document_export_menu_action(
        kind,
        app.canvas != nullptr,
        !requires_license || app.check_license());
    LegacyDocumentExportMenuServices services(app);
    const auto status = pp::app::execute_document_export_menu_plan(plan, services);
    if (!status.ok())
        LOG("Document export menu action failed: %s", status.message);
    return status.ok() && plan.opens_dialog;
}

void apply_legacy_file_menu_command(App& app, pp::app::FileMenuCommand command)
{
    const auto plan = pp::app::plan_file_menu_command(command);
    LegacyFileMenuServices services(app);
    const auto status = pp::app::execute_file_menu_plan(plan, services);
    if (!status.ok())
        LOG("File menu action failed: %s", status.message);
}

void execute_legacy_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan)
{
    LegacyMainToolbarServices services(app);
    const auto status = pp::app::execute_main_toolbar_plan(plan, services);
    if (!status.ok())
        LOG("Main toolbar action failed: %s", status.message);
}

void execute_legacy_about_menu_plan(App& app, const pp::app::AboutMenuPlan& plan)
{
    LegacyAboutMenuServices services(app);
    const auto status = pp::app::execute_about_menu_plan(plan, services);
    if (!status.ok())
        LOG("About menu action failed: %s", status.message);
}

void execute_legacy_tools_menu_plan(App& app, const pp::app::ToolsMenuPlan& plan)
{
    LegacyToolsMenuServices services(app);
    const auto status = pp::app::execute_tools_menu_plan(plan, services);
    if (!status.ok())
        LOG("Tools menu action failed: %s", status.message);
}

} // namespace pp::panopainter
