#include "RenderOutputQueue.h"

RenderOutputQueue::RenderOutputQueue(const VideoPlayoutPolicy& policy) :
	mPolicy(NormalizeVideoPlayoutPolicy(policy))
{
}

void RenderOutputQueue::Configure(const VideoPlayoutPolicy& policy)
{
	std::lock_guard<std::mutex> lock(mMutex);
	mPolicy = NormalizeVideoPlayoutPolicy(policy);
	while (mReadyFrames.size() > CapacityLocked())
	{
		ReleaseFrame(mReadyFrames.front());
		mReadyFrames.pop_front();
		++mDroppedCount;
	}
}

bool RenderOutputQueue::Push(RenderOutputFrame frame)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (mReadyFrames.size() >= CapacityLocked())
	{
		ReleaseFrame(mReadyFrames.front());
		mReadyFrames.pop_front();
		++mDroppedCount;
	}

	mReadyFrames.push_back(frame);
	++mPushedCount;
	return true;
}

bool RenderOutputQueue::TryPop(RenderOutputFrame& frame)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (mReadyFrames.empty())
	{
		++mUnderrunCount;
		return false;
	}

	frame = mReadyFrames.front();
	mReadyFrames.pop_front();
	++mPoppedCount;
	return true;
}

bool RenderOutputQueue::DropOldestFrame()
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (mReadyFrames.empty())
		return false;

	ReleaseFrame(mReadyFrames.front());
	mReadyFrames.pop_front();
	++mDroppedCount;
	return true;
}

void RenderOutputQueue::Clear()
{
	std::lock_guard<std::mutex> lock(mMutex);
	for (RenderOutputFrame& frame : mReadyFrames)
		ReleaseFrame(frame);
	mReadyFrames.clear();
}

RenderOutputQueueMetrics RenderOutputQueue::GetMetrics() const
{
	std::lock_guard<std::mutex> lock(mMutex);
	RenderOutputQueueMetrics metrics;
	metrics.depth = mReadyFrames.size();
	metrics.capacity = CapacityLocked();
	metrics.pushedCount = mPushedCount;
	metrics.poppedCount = mPoppedCount;
	metrics.droppedCount = mDroppedCount;
	metrics.underrunCount = mUnderrunCount;
	return metrics;
}

std::size_t RenderOutputQueue::CapacityLocked() const
{
	return static_cast<std::size_t>(mPolicy.maxReadyFrames);
}

void RenderOutputQueue::ReleaseFrame(RenderOutputFrame& frame)
{
	if (frame.releaseFrame)
		frame.releaseFrame(frame.frame);
	frame.releaseFrame = {};
}
