#pragma once

#include "SystemFrameTypes.h"

#include <chrono>
#include <condition_variable>
#include <deque>
#include <mutex>
#include <vector>

class SystemFrameExchange
{
public:
	SystemFrameExchange() = default;
	explicit SystemFrameExchange(const SystemFrameExchangeConfig& config);

	void Configure(const SystemFrameExchangeConfig& config);
	SystemFrameExchangeConfig Config() const;

	bool AcquireForRender(SystemFrame& frame);
	bool PublishCompleted(const SystemFrame& frame);
	bool ConsumeCompletedForSchedule(SystemFrame& frame);
	bool ReleaseScheduledByBytes(void* bytes);
	bool TryAcquireLatestForPreview(SystemFrame& frame);
	bool ReleasePreviewFrame(const SystemFrame& frame);
	bool WaitForCompletedDepth(std::size_t targetDepth, std::chrono::milliseconds timeout);
	bool WaitForStableCompletedDepth(
		std::size_t targetDepth,
		std::chrono::milliseconds stableDuration,
		std::chrono::milliseconds timeout);
	void Clear();

	SystemFrameExchangeMetrics Metrics() const;

private:
	struct Slot
	{
		std::vector<unsigned char> bytes;
		SystemFrameSlotState state = SystemFrameSlotState::Free;
		uint64_t generation = 1;
		uint64_t frameIndex = 0;
		std::size_t previewReaders = 0;
	};

	bool AcquireFreeLocked(SystemFrame& frame);
	bool DropOldestCompletedLocked();
	bool ReleaseFreeIfUnreferencedLocked(Slot& slot);
	void TrimCompletedLocked();
	bool IsValidLocked(const SystemFrame& frame) const;
	void FillFrameLocked(std::size_t index, SystemFrame& frame);
	std::size_t CompletedCountLocked() const;
	std::size_t FrameByteCount() const;

	mutable std::mutex mMutex;
	std::condition_variable mCondition;
	SystemFrameExchangeConfig mConfig;
	std::vector<Slot> mSlots;
	std::deque<std::size_t> mCompletedIndices;
	SystemFrameExchangeMetrics mCounters;
};
