#pragma once

#include "VideoIOTypes.h"

#include <cstddef>
#include <cstdint>
#include <deque>
#include <mutex>
#include <vector>

enum class RenderCommandResetScope
{
	None,
	ShaderFeedbackOnly,
	TemporalHistoryOnly,
	TemporalHistoryAndFeedback
};

struct RenderPreviewPresentRequest
{
	unsigned outputFrameWidth = 0;
	unsigned outputFrameHeight = 0;
};

struct RenderScreenshotCaptureRequest
{
	unsigned width = 0;
	unsigned height = 0;
};

struct RenderInputUploadRequest
{
	VideoIOFrame inputFrame;
	VideoIOState videoState;
	std::vector<unsigned char> ownedBytes;
};

struct RenderOutputFrameRequest
{
	VideoIOState videoState;
	VideoIOCompletion completion;
};

struct RenderCommandQueueMetrics
{
	std::size_t depth = 0;
	uint64_t enqueuedCount = 0;
	uint64_t coalescedCount = 0;
};

class RenderCommandQueue
{
public:
	void RequestPreviewPresent(const RenderPreviewPresentRequest& request);
	bool TryTakePreviewPresent(RenderPreviewPresentRequest& request);

	void RequestScreenshotCapture(const RenderScreenshotCaptureRequest& request);
	bool TryTakeScreenshotCapture(RenderScreenshotCaptureRequest& request);

	void RequestInputUpload(const RenderInputUploadRequest& request);
	bool TryTakeInputUpload(RenderInputUploadRequest& request);

	void RequestOutputFrame(const RenderOutputFrameRequest& request);
	bool TryTakeOutputFrame(RenderOutputFrameRequest& request);

	void RequestRenderReset(RenderCommandResetScope scope);
	bool TryTakeRenderReset(RenderCommandResetScope& scope);

	RenderCommandQueueMetrics GetMetrics() const;

private:
	static RenderCommandResetScope MergeResetScopes(RenderCommandResetScope current, RenderCommandResetScope requested);

	mutable std::mutex mMutex;
	bool mHasPreviewPresentRequest = false;
	RenderPreviewPresentRequest mPreviewPresentRequest;
	bool mHasScreenshotCaptureRequest = false;
	RenderScreenshotCaptureRequest mScreenshotCaptureRequest;
	bool mHasInputUploadRequest = false;
	RenderInputUploadRequest mInputUploadRequest;
	std::deque<RenderOutputFrameRequest> mOutputFrameRequests;
	RenderCommandResetScope mRenderResetScope = RenderCommandResetScope::None;
	uint64_t mEnqueuedCount = 0;
	uint64_t mCoalescedCount = 0;
};
