#include "DeckLinkDisplayMode.h"

#include <cctype>

std::string NormalizeModeToken(const std::string& value)
{
	std::string normalized;
	for (unsigned char ch : value)
	{
		if (std::isalnum(ch))
			normalized.push_back(static_cast<char>(std::tolower(ch)));
	}
	return normalized;
}

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

	displayMode = videoMode.displayMode;
	displayModeName = videoMode.displayName;
	return true;
}

bool ResolveConfiguredVideoFormat(const std::string& videoFormat, const std::string& frameRate, VideoFormat& videoMode)
{
	const std::string formatToken = NormalizeModeToken(videoFormat);
	const std::string frameToken = NormalizeModeToken(frameRate);
	const std::string combinedToken = formatToken + frameToken;

	struct ModeOption
	{
		const char* token;
		BMDDisplayMode mode;
		const char* displayName;
	};

	static const ModeOption options[] =
	{
		{ "720p50", bmdModeHD720p50, "720p50" },
		{ "hd720p50", bmdModeHD720p50, "720p50" },
		{ "720p5994", bmdModeHD720p5994, "720p59.94" },
		{ "hd720p5994", bmdModeHD720p5994, "720p59.94" },
		{ "720p60", bmdModeHD720p60, "720p60" },
		{ "hd720p60", bmdModeHD720p60, "720p60" },
		{ "1080i50", bmdModeHD1080i50, "1080i50" },
		{ "hd1080i50", bmdModeHD1080i50, "1080i50" },
		{ "1080i5994", bmdModeHD1080i5994, "1080i59.94" },
		{ "hd1080i5994", bmdModeHD1080i5994, "1080i59.94" },
		{ "1080i60", bmdModeHD1080i6000, "1080i60" },
		{ "hd1080i60", bmdModeHD1080i6000, "1080i60" },
		{ "1080p2398", bmdModeHD1080p2398, "1080p23.98" },
		{ "hd1080p2398", bmdModeHD1080p2398, "1080p23.98" },
		{ "1080p24", bmdModeHD1080p24, "1080p24" },
		{ "hd1080p24", bmdModeHD1080p24, "1080p24" },
		{ "1080p25", bmdModeHD1080p25, "1080p25" },
		{ "hd1080p25", bmdModeHD1080p25, "1080p25" },
		{ "1080p2997", bmdModeHD1080p2997, "1080p29.97" },
		{ "hd1080p2997", bmdModeHD1080p2997, "1080p29.97" },
		{ "1080p30", bmdModeHD1080p30, "1080p30" },
		{ "hd1080p30", bmdModeHD1080p30, "1080p30" },
		{ "1080p50", bmdModeHD1080p50, "1080p50" },
		{ "hd1080p50", bmdModeHD1080p50, "1080p50" },
		{ "1080p5994", bmdModeHD1080p5994, "1080p59.94" },
		{ "hd1080p5994", bmdModeHD1080p5994, "1080p59.94" },
		{ "1080p60", bmdModeHD1080p6000, "1080p60" },
		{ "hd1080p60", bmdModeHD1080p6000, "1080p60" },
		{ "2160p2398", bmdMode4K2160p2398, "2160p23.98" },
		{ "4k2160p2398", bmdMode4K2160p2398, "2160p23.98" },
		{ "2160p24", bmdMode4K2160p24, "2160p24" },
		{ "4k2160p24", bmdMode4K2160p24, "2160p24" },
		{ "2160p25", bmdMode4K2160p25, "2160p25" },
		{ "4k2160p25", bmdMode4K2160p25, "2160p25" },
		{ "2160p2997", bmdMode4K2160p2997, "2160p29.97" },
		{ "4k2160p2997", bmdMode4K2160p2997, "2160p29.97" },
		{ "2160p30", bmdMode4K2160p30, "2160p30" },
		{ "4k2160p30", bmdMode4K2160p30, "2160p30" },
		{ "2160p50", bmdMode4K2160p50, "2160p50" },
		{ "4k2160p50", bmdMode4K2160p50, "2160p50" },
		{ "2160p5994", bmdMode4K2160p5994, "2160p59.94" },
		{ "4k2160p5994", bmdMode4K2160p5994, "2160p59.94" },
		{ "2160p60", bmdMode4K2160p60, "2160p60" },
		{ "4k2160p60", bmdMode4K2160p60, "2160p60" }
	};

	for (const ModeOption& option : options)
	{
		if (combinedToken == option.token || (frameToken.empty() && formatToken == option.token))
		{
			videoMode.displayMode = option.mode;
			videoMode.displayName = option.displayName;
			return true;
		}
	}

	return false;
}

bool ResolveConfiguredVideoFormats(
	const std::string& inputVideoFormat,
	const std::string& inputFrameRate,
	const std::string& outputVideoFormat,
	const std::string& outputFrameRate,
	VideoFormatSelection& videoModes,
	std::string& error)
{
	if (!ResolveConfiguredVideoFormat(inputVideoFormat, inputFrameRate, videoModes.input))
	{
		error = "Unsupported DeckLink inputVideoFormat/inputFrameRate in config/runtime-host.json: " +
			inputVideoFormat + " / " + inputFrameRate;
		return false;
	}

	if (!ResolveConfiguredVideoFormat(outputVideoFormat, outputFrameRate, videoModes.output))
	{
		error = "Unsupported DeckLink outputVideoFormat/outputFrameRate in config/runtime-host.json: " +
			outputVideoFormat + " / " + outputFrameRate;
		return false;
	}

	return true;
}

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;
}
