#pragma once

#include "foundation/result.h"

#include <cmath>

namespace pp::app {

struct DocumentCanvasClearPlan {
    float r = 0.0F;
    float g = 0.0F;
    float b = 0.0F;
    float a = 0.0F;
    bool clears_canvas = false;
    bool records_undo = false;
    bool marks_unsaved = false;
    bool no_op = true;
};

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

    virtual void clear_current_canvas(float r, float g, float b, float a) = 0;
};

[[nodiscard]] inline pp::foundation::Status validate_clear_color_channel(float value) noexcept
{
    if (!std::isfinite(value)) {
        return pp::foundation::Status::invalid_argument("clear color channel must be finite");
    }
    if (value < 0.0F || value > 1.0F) {
        return pp::foundation::Status::out_of_range("clear color channel must be within 0..1");
    }
    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Result<DocumentCanvasClearPlan> plan_document_canvas_clear(
    bool has_canvas,
    float r = 0.0F,
    float g = 0.0F,
    float b = 0.0F,
    float a = 0.0F) noexcept
{
    const float channels[] { r, g, b, a };
    for (const float channel : channels) {
        const auto status = validate_clear_color_channel(channel);
        if (!status.ok()) {
            return pp::foundation::Result<DocumentCanvasClearPlan>::failure(status);
        }
    }

    DocumentCanvasClearPlan plan;
    plan.r = r;
    plan.g = g;
    plan.b = b;
    plan.a = a;
    plan.clears_canvas = has_canvas;
    plan.records_undo = has_canvas;
    plan.marks_unsaved = has_canvas;
    plan.no_op = !has_canvas;
    return pp::foundation::Result<DocumentCanvasClearPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Status execute_document_canvas_clear_plan(
    const DocumentCanvasClearPlan& plan,
    DocumentCanvasClearServices& services)
{
    const float channels[] { plan.r, plan.g, plan.b, plan.a };
    for (const float channel : channels) {
        const auto status = validate_clear_color_channel(channel);
        if (!status.ok()) {
            return status;
        }
    }

    if (plan.no_op || !plan.clears_canvas) {
        return pp::foundation::Status::success();
    }

    services.clear_current_canvas(plan.r, plan.g, plan.b, plan.a);
    return pp::foundation::Status::success();
}

} // namespace pp::app
