#include "app_core/document_import.h"
#include "test_harness.h"

#include <string>
#include <string_view>

namespace {

class FakeDocumentImageImportServices final : public pp::app::DocumentImageImportServices {
public:
    void import_equirectangular(std::string_view path) override
    {
        equirectangular_imports += 1;
        last_path = std::string(path);
        call_order += "equirect;";
    }

    void enter_transform_import(std::string_view path) override
    {
        transform_imports += 1;
        last_path = std::string(path);
        call_order += "transform;";
    }

    int equirectangular_imports = 0;
    int transform_imports = 0;
    std::string last_path;
    std::string call_order;
};

void import_plan_detects_wide_equirectangular_images(pp::tests::Harness& harness)
{
    const auto plan = pp::app::plan_document_image_import(4096, 2048);
    PP_EXPECT(harness, plan);
    if (plan) {
        PP_EXPECT(harness, plan.value().action == pp::app::DocumentImageImportAction::import_equirectangular);
        PP_EXPECT(harness, plan.value().imports_equirectangular);
        PP_EXPECT(harness, !plan.value().enters_transform_mode);
    }
}

void import_plan_detects_legacy_vertical_cube_strips(pp::tests::Harness& harness)
{
    const auto plan = pp::app::plan_document_image_import(512, 3072);
    PP_EXPECT(harness, plan);
    if (plan) {
        PP_EXPECT(harness, plan.value().action == pp::app::DocumentImageImportAction::import_equirectangular);
        PP_EXPECT(harness, plan.value().imports_equirectangular);
        PP_EXPECT(harness, !plan.value().enters_transform_mode);
    }
}

void import_plan_places_regular_images_as_transform_sources(pp::tests::Harness& harness)
{
    const auto plan = pp::app::plan_document_image_import(1024, 1024);
    PP_EXPECT(harness, plan);
    if (plan) {
        PP_EXPECT(harness, plan.value().action == pp::app::DocumentImageImportAction::place_transform);
        PP_EXPECT(harness, !plan.value().imports_equirectangular);
        PP_EXPECT(harness, plan.value().enters_transform_mode);
    }
}

void import_plan_rejects_invalid_dimensions(pp::tests::Harness& harness)
{
    PP_EXPECT(harness, !pp::app::plan_document_image_import(0, 1024));
    PP_EXPECT(harness, !pp::app::plan_document_image_import(1024, 0));
    PP_EXPECT(harness, !pp::app::plan_document_image_import(-1, 1024));
}

void import_executor_dispatches_equirectangular_import(pp::tests::Harness& harness)
{
    FakeDocumentImageImportServices services;
    const auto plan = pp::app::plan_document_image_import(4096, 2048);
    PP_EXPECT(harness, plan);
    if (plan) {
        PP_EXPECT(harness, pp::app::execute_document_image_import_plan(
            plan.value(),
            "D:/Paint/equirect.png",
            services).ok());
    }

    PP_EXPECT(harness, services.equirectangular_imports == 1);
    PP_EXPECT(harness, services.transform_imports == 0);
    PP_EXPECT(harness, services.last_path == "D:/Paint/equirect.png");
    PP_EXPECT(harness, services.call_order == "equirect;");
}

void import_executor_dispatches_transform_import(pp::tests::Harness& harness)
{
    FakeDocumentImageImportServices services;
    const auto plan = pp::app::plan_document_image_import(1024, 1024);
    PP_EXPECT(harness, plan);
    if (plan) {
        PP_EXPECT(harness, pp::app::execute_document_image_import_plan(
            plan.value(),
            "D:/Paint/flat.png",
            services).ok());
    }

    PP_EXPECT(harness, services.equirectangular_imports == 0);
    PP_EXPECT(harness, services.transform_imports == 1);
    PP_EXPECT(harness, services.last_path == "D:/Paint/flat.png");
    PP_EXPECT(harness, services.call_order == "transform;");
}

void import_executor_rejects_empty_path(pp::tests::Harness& harness)
{
    FakeDocumentImageImportServices services;
    const auto plan = pp::app::plan_document_image_import(1024, 1024);
    PP_EXPECT(harness, plan);
    if (plan) {
        const auto status = pp::app::execute_document_image_import_plan(plan.value(), "", services);
        PP_EXPECT(harness, !status.ok());
        PP_EXPECT(harness, status.code == pp::foundation::StatusCode::invalid_argument);
    }
    PP_EXPECT(harness, services.equirectangular_imports == 0);
    PP_EXPECT(harness, services.transform_imports == 0);
}

void import_executor_rejects_invalid_dimensions(pp::tests::Harness& harness)
{
    FakeDocumentImageImportServices services;
    pp::app::DocumentImageImportPlan plan;
    plan.width = 0;
    plan.height = 1024;
    plan.action = pp::app::DocumentImageImportAction::import_equirectangular;
    plan.imports_equirectangular = true;

    const auto status = pp::app::execute_document_image_import_plan(plan, "D:/Paint/bad.png", services);
    PP_EXPECT(harness, !status.ok());
    PP_EXPECT(harness, status.code == pp::foundation::StatusCode::invalid_argument);
    PP_EXPECT(harness, services.equirectangular_imports == 0);
    PP_EXPECT(harness, services.transform_imports == 0);
}

}

int main()
{
    pp::tests::Harness harness;
    harness.run("import plan detects wide equirectangular images", import_plan_detects_wide_equirectangular_images);
    harness.run("import plan detects legacy vertical cube strips", import_plan_detects_legacy_vertical_cube_strips);
    harness.run("import plan places regular images as transform sources", import_plan_places_regular_images_as_transform_sources);
    harness.run("import plan rejects invalid dimensions", import_plan_rejects_invalid_dimensions);
    harness.run("import executor dispatches equirectangular import", import_executor_dispatches_equirectangular_import);
    harness.run("import executor dispatches transform import", import_executor_dispatches_transform_import);
    harness.run("import executor rejects empty path", import_executor_rejects_empty_path);
    harness.run("import executor rejects invalid dimensions", import_executor_rejects_invalid_dimensions);
    return harness.finish();
}
