#pragma once

#include "../base/Types.h"

#include <blend2d/blend2d.h>
#include <renderive/renderable/color/Color_Cache.hpp>

#include <span>
#include <string_view>

namespace renderive::detail {

class Blend2D_Color_Cache final : public ::Color_Cache {
public:
    Blend2D_Color_Cache() = default;
    explicit Blend2D_Color_Cache(std::pmr::memory_resource&) {}

    void clear() override;
    void composite(const ::Color_Cache& source) override;
    void ensure_size(Size size);
    [[nodiscard]] Size size() const noexcept;
    [[nodiscard]] Image_View view() const noexcept;

private:
    friend class Painter;
    BLImage image_;
};

class Painter {
public:
    class Clip_Scope {
    public:
        Clip_Scope(Painter& painter, RectF rect);
        ~Clip_Scope();
        Clip_Scope(const Clip_Scope&) = delete;
        Clip_Scope& operator=(const Clip_Scope&) = delete;
    private:
        Painter* painter_{};
    };
    Painter(Blend2D_Color_Cache& cache, Size size);
    ~Painter();
    Painter(const Painter&) = delete;
    Painter& operator=(const Painter&) = delete;

    [[nodiscard]] explicit operator bool() const noexcept { return active_; }
    [[nodiscard]] Clip_Scope scoped_clip(RectF rect);
    void composite(const Blend2D_Color_Cache& source);
    void line(PointF first, PointF second, const Pen& pen);
    void polyline(std::span<const PointF> points, const Pen& pen);
    void polygon(std::span<const PointF> points, const Pen& pen, const Brush& brush);
    void rect(RectF rect, const Pen& pen, const Brush& brush = {});
    void circle(PointF center, double radius, const Pen& pen, const Brush& brush = {});
    void text(PointF position, std::string_view value, const Font& font, const Pen& pen,
              double rotation_degrees = 0.0);
    void heatmap(RectF target,
                 int width,
                 int height,
                 std::span<const Pixel> pixels,
                 Image_Interpolation_Mode interpolation);

private:
    void apply_pen(const Pen& pen);
    static BLRgba rgba(Color color) noexcept;
    static BLFont make_font(const Font& font);

    BLContext context_;
    bool active_{};
};

} // namespace renderive::detail
