#pragma once

#include "render_3D/Render_3D.h"

#include <datoviz/stream/frame_stream.h>
#include <datoviz/vk/gpu_ctx.h>
#include <datoviz/vklite.h>

#include <cstddef>
#include <cstdint>
#include <memory>
#include <vector>

namespace renderive::detail {

class Frame_Target final {
public:
    Frame_Target(DvzGpuCtx* gpu_context, Extent_2D extent, std::uint64_t generation);
    ~Frame_Target();

    Frame_Target(const Frame_Target&) = delete;
    Frame_Target& operator=(const Frame_Target&) = delete;

    [[nodiscard]] bool begin();
    [[nodiscard]] DvzStreamFrame stream_frame() const;
    [[nodiscard]] bool submit_and_wait();
    void abort() noexcept;
    void wait() noexcept;

private:
    friend class Frame_Target_Pool;

    void transition_to_color_attachment();
    void destroy() noexcept;

    DvzGpuCtx* gpu_context_{};
    Extent_2D extent_;
    std::uint64_t generation_{};
    DvzImages* image_{};
    DvzImageViews* image_view_{};
    DvzCommands* commands_{};
    DvzFence* fence_{};
    DvzSubmit* submit_{};
    VkImageLayout completed_layout_{VK_IMAGE_LAYOUT_UNDEFINED};
    bool recording_{};
    bool submitted_{};
};

class Frame_Target_Pool final {
public:
    Frame_Target_Pool(DvzGpuCtx* gpu_context, std::size_t slot_count);
    ~Frame_Target_Pool();

    Frame_Target_Pool(const Frame_Target_Pool&) = delete;
    Frame_Target_Pool& operator=(const Frame_Target_Pool&) = delete;

    [[nodiscard]] Frame_Target& acquire(std::uint64_t frame_sequence, Extent_2D extent);
    void external_target_replaced();
    void runtime_released_targets();
    void wait_all() noexcept;

private:
    [[nodiscard]] std::vector<std::unique_ptr<Frame_Target>> make_generation(Extent_2D extent);

    DvzGpuCtx* gpu_context_{};
    std::size_t slot_count_{};
    std::uint64_t generation_{};
    std::vector<std::unique_ptr<Frame_Target>> targets_;
    std::vector<std::unique_ptr<Frame_Target>> retired_targets_;
};

} // namespace renderive::detail
