#pragma once

#include "foundation/result.h"

#include <string>
#include <string_view>
#include <utility>

namespace pp::app {

enum class GridUiOperation {
    request_heightmap_pick,
    load_heightmap,
    clear_heightmap,
    reload_heightmap,
    render_lightmap,
    commit_heightmap,
};

struct GridUiPlan {
    GridUiOperation operation = GridUiOperation::request_heightmap_pick;
    std::string path;
    int texture_resolution = 0;
    int sample_count = 0;
    bool opens_picker = false;
    bool loads_heightmap = false;
    bool clears_heightmap = false;
    bool renders_lightmap = false;
    bool commits_heightmap = false;
    bool updates_preview = false;
    bool updates_ground_opacity = false;
    bool updates_shading_mode = false;
    bool shows_unsupported_message = false;
    bool shows_progress = false;
    bool mutates_grid_state = false;
};

class GridUiServices {
public:
    virtual ~GridUiServices() = default;

    virtual void request_heightmap_pick() = 0;
    virtual pp::foundation::Status load_heightmap(std::string_view path, bool raise_ground_opacity) = 0;
    virtual void clear_heightmap(bool updates_preview) = 0;
    virtual void render_lightmap(bool shows_unsupported_message, bool renders_lightmap) = 0;
    virtual void commit_heightmap(bool updates_ground_opacity) = 0;
};

[[nodiscard]] inline pp::foundation::Status validate_grid_texture_resolution(int texture_resolution) noexcept
{
    if (texture_resolution <= 0 || texture_resolution > 16384) {
        return pp::foundation::Status::out_of_range("grid texture resolution must be within 1..16384");
    }

    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Status validate_grid_lightmap_samples(int sample_count) noexcept
{
    if (sample_count <= 0 || sample_count > 4096) {
        return pp::foundation::Status::out_of_range("grid lightmap samples must be within 1..4096");
    }

    return pp::foundation::Status::success();
}

[[nodiscard]] inline constexpr GridUiPlan plan_grid_heightmap_pick() noexcept
{
    GridUiPlan plan;
    plan.operation = GridUiOperation::request_heightmap_pick;
    plan.opens_picker = true;
    return plan;
}

[[nodiscard]] inline pp::foundation::Result<GridUiPlan> plan_grid_heightmap_load(std::string_view path)
{
    if (path.empty()) {
        return pp::foundation::Result<GridUiPlan>::failure(
            pp::foundation::Status::invalid_argument("heightmap path must not be empty"));
    }

    GridUiPlan plan;
    plan.operation = GridUiOperation::load_heightmap;
    plan.path = std::string(path);
    plan.loads_heightmap = true;
    plan.updates_preview = true;
    plan.updates_ground_opacity = true;
    plan.mutates_grid_state = true;
    return pp::foundation::Result<GridUiPlan>::success(std::move(plan));
}

[[nodiscard]] inline constexpr GridUiPlan plan_grid_heightmap_clear(bool has_heightmap) noexcept
{
    GridUiPlan plan;
    plan.operation = GridUiOperation::clear_heightmap;
    plan.clears_heightmap = true;
    plan.updates_preview = has_heightmap;
    plan.mutates_grid_state = has_heightmap;
    return plan;
}

[[nodiscard]] inline pp::foundation::Result<GridUiPlan> plan_grid_heightmap_reload(std::string_view path)
{
    auto plan = plan_grid_heightmap_load(path);
    if (!plan) {
        return pp::foundation::Result<GridUiPlan>::failure(plan.status());
    }
    plan.value().operation = GridUiOperation::reload_heightmap;
    plan.value().updates_ground_opacity = false;
    return plan;
}

[[nodiscard]] inline pp::foundation::Result<GridUiPlan> plan_grid_lightmap_render(
    bool has_heightmap,
    bool supports_float32,
    bool supports_float16,
    int texture_resolution,
    int sample_count)
{
    const auto texture_status = validate_grid_texture_resolution(texture_resolution);
    if (!texture_status.ok()) {
        return pp::foundation::Result<GridUiPlan>::failure(texture_status);
    }

    const auto sample_status = validate_grid_lightmap_samples(sample_count);
    if (!sample_status.ok()) {
        return pp::foundation::Result<GridUiPlan>::failure(sample_status);
    }

    GridUiPlan plan;
    plan.operation = GridUiOperation::render_lightmap;
    plan.texture_resolution = texture_resolution;
    plan.sample_count = sample_count;
    if (!supports_float32 && !supports_float16) {
        plan.shows_unsupported_message = true;
        return pp::foundation::Result<GridUiPlan>::success(plan);
    }

    plan.renders_lightmap = has_heightmap;
    plan.shows_progress = has_heightmap;
    plan.updates_shading_mode = has_heightmap;
    plan.mutates_grid_state = has_heightmap;
    return pp::foundation::Result<GridUiPlan>::success(plan);
}

[[nodiscard]] inline constexpr GridUiPlan plan_grid_heightmap_commit(bool has_canvas) noexcept
{
    GridUiPlan plan;
    plan.operation = GridUiOperation::commit_heightmap;
    plan.commits_heightmap = has_canvas;
    plan.updates_ground_opacity = has_canvas;
    plan.mutates_grid_state = has_canvas;
    return plan;
}

[[nodiscard]] inline pp::foundation::Status execute_grid_ui_plan(
    const GridUiPlan& plan,
    GridUiServices& services)
{
    switch (plan.operation) {
    case GridUiOperation::request_heightmap_pick:
        if (!plan.opens_picker) {
            return pp::foundation::Status::invalid_argument("grid heightmap pick plan must open a picker");
        }
        services.request_heightmap_pick();
        return pp::foundation::Status::success();

    case GridUiOperation::load_heightmap:
    case GridUiOperation::reload_heightmap:
        if (!plan.loads_heightmap || plan.path.empty()) {
            return pp::foundation::Status::invalid_argument("grid heightmap load plan must provide a path");
        }
        return services.load_heightmap(plan.path, plan.updates_ground_opacity);

    case GridUiOperation::clear_heightmap:
        if (!plan.clears_heightmap) {
            return pp::foundation::Status::invalid_argument("grid heightmap clear plan must clear heightmap state");
        }
        services.clear_heightmap(plan.updates_preview);
        return pp::foundation::Status::success();

    case GridUiOperation::render_lightmap:
    {
        const auto texture_status = validate_grid_texture_resolution(plan.texture_resolution);
        if (!texture_status.ok()) {
            return texture_status;
        }
        const auto sample_status = validate_grid_lightmap_samples(plan.sample_count);
        if (!sample_status.ok()) {
            return sample_status;
        }
        if (!plan.shows_unsupported_message && !plan.renders_lightmap) {
            return pp::foundation::Status::success();
        }
        services.render_lightmap(plan.shows_unsupported_message, plan.renders_lightmap);
        return pp::foundation::Status::success();
    }

    case GridUiOperation::commit_heightmap:
        if (plan.commits_heightmap) {
            services.commit_heightmap(plan.updates_ground_opacity);
        }
        return pp::foundation::Status::success();
    }

    return pp::foundation::Status::invalid_argument("unknown grid UI operation");
}

} // namespace pp::app
