#include "RenderOutputQueue.h"

#include <iostream>

namespace
{
int gFailures = 0;

void Expect(bool condition, const char* message)
{
	if (condition)
		return;

	std::cerr << "FAIL: " << message << "\n";
	++gFailures;
}

RenderOutputFrame MakeFrame(uint64_t index)
{
	RenderOutputFrame frame;
	frame.frameIndex = index;
	frame.frame.nativeFrame = reinterpret_cast<void*>(static_cast<uintptr_t>(index + 1));
	return frame;
}

void TestQueuePreservesOrdering()
{
	VideoPlayoutPolicy policy;
	policy.maxReadyFrames = 3;
	RenderOutputQueue queue(policy);

	Expect(queue.Push(MakeFrame(1)), "first ready frame pushes");
	Expect(queue.Push(MakeFrame(2)), "second ready frame pushes");

	RenderOutputFrame frame;
	Expect(queue.TryPop(frame), "first ready frame pops");
	Expect(frame.frameIndex == 1, "queue pops first frame first");
	Expect(queue.TryPop(frame), "second ready frame pops");
	Expect(frame.frameIndex == 2, "queue pops second frame second");
}

void TestBoundedQueueDropsOldestFrame()
{
	VideoPlayoutPolicy policy;
	policy.maxReadyFrames = 2;
	RenderOutputQueue queue(policy);

	queue.Push(MakeFrame(1));
	queue.Push(MakeFrame(2));
	queue.Push(MakeFrame(3));

	RenderOutputQueueMetrics metrics = queue.GetMetrics();
	Expect(metrics.depth == 2, "bounded queue depth stays at capacity");
	Expect(metrics.droppedCount == 1, "bounded queue counts dropped oldest frame");

	RenderOutputFrame frame;
	Expect(queue.TryPop(frame), "bounded queue pops after drop");
	Expect(frame.frameIndex == 2, "oldest frame was dropped when queue overflowed");
}

void TestUnderrunIsCounted()
{
	RenderOutputQueue queue;
	RenderOutputFrame frame;
	Expect(!queue.TryPop(frame), "empty queue reports underrun");

	RenderOutputQueueMetrics metrics = queue.GetMetrics();
	Expect(metrics.underrunCount == 1, "empty pop increments underrun count");
}

void TestConfigureShrinksDepthToNewCapacity()
{
	VideoPlayoutPolicy policy;
	policy.maxReadyFrames = 4;
	RenderOutputQueue queue(policy);
	queue.Push(MakeFrame(1));
	queue.Push(MakeFrame(2));
	queue.Push(MakeFrame(3));

	VideoPlayoutPolicy smallerPolicy;
	smallerPolicy.targetReadyFrames = 1;
	smallerPolicy.maxReadyFrames = 1;
	queue.Configure(smallerPolicy);

	RenderOutputQueueMetrics metrics = queue.GetMetrics();
	Expect(metrics.depth == 1, "configure trims queue to new capacity");
	Expect(metrics.droppedCount == 2, "configure counts trimmed frames as drops");

	RenderOutputFrame frame;
	Expect(queue.TryPop(frame), "trimmed queue still has newest frame");
	Expect(frame.frameIndex == 3, "configure keeps newest ready frame");
}
}

int main()
{
	TestQueuePreservesOrdering();
	TestBoundedQueueDropsOldestFrame();
	TestUnderrunIsCounted();
	TestConfigureShrinksDepthToNewCapacity();

	if (gFailures != 0)
	{
		std::cerr << gFailures << " render output queue test failure(s).\n";
		return 1;
	}

	std::cout << "RenderOutputQueue tests passed.\n";
	return 0;
}
