#include "VideoBackendLifecycle.h"

#include <iostream>
#include <string>

namespace
{
int gFailures = 0;

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

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

void TestAllowedLifecycleTransitions()
{
	VideoBackendLifecycle lifecycle;
	Expect(lifecycle.State() == VideoBackendLifecycleState::Uninitialized, "lifecycle starts uninitialized");

	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Discovering, "discover").accepted,
		"uninitialized can transition to discovering");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Discovered, "discovered").accepted,
		"discovering can transition to discovered");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Configuring, "configuring").accepted,
		"discovered can transition to configuring");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Configured, "configured").accepted,
		"configuring can transition to configured");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Prerolling, "preroll").accepted,
		"configured can transition to prerolling");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Running, "running").accepted,
		"prerolling can transition to running");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Degraded, "degraded").accepted,
		"running can transition to degraded");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Running, "recovered").accepted,
		"degraded can transition back to running");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Stopping, "stopping").accepted,
		"running can transition to stopping");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Stopped, "stopped").accepted,
		"stopping can transition to stopped");
}

void TestInvalidLifecycleTransitionIsRejected()
{
	VideoBackendLifecycle lifecycle;
	const VideoBackendLifecycleTransition transition =
		lifecycle.TransitionTo(VideoBackendLifecycleState::Running, "skip setup");
	Expect(!transition.accepted, "uninitialized cannot transition directly to running");
	Expect(lifecycle.State() == VideoBackendLifecycleState::Uninitialized, "invalid transition leaves state unchanged");
	Expect(transition.errorMessage.find("Invalid video backend lifecycle transition") != std::string::npos,
		"invalid transition reports an error");
}

void TestFailureStateRecordsReasonAndCanRecover()
{
	VideoBackendLifecycle lifecycle;
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Discovering, "discover").accepted,
		"lifecycle can start discovery");
	Expect(lifecycle.Fail("no device").accepted, "discovering can transition to failed");
	Expect(lifecycle.State() == VideoBackendLifecycleState::Failed, "failure transition sets failed state");
	Expect(lifecycle.FailureReason() == "no device", "failure reason is retained");
	Expect(lifecycle.TransitionTo(VideoBackendLifecycleState::Discovering, "retry").accepted,
		"failed lifecycle can retry discovery");
	Expect(lifecycle.FailureReason().empty(), "successful non-failed transition clears failure reason");
}

void TestStateNamesAreStable()
{
	Expect(std::string(VideoBackendLifecycle::StateName(VideoBackendLifecycleState::Uninitialized)) == "uninitialized",
		"uninitialized state name is stable");
	Expect(std::string(VideoBackendLifecycle::StateName(VideoBackendLifecycleState::Running)) == "running",
		"running state name is stable");
	Expect(std::string(VideoBackendLifecycle::StateName(VideoBackendLifecycleState::Failed)) == "failed",
		"failed state name is stable");
}
}

int main()
{
	TestAllowedLifecycleTransitions();
	TestInvalidLifecycleTransitionIsRejected();
	TestFailureStateRecordsReasonAndCanRecover();
	TestStateNamesAreStable();

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

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