#include "RenderCommandQueue.h"

void RenderCommandQueue::RequestPreviewPresent(const RenderPreviewPresentRequest& request)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (mHasPreviewPresentRequest)
		++mCoalescedCount;
	else
		++mEnqueuedCount;

	mPreviewPresentRequest = request;
	mHasPreviewPresentRequest = true;
}

bool RenderCommandQueue::TryTakePreviewPresent(RenderPreviewPresentRequest& request)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (!mHasPreviewPresentRequest)
		return false;

	request = mPreviewPresentRequest;
	mPreviewPresentRequest = {};
	mHasPreviewPresentRequest = false;
	return true;
}

void RenderCommandQueue::RequestScreenshotCapture(const RenderScreenshotCaptureRequest& request)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (mHasScreenshotCaptureRequest)
		++mCoalescedCount;
	else
		++mEnqueuedCount;

	mScreenshotCaptureRequest = request;
	mHasScreenshotCaptureRequest = true;
}

bool RenderCommandQueue::TryTakeScreenshotCapture(RenderScreenshotCaptureRequest& request)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (!mHasScreenshotCaptureRequest)
		return false;

	request = mScreenshotCaptureRequest;
	mScreenshotCaptureRequest = {};
	mHasScreenshotCaptureRequest = false;
	return true;
}

void RenderCommandQueue::RequestInputUpload(const RenderInputUploadRequest& request)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (mHasInputUploadRequest)
		++mCoalescedCount;
	else
		++mEnqueuedCount;

	mInputUploadRequest = request;
	mHasInputUploadRequest = true;
}

bool RenderCommandQueue::TryTakeInputUpload(RenderInputUploadRequest& request)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (!mHasInputUploadRequest)
		return false;

	request = mInputUploadRequest;
	mInputUploadRequest = {};
	mHasInputUploadRequest = false;
	return true;
}

void RenderCommandQueue::RequestOutputFrame(const RenderOutputFrameRequest& request)
{
	std::lock_guard<std::mutex> lock(mMutex);
	mOutputFrameRequests.push_back(request);
	++mEnqueuedCount;
}

bool RenderCommandQueue::TryTakeOutputFrame(RenderOutputFrameRequest& request)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (mOutputFrameRequests.empty())
		return false;

	request = mOutputFrameRequests.front();
	mOutputFrameRequests.pop_front();
	return true;
}

void RenderCommandQueue::RequestRenderReset(RenderCommandResetScope scope)
{
	if (scope == RenderCommandResetScope::None)
		return;

	std::lock_guard<std::mutex> lock(mMutex);
	if (mRenderResetScope != RenderCommandResetScope::None)
		++mCoalescedCount;
	else
		++mEnqueuedCount;

	mRenderResetScope = MergeResetScopes(mRenderResetScope, scope);
}

bool RenderCommandQueue::TryTakeRenderReset(RenderCommandResetScope& scope)
{
	std::lock_guard<std::mutex> lock(mMutex);
	if (mRenderResetScope == RenderCommandResetScope::None)
		return false;

	scope = mRenderResetScope;
	mRenderResetScope = RenderCommandResetScope::None;
	return true;
}

RenderCommandQueueMetrics RenderCommandQueue::GetMetrics() const
{
	std::lock_guard<std::mutex> lock(mMutex);
	RenderCommandQueueMetrics metrics;
	metrics.depth =
		(mHasPreviewPresentRequest ? 1u : 0u) +
		(mHasScreenshotCaptureRequest ? 1u : 0u) +
		(mHasInputUploadRequest ? 1u : 0u) +
		mOutputFrameRequests.size() +
		(mRenderResetScope != RenderCommandResetScope::None ? 1u : 0u);
	metrics.enqueuedCount = mEnqueuedCount;
	metrics.coalescedCount = mCoalescedCount;
	return metrics;
}

RenderCommandResetScope RenderCommandQueue::MergeResetScopes(RenderCommandResetScope current, RenderCommandResetScope requested)
{
	if (current == RenderCommandResetScope::TemporalHistoryAndFeedback ||
		requested == RenderCommandResetScope::TemporalHistoryAndFeedback)
	{
		return RenderCommandResetScope::TemporalHistoryAndFeedback;
	}

	if ((current == RenderCommandResetScope::TemporalHistoryOnly && requested == RenderCommandResetScope::ShaderFeedbackOnly) ||
		(current == RenderCommandResetScope::ShaderFeedbackOnly && requested == RenderCommandResetScope::TemporalHistoryOnly))
	{
		return RenderCommandResetScope::TemporalHistoryAndFeedback;
	}

	if (current == RenderCommandResetScope::TemporalHistoryOnly ||
		requested == RenderCommandResetScope::TemporalHistoryOnly)
	{
		return RenderCommandResetScope::TemporalHistoryOnly;
	}

	if (current == RenderCommandResetScope::ShaderFeedbackOnly ||
		requested == RenderCommandResetScope::ShaderFeedbackOnly)
	{
		return RenderCommandResetScope::ShaderFeedbackOnly;
	}

	return RenderCommandResetScope::None;
}
