#pragma once

#include "VideoIOFormat.h"

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

enum class InputFrameSlotState
{
	Free,
	Ready,
	Reading
};

struct InputFrameMailboxConfig
{
	unsigned width = 0;
	unsigned height = 0;
	VideoIOPixelFormat pixelFormat = VideoIOPixelFormat::Bgra8;
	unsigned rowBytes = 0;
	std::size_t capacity = 0;
	std::size_t maxReadyFrames = 0;
};

struct InputFrame
{
	const void* bytes = nullptr;
	long rowBytes = 0;
	unsigned width = 0;
	unsigned height = 0;
	VideoIOPixelFormat pixelFormat = VideoIOPixelFormat::Bgra8;
	std::size_t index = 0;
	uint64_t generation = 0;
	uint64_t frameIndex = 0;
};

struct InputFrameMailboxMetrics
{
	std::size_t capacity = 0;
	std::size_t freeCount = 0;
	std::size_t readyCount = 0;
	std::size_t readingCount = 0;
	uint64_t submittedFrames = 0;
	uint64_t consumedFrames = 0;
	uint64_t droppedReadyFrames = 0;
	uint64_t submitMisses = 0;
	uint64_t consumeMisses = 0;
	uint64_t latestFrameIndex = 0;
	bool hasSubmittedFrame = false;
	double latestFrameAgeMilliseconds = 0.0;
};

class InputFrameMailbox
{
public:
	InputFrameMailbox() = default;
	explicit InputFrameMailbox(const InputFrameMailboxConfig& config);

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

	bool SubmitFrame(const void* bytes, unsigned rowBytes, uint64_t frameIndex);
	bool TryAcquireOldest(InputFrame& frame);
	bool Release(const InputFrame& frame);
	void Clear();
	InputFrameMailboxMetrics Metrics() const;

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

	bool IsValidLocked(const InputFrame& frame) const;
	void FillFrameLocked(std::size_t index, InputFrame& frame) const;
	bool DropOldestReadyLocked();
	void TrimReadyFramesLocked();
	std::size_t FrameByteCount() const;

	mutable std::mutex mMutex;
	InputFrameMailboxConfig mConfig;
	std::vector<Slot> mSlots;
	std::deque<std::size_t> mReadyIndices;
	InputFrameMailboxMetrics mCounters;
	std::chrono::steady_clock::time_point mLatestSubmitTime;
};
