#include "DeckLinkOutput.h"

#include "VideoIOFormat.h"

namespace RenderCadenceCompositor
{
DeckLinkOutput::~DeckLinkOutput()
{
	ReleaseResources();
}

bool DeckLinkOutput::Initialize(const DeckLinkOutputConfig& config, CompletionCallback completionCallback, std::string& error)
{
	mConfig = config;
	mCompletionCallback = completionCallback;

	VideoFormatSelection formats;
	formats.output = config.outputVideoMode;
	if (!mSession.DiscoverDevicesAndModes(formats, error))
		return false;
	if (!mSession.SelectPreferredFormats(formats, config.outputAlphaRequired, error))
		return false;
	if (!mSession.ConfigureOutput(
		[this](const VideoIOCompletion& completion) { HandleCompletion(completion); },
		formats.output,
		config.externalKeyingEnabled,
		error))
	{
		return false;
	}
	if (!mSession.PrepareOutputSchedule())
	{
		error = "DeckLink output schedule preparation failed.";
		return false;
	}
	return true;
}

bool DeckLinkOutput::StartScheduledPlayback(std::string& error)
{
	if (mSession.StartScheduledPlayback())
		return true;
	error = "DeckLink scheduled playback failed to start.";
	return false;
}

bool DeckLinkOutput::ScheduleFrame(const VideoIOOutputFrame& frame)
{
	return mSession.ScheduleOutputFrame(frame);
}

void DeckLinkOutput::Stop()
{
	mSession.Stop();
}

void DeckLinkOutput::ReleaseResources()
{
	mSession.ReleaseResources();
}

const VideoIOState& DeckLinkOutput::State() const
{
	return mSession.State();
}

DeckLinkOutputMetrics DeckLinkOutput::Metrics() const
{
	DeckLinkOutputMetrics metrics;
	metrics.completions = mCompletions.load();
	metrics.displayedLate = mDisplayedLate.load();
	metrics.dropped = mDropped.load();
	metrics.flushed = mFlushed.load();

	const VideoIOState& state = mSession.State();
	metrics.scheduleFailures = state.deckLinkScheduleFailureCount;
	metrics.actualBufferedFramesAvailable = state.actualDeckLinkBufferedFramesAvailable;
	metrics.actualBufferedFrames = state.actualDeckLinkBufferedFrames;
	metrics.scheduleCallMilliseconds = state.deckLinkScheduleCallMilliseconds;
	metrics.scheduleLeadAvailable = state.deckLinkScheduleLeadAvailable;
	metrics.playbackStreamTime = state.deckLinkPlaybackStreamTime;
	metrics.playbackFrameIndex = state.deckLinkPlaybackFrameIndex;
	metrics.nextScheduleFrameIndex = state.deckLinkNextScheduleFrameIndex;
	metrics.scheduleLeadFrames = state.deckLinkScheduleLeadFrames;
	metrics.scheduleRealignmentCount = state.deckLinkScheduleRealignmentCount;
	return metrics;
}

void DeckLinkOutput::HandleCompletion(const VideoIOCompletion& completion)
{
	++mCompletions;
	switch (completion.result)
	{
	case VideoIOCompletionResult::DisplayedLate:
		++mDisplayedLate;
		break;
	case VideoIOCompletionResult::Dropped:
		++mDropped;
		break;
	case VideoIOCompletionResult::Flushed:
		++mFlushed;
		break;
	case VideoIOCompletionResult::Completed:
	case VideoIOCompletionResult::Unknown:
	default:
		break;
	}

	if (mCompletionCallback)
		mCompletionCallback(completion);
}
}
