#include "VideoIOFormat.h"
#include "DeckLinkVideoIOFormat.h"

#include <cmath>
#include <iostream>

namespace
{
int gFailures = 0;

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

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

void TestPreferredFormatSelection()
{
	Expect(ChoosePreferredVideoIOFormat(true) == VideoIOPixelFormat::V210, "10-bit is preferred when supported");
	Expect(ChoosePreferredVideoIOFormat(false) == VideoIOPixelFormat::Uyvy8, "8-bit is used as fallback");
	Expect(DeckLinkPixelFormatForVideoIO(VideoIOPixelFormat::V210) == bmdFormat10BitYUV, "v210 maps to DeckLink 10-bit YUV");
	Expect(DeckLinkPixelFormatForVideoIO(VideoIOPixelFormat::Yuva10) == bmdFormat10BitYUVA, "Ay10 maps to DeckLink 10-bit YUVA");
	Expect(DeckLinkPixelFormatForVideoIO(VideoIOPixelFormat::Uyvy8) == bmdFormat8BitYUV, "UYVY maps to DeckLink 8-bit YUV");
	Expect(DeckLinkPixelFormatForVideoIO(VideoIOPixelFormat::Bgra8) == bmdFormat8BitBGRA, "BGRA maps to DeckLink 8-bit BGRA");
}

void TestRowByteHelpers()
{
	Expect(MinimumV210RowBytes(1920) == 5120, "1920-wide v210 active row bytes");
	Expect(MinimumV210RowBytes(1280) == 3424, "1280-wide v210 active row bytes rounds up to six-pixel group");
	Expect(MinimumV210RowBytes(3840) == 10240, "3840-wide v210 active row bytes");
	Expect(MinimumYuva10RowBytes(1920) == 7680, "1920-wide Ay10 row bytes");
	Expect(MinimumYuva10RowBytes(1919) == 7680, "Ay10 row bytes round up to 64-pixel boundary");
	Expect(MinimumYuva10RowBytes(3840) == 15360, "3840-wide Ay10 row bytes");
	Expect(PackedTextureWidthFromRowBytes(5120) == 1280, "packed texture width is row bytes divided into RGBA byte texels");
	Expect(ActiveV210WordsForWidth(1920) == 1280, "active v210 words match 1920 width");
	Expect(VideoIORowBytes(VideoIOPixelFormat::Uyvy8, 1920) == 3840, "UYVY row bytes");
	Expect(VideoIORowBytes(VideoIOPixelFormat::Bgra8, 1920) == 7680, "BGRA row bytes");
	Expect(VideoIORowBytes(VideoIOPixelFormat::V210, 1920) == 5120, "v210 row bytes");
	Expect(VideoIORowBytes(VideoIOPixelFormat::Yuva10, 1920) == 7680, "Ay10 row bytes");
}

void TestV210PackUnpack()
{
	V210SixPixelBlock input;
	input.y = { 64, 128, 256, 512, 768, 940 };
	input.cb = { 64, 512, 960 };
	input.cr = { 960, 512, 64 };

	const V210SixPixelBlock output = UnpackV210Block(PackV210Block(input));
	Expect(output.y == input.y, "v210 luma survives pack/unpack");
	Expect(output.cb == input.cb, "v210 Cb survives pack/unpack");
	Expect(output.cr == input.cr, "v210 Cr survives pack/unpack");
}

void TestRec709LegalRanges()
{
	const V210CodeValues black = Rec709RgbToLegalV210(0.0f, 0.0f, 0.0f);
	const V210CodeValues grey = Rec709RgbToLegalV210(0.5f, 0.5f, 0.5f);
	const V210CodeValues white = Rec709RgbToLegalV210(1.0f, 1.0f, 1.0f);

	Expect(black.y == 64, "black maps to legal-range 10-bit luma minimum");
	Expect(white.y == 940, "white maps to legal-range 10-bit luma maximum");
	Expect(std::abs(static_cast<int>(grey.y) - 502) <= 1, "middle grey maps near legal-range midpoint");
	Expect(black.cb == 512 && black.cr == 512, "black keeps neutral chroma");
	Expect(grey.cb == 512 && grey.cr == 512, "grey keeps neutral chroma");
	Expect(white.cb == 512 && white.cr == 512, "white keeps neutral chroma");
}
}

int main()
{
	TestPreferredFormatSelection();
	TestRowByteHelpers();
	TestV210PackUnpack();
	TestRec709LegalRanges();

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

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