#pragma once

#include "foundation/result.h"

#include <array>
#include <cmath>

namespace pp::app {

enum class CanvasViewCursorMode {
    never = 0,
    small_brush = 1,
    not_painting = 2,
    always = 3,
};

struct CanvasCameraState {
    std::array<float, 16> rotation {};
    std::array<float, 3> position {};
    float field_of_view_degrees = 85.0F;
    std::array<float, 2> pan {};
};

struct CanvasViewDensityPlan {
    float density = 1.0F;
    bool recreates_buffers = true;
};

struct CanvasViewCursorModePlan {
    CanvasViewCursorMode mode = CanvasViewCursorMode::never;
};

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

    virtual void reset_camera(const CanvasCameraState& state) = 0;
    virtual void set_density(const CanvasViewDensityPlan& plan) = 0;
    virtual void set_cursor_mode(const CanvasViewCursorModePlan& plan) = 0;
};

[[nodiscard]] constexpr CanvasCameraState plan_canvas_camera_reset() noexcept
{
    CanvasCameraState state;
    state.rotation = {
        1.0F, 0.0F, 0.0F, 0.0F,
        0.0F, 1.0F, 0.0F, 0.0F,
        0.0F, 0.0F, 1.0F, 0.0F,
        0.0F, 0.0F, 0.0F, 1.0F,
    };
    state.position = { 0.0F, 0.0F, 0.0F };
    state.field_of_view_degrees = 85.0F;
    state.pan = { 0.0F, 0.0F };
    return state;
}

[[nodiscard]] inline pp::foundation::Result<CanvasViewDensityPlan> plan_canvas_view_density(float density)
{
    if (!std::isfinite(density) || density <= 0.0F) {
        return pp::foundation::Result<CanvasViewDensityPlan>::failure(
            pp::foundation::Status::invalid_argument("canvas view density must be finite and positive"));
    }

    return pp::foundation::Result<CanvasViewDensityPlan>::success(CanvasViewDensityPlan {
        .density = density,
        .recreates_buffers = true,
    });
}

[[nodiscard]] inline pp::foundation::Result<CanvasViewCursorModePlan> plan_canvas_view_cursor_mode(int mode)
{
    if (mode < static_cast<int>(CanvasViewCursorMode::never)
        || mode > static_cast<int>(CanvasViewCursorMode::always)) {
        return pp::foundation::Result<CanvasViewCursorModePlan>::failure(
            pp::foundation::Status::out_of_range("canvas cursor mode is out of range"));
    }

    return pp::foundation::Result<CanvasViewCursorModePlan>::success(CanvasViewCursorModePlan {
        .mode = static_cast<CanvasViewCursorMode>(mode),
    });
}

[[nodiscard]] inline pp::foundation::Status execute_canvas_camera_reset(CanvasViewServices& services)
{
    services.reset_camera(plan_canvas_camera_reset());
    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Status execute_canvas_view_density(
    float density,
    CanvasViewServices& services)
{
    const auto plan = plan_canvas_view_density(density);
    if (!plan) {
        return plan.status();
    }

    services.set_density(plan.value());
    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Status execute_canvas_view_cursor_mode(
    int mode,
    CanvasViewServices& services)
{
    const auto plan = plan_canvas_view_cursor_mode(mode);
    if (!plan) {
        return plan.status();
    }

    services.set_cursor_mode(plan.value());
    return pp::foundation::Status::success();
}

} // namespace pp::app
