#include "DeckLinkDisplayMode.h"

bool DeckLinkDisplayModeForVideoFormat(const VideoFormat& videoMode, BMDDisplayMode& displayMode)
{
	struct ModeOption
	{
		const char* displayName;
		BMDDisplayMode mode;
	};

	static const ModeOption options[] =
	{
		{ "720p50", bmdModeHD720p50 },
		{ "720p59.94", bmdModeHD720p5994 },
		{ "720p60", bmdModeHD720p60 },
		{ "1080i50", bmdModeHD1080i50 },
		{ "1080i59.94", bmdModeHD1080i5994 },
		{ "1080i60", bmdModeHD1080i6000 },
		{ "1080p23.98", bmdModeHD1080p2398 },
		{ "1080p24", bmdModeHD1080p24 },
		{ "1080p25", bmdModeHD1080p25 },
		{ "1080p29.97", bmdModeHD1080p2997 },
		{ "1080p30", bmdModeHD1080p30 },
		{ "1080p50", bmdModeHD1080p50 },
		{ "1080p59.94", bmdModeHD1080p5994 },
		{ "1080p60", bmdModeHD1080p6000 },
		{ "2160p23.98", bmdMode4K2160p2398 },
		{ "2160p24", bmdMode4K2160p24 },
		{ "2160p25", bmdMode4K2160p25 },
		{ "2160p29.97", bmdMode4K2160p2997 },
		{ "2160p30", bmdMode4K2160p30 },
		{ "2160p50", bmdMode4K2160p50 },
		{ "2160p59.94", bmdMode4K2160p5994 },
		{ "2160p60", bmdMode4K2160p60 }
	};

	const std::string displayToken = NormalizeModeToken(videoMode.displayName);
	for (const ModeOption& option : options)
	{
		if (displayToken == NormalizeModeToken(option.displayName))
		{
			displayMode = option.mode;
			return true;
		}
	}

	return false;
}

bool ResolveConfiguredDisplayMode(const std::string& videoFormat, const std::string& frameRate, BMDDisplayMode& displayMode, std::string& displayModeName)
{
	VideoFormat videoMode;
	if (!ResolveConfiguredVideoFormat(videoFormat, frameRate, videoMode))
		return false;

	if (!DeckLinkDisplayModeForVideoFormat(videoMode, displayMode))
		return false;

	displayModeName = videoMode.displayName;
	return true;
}

double FrameDurationMillisecondsFromDisplayMode(BMDDisplayMode displayMode, double fallbackMilliseconds)
{
	struct ModeRate
	{
		BMDDisplayMode mode;
		int64_t frameDuration;
		int64_t timeScale;
	};

	static const ModeRate rates[] =
	{
		{ bmdModeHD720p50, 1, 50 },
		{ bmdModeHD720p5994, 1001, 60000 },
		{ bmdModeHD720p60, 1, 60 },
		{ bmdModeHD1080i50, 1, 25 },
		{ bmdModeHD1080i5994, 1001, 30000 },
		{ bmdModeHD1080i6000, 1, 30 },
		{ bmdModeHD1080p2398, 1001, 24000 },
		{ bmdModeHD1080p24, 1, 24 },
		{ bmdModeHD1080p25, 1, 25 },
		{ bmdModeHD1080p2997, 1001, 30000 },
		{ bmdModeHD1080p30, 1, 30 },
		{ bmdModeHD1080p50, 1, 50 },
		{ bmdModeHD1080p5994, 1001, 60000 },
		{ bmdModeHD1080p6000, 1, 60 },
		{ bmdMode4K2160p2398, 1001, 24000 },
		{ bmdMode4K2160p24, 1, 24 },
		{ bmdMode4K2160p25, 1, 25 },
		{ bmdMode4K2160p2997, 1001, 30000 },
		{ bmdMode4K2160p30, 1, 30 },
		{ bmdMode4K2160p50, 1, 50 },
		{ bmdMode4K2160p5994, 1001, 60000 },
		{ bmdMode4K2160p60, 1, 60 }
	};

	for (const ModeRate& rate : rates)
	{
		if (rate.mode == displayMode && rate.timeScale > 0)
			return (static_cast<double>(rate.frameDuration) * 1000.0) / static_cast<double>(rate.timeScale);
	}

	return fallbackMilliseconds;
}

bool FindDeckLinkDisplayMode(IDeckLinkDisplayModeIterator* iterator, BMDDisplayMode targetMode, IDeckLinkDisplayMode** foundMode)
{
	if (!iterator || !foundMode)
		return false;

	*foundMode = NULL;
	IDeckLinkDisplayMode* candidate = NULL;
	while (iterator->Next(&candidate) == S_OK)
	{
		if (candidate->GetDisplayMode() == targetMode)
		{
			*foundMode = candidate;
			return true;
		}

		candidate->Release();
		candidate = NULL;
	}

	return false;
}
