#pragma once

#include "GLExtensions.h"

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

class PboReadbackRing
{
public:
	struct CompletedReadback
	{
		GLuint pbo = 0;
		uint64_t frameIndex = 0;
		std::size_t byteCount = 0;
	};

	PboReadbackRing() = default;
	PboReadbackRing(const PboReadbackRing&) = delete;
	PboReadbackRing& operator=(const PboReadbackRing&) = delete;
	~PboReadbackRing();

	bool Initialize(std::size_t depth, std::size_t byteCount);
	void Shutdown();

	bool QueueReadback(GLuint framebuffer, unsigned width, unsigned height, uint64_t frameIndex);
	bool TryAcquireCompleted(CompletedReadback& readback);
	void ReleaseCompleted(const CompletedReadback& readback);
	void DrainCompleted();

	std::size_t Depth() const { return mSlots.size(); }
	uint64_t QueueMisses() const { return mQueueMisses; }

private:
	struct Slot
	{
		GLuint pbo = 0;
		GLsync fence = nullptr;
		bool inFlight = false;
		bool acquired = false;
		uint64_t frameIndex = 0;
	};

	void ResetSlot(Slot& slot);

	std::vector<Slot> mSlots;
	std::size_t mWriteIndex = 0;
	std::size_t mReadIndex = 0;
	std::size_t mByteCount = 0;
	uint64_t mQueueMisses = 0;
};
