#pragma once

#include "PboReadbackRing.h"
#include "VideoIOFormat.h"

#include <cstddef>
#include <cstdint>
#include <functional>

struct SystemFrame;

class Bgra8ReadbackPipeline
{
public:
	using RenderCallback = std::function<void(uint64_t frameIndex)>;
	using AcquireFrameCallback = std::function<bool(SystemFrame& frame)>;
	using PublishFrameCallback = std::function<bool(const SystemFrame& frame)>;
	using CounterCallback = std::function<void()>;

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

	bool Initialize(unsigned width, unsigned height, std::size_t pboDepth);
	void Shutdown();

	bool RenderAndQueue(uint64_t frameIndex, const RenderCallback& renderFrame);
	void ConsumeCompleted(
		const AcquireFrameCallback& acquireFrame,
		const PublishFrameCallback& publishFrame,
		const CounterCallback& onAcquireMiss = {},
		const CounterCallback& onCompleted = {});

	GLuint Framebuffer() const { return mFramebuffer; }
	unsigned Width() const { return mWidth; }
	unsigned Height() const { return mHeight; }
	unsigned RowBytes() const { return mRowBytes; }
	VideoIOPixelFormat PixelFormat() const { return VideoIOPixelFormat::Bgra8; }
	uint64_t PboQueueMisses() const { return mPboRing.QueueMisses(); }
	double LastRenderFrameMilliseconds() const { return mLastRenderFrameMilliseconds; }
	double LastReadbackQueueMilliseconds() const { return mLastReadbackQueueMilliseconds; }
	double LastCompletedReadbackCopyMilliseconds() const { return mLastCompletedReadbackCopyMilliseconds; }

private:
	bool CreateRenderTarget();
	void DestroyRenderTarget();

	unsigned mWidth = 0;
	unsigned mHeight = 0;
	unsigned mRowBytes = 0;
	GLuint mFramebuffer = 0;
	GLuint mTexture = 0;
	double mLastRenderFrameMilliseconds = 0.0;
	double mLastReadbackQueueMilliseconds = 0.0;
	double mLastCompletedReadbackCopyMilliseconds = 0.0;
	PboReadbackRing mPboRing;
};
