#pragma once

#include "DeckLinkInput.h"

#include <atomic>
#include <chrono>
#include <string>
#include <thread>

namespace RenderCadenceCompositor
{
struct DeckLinkInputThreadConfig
{
	std::chrono::milliseconds idleSleep = std::chrono::milliseconds(100);
};

class DeckLinkInputThread
{
public:
	DeckLinkInputThread(DeckLinkInput& input, DeckLinkInputThreadConfig config = DeckLinkInputThreadConfig()) :
		mInput(input),
		mConfig(config)
	{
	}

	DeckLinkInputThread(const DeckLinkInputThread&) = delete;
	DeckLinkInputThread& operator=(const DeckLinkInputThread&) = delete;

	~DeckLinkInputThread()
	{
		Stop();
	}

	bool Start(std::string& error)
	{
		if (mThread.joinable())
			return true;
		mStartSucceeded.store(false, std::memory_order_release);
		mStartCompleted.store(false, std::memory_order_release);
		mStopping.store(false, std::memory_order_release);
		mThread = std::thread([this]() { ThreadMain(); });

		while (!mStartCompleted.load(std::memory_order_acquire))
			std::this_thread::sleep_for(std::chrono::milliseconds(1));

		if (mStartSucceeded.load(std::memory_order_acquire))
			return true;

		error = mStartError;
		Stop();
		return false;
	}

	void Stop()
	{
		mStopping.store(true, std::memory_order_release);
		if (mThread.joinable())
			mThread.join();
	}

private:
	void ThreadMain()
	{
		std::string error;
		if (!mInput.Start(error))
		{
			mStartError = error;
			mStartCompleted.store(true, std::memory_order_release);
			return;
		}

		mStartSucceeded.store(true, std::memory_order_release);
		mStartCompleted.store(true, std::memory_order_release);
		while (!mStopping.load(std::memory_order_acquire))
			std::this_thread::sleep_for(mConfig.idleSleep);
		mInput.Stop();
	}

	DeckLinkInput& mInput;
	DeckLinkInputThreadConfig mConfig;
	std::thread mThread;
	std::atomic<bool> mStopping{ false };
	std::atomic<bool> mStartCompleted{ false };
	std::atomic<bool> mStartSucceeded{ false };
	std::string mStartError;
};
}
