#include "paint_renderer/compositor.h"

#include <limits>

namespace pp::paint_renderer {

namespace {

[[nodiscard]] pp::foundation::Result<std::size_t> expected_pixel_count(pp::renderer::Extent2D extent) noexcept
{
    const auto extent_status = pp::renderer::validate_extent(extent);
    if (!extent_status.ok()) {
        return pp::foundation::Result<std::size_t>::failure(extent_status);
    }

    const auto width = static_cast<std::uint64_t>(extent.width);
    const auto height = static_cast<std::uint64_t>(extent.height);
    if (width > std::numeric_limits<std::uint64_t>::max() / height) {
        return pp::foundation::Result<std::size_t>::failure(
            pp::foundation::Status::out_of_range("pixel count overflows uint64"));
    }

    const auto count = width * height;
    if (count > static_cast<std::uint64_t>(std::numeric_limits<std::size_t>::max())) {
        return pp::foundation::Result<std::size_t>::failure(
            pp::foundation::Status::out_of_range("pixel count exceeds addressable memory"));
    }

    return pp::foundation::Result<std::size_t>::success(static_cast<std::size_t>(count));
}

}

pp::foundation::Status composite_layer(
    std::span<pp::paint::Rgba> destination,
    pp::renderer::Extent2D extent,
    LayerCompositeView layer) noexcept
{
    const auto pixel_count = expected_pixel_count(extent);
    if (!pixel_count) {
        return pixel_count.status();
    }

    if (destination.size() != pixel_count.value() || layer.pixels.size() != pixel_count.value()) {
        return pp::foundation::Status::invalid_argument("composite buffers must match the render extent");
    }

    if (layer.opacity < 0.0F || layer.opacity > 1.0F) {
        return pp::foundation::Status::out_of_range("layer opacity must be between 0 and 1");
    }

    if (!layer.visible || layer.opacity == 0.0F) {
        return pp::foundation::Status::success();
    }

    for (std::size_t i = 0; i < destination.size(); ++i) {
        auto stroke = layer.pixels[i];
        stroke.a *= layer.opacity;
        destination[i] = pp::paint::blend_pixels(destination[i], stroke, layer.blend_mode);
    }

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

}
