#include <cassert>
#include <stdlib.h>

#include <QCoreApplication>
#include <QResource>
#include <QLocale>
#include <QFile>
#include <QString>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QPair>
#include <cstdint>
#include <limits>
#include <QThread>

#include <utils/Components.h>
#include <utils/JsonUtils.h>
#include <utils/Image.h>

#include <HyperionConfig.h> // Required to determine the cmake options

// bonjour browser
#ifdef ENABLE_AVAHI
#include <bonjour/bonjourbrowserwrapper.h>
#endif
#include <jsonserver/JsonServer.h>
#include <webserver/WebServer.h>
#include "hyperiond.h"

// Flatbuffer Server
#include <flatbufserver/FlatBufferServer.h>

// Protobuffer Server
#include <protoserver/ProtoServer.h>

// ssdp
#include <ssdp/SSDPHandler.h>

// settings
#include <hyperion/SettingsManager.h>

// AuthManager
#include <hyperion/AuthManager.h>

// InstanceManager Hyperion
#include <hyperion/HyperionIManager.h>

// NetOrigin checks
#include <utils/NetOrigin.h>

// Init Python
#include <python/PythonInit.h>

// EffectFileHandler
#include <effectengine/EffectFileHandler.h>

#ifdef ENABLE_CEC
#include <cec/CECHandler.h>
#endif

HyperionDaemon *HyperionDaemon::daemon = nullptr;

HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool logLvlOverwrite, bool readonlyMode)
	: QObject(parent), _log(Logger::getInstance("DAEMON"))
	  , _instanceManager(new HyperionIManager(rootPath, this, readonlyMode))
	  , _authManager(new AuthManager(this, readonlyMode))
#ifdef ENABLE_AVAHI
	  , _bonjourBrowserWrapper(new BonjourBrowserWrapper())
#endif
	  , _netOrigin(new NetOrigin(this))
	  , _pyInit(new PythonInit())
	  , _webserver(nullptr)
	  , _sslWebserver(nullptr)
	  , _jsonServer(nullptr)
	  , _videoGrabber(nullptr)
	  , _dispmanx(nullptr)
	  , _x11Grabber(nullptr)
	  , _xcbGrabber(nullptr)
	  , _amlGrabber(nullptr)
	  , _fbGrabber(nullptr)
	  , _osxGrabber(nullptr)
	  , _qtGrabber(nullptr)
	  , _dxGrabber(nullptr)
	  , _ssdp(nullptr)
	  , _cecHandler(nullptr)
	  , _currVideoMode(VideoMode::VIDEO_2D)
{
	HyperionDaemon::daemon = this;

	// Register metas for thread queued connection
	qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
	qRegisterMetaType<hyperion::Components>("hyperion::Components");
	qRegisterMetaType<settings::type>("settings::type");
	qRegisterMetaType<VideoMode>("VideoMode");
	qRegisterMetaType<QMap<quint8, QJsonObject>>("QMap<quint8,QJsonObject>");
	qRegisterMetaType<std::vector<ColorRgb>>("std::vector<ColorRgb>");

	// init settings, this settingsManager accesses global settings which are independent from instances
	_settingsManager = new SettingsManager(GLOABL_INSTANCE_ID, this, readonlyMode);

	// set inital log lvl if the loglvl wasn't overwritten by arg
	if (!logLvlOverwrite)
	{
		handleSettingsUpdate(settings::LOGGER, getSetting(settings::LOGGER));
	}

	createCecHandler();

	// init EffectFileHandler
	EffectFileHandler* efh = new EffectFileHandler(rootPath, getSetting(settings::EFFECTS), this);
	connect(this, &HyperionDaemon::settingsChanged, efh, &EffectFileHandler::handleSettingsUpdate);

	// connect and apply settings for AuthManager
	connect(this, &HyperionDaemon::settingsChanged, _authManager, &AuthManager::handleSettingsUpdate);
	_authManager->handleSettingsUpdate(settings::NETWORK, _settingsManager->getSetting(settings::NETWORK));

	// connect and apply settings for NetOrigin
	connect(this, &HyperionDaemon::settingsChanged, _netOrigin, &NetOrigin::handleSettingsUpdate);
	_netOrigin->handleSettingsUpdate(settings::NETWORK, _settingsManager->getSetting(settings::NETWORK));

	// spawn all Hyperion instances (non blocking)
	_instanceManager->startAll();

	//Cleaning up Hyperion before quit
	connect(parent, SIGNAL(aboutToQuit()), this, SLOT(freeObjects()));

	// pipe settings changes from HyperionIManager to Daemon
	connect(_instanceManager, &HyperionIManager::settingsChanged, this, &HyperionDaemon::settingsChanged);

	// listen for setting changes of framegrabber and v4l2
	connect(this, &HyperionDaemon::settingsChanged, this, &HyperionDaemon::handleSettingsUpdate);

	// forward videoModes from HyperionIManager to Daemon evaluation
	connect(_instanceManager, &HyperionIManager::requestVideoMode, this, &HyperionDaemon::setVideoMode);
	// return videoMode changes from Daemon to HyperionIManager
	connect(this, &HyperionDaemon::videoMode, _instanceManager, &HyperionIManager::newVideoMode);

	// ---- grabber -----
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_XCB) && !defined(ENABLE_AMLOGIC) && !defined(ENABLE_QT) && !defined(ENABLE_DX)
	Info(_log, "No platform capture supported on this platform");
#endif

	// init system capture (framegrabber)
	handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));

	// init v4l2 && media foundation capture
	handleSettingsUpdate(settings::V4L2, getSetting(settings::V4L2));

	// ---- network services -----
	startNetworkServices();
}

HyperionDaemon::~HyperionDaemon()
{
	delete _settingsManager;
	delete _pyInit;
}

void HyperionDaemon::setVideoMode(VideoMode mode)
{
	if (_currVideoMode != mode)
	{
		_currVideoMode = mode;
		emit videoMode(mode);
	}
}

QJsonDocument HyperionDaemon::getSetting(settings::type type) const
{
	return _settingsManager->getSetting(type);
}

void HyperionDaemon::freeObjects()
{
	Debug(_log, "Cleaning up Hyperion before quit.");

	// destroy network first as a client might want to access hyperion
	delete _jsonServer;
	_jsonServer = nullptr;

	if (_flatBufferServer != nullptr)
	{
		auto flatBufferServerThread = _flatBufferServer->thread();
		flatBufferServerThread->quit();
		flatBufferServerThread->wait();
		delete flatBufferServerThread;
		_flatBufferServer = nullptr;
	}

	if (_protoServer != nullptr)
	{
		auto protoServerThread = _protoServer->thread();
		protoServerThread->quit();
		protoServerThread->wait();
		delete protoServerThread;
		_protoServer = nullptr;
	}

	//ssdp before webserver
	if (_ssdp != nullptr)
	{
		auto ssdpThread = _ssdp->thread();
		ssdpThread->quit();
		ssdpThread->wait();
		delete ssdpThread;
		_ssdp = nullptr;
	}

	if (_webserver != nullptr)
	{
		auto webserverThread = _webserver->thread();
		webserverThread->quit();
		webserverThread->wait();
		delete webserverThread;
		_webserver = nullptr;
	}

	if (_sslWebserver != nullptr)
	{
		auto sslWebserverThread = _sslWebserver->thread();
		sslWebserverThread->quit();
		sslWebserverThread->wait();
		delete sslWebserverThread;
		_sslWebserver = nullptr;
	}

#ifdef ENABLE_CEC
	if (_cecHandler != nullptr)
	{
		auto cecHandlerThread = _cecHandler->thread();
		cecHandlerThread->quit();
		cecHandlerThread->wait();
		delete cecHandlerThread;
		delete _cecHandler;
		_cecHandler = nullptr;
	}
#endif

	// stop Hyperions (non blocking)
	_instanceManager->stopAll();

#ifdef ENABLE_AVAHI
	delete _bonjourBrowserWrapper;
	_bonjourBrowserWrapper = nullptr;
#endif

	delete _amlGrabber;
	delete _dispmanx;
	delete _fbGrabber;
	delete _osxGrabber;
	delete _qtGrabber;
	delete _dxGrabber;
	delete _videoGrabber;

	_videoGrabber = nullptr;
	_amlGrabber  = nullptr;
	_dispmanx    = nullptr;
	_fbGrabber   = nullptr;
	_osxGrabber  = nullptr;
	_qtGrabber   = nullptr;
	_dxGrabber   = nullptr;
}

void HyperionDaemon::startNetworkServices()
{
	// Create Json server
	_jsonServer = new JsonServer(getSetting(settings::JSONSERVER));
	connect(this, &HyperionDaemon::settingsChanged, _jsonServer, &JsonServer::handleSettingsUpdate);

	// Create FlatBuffer server in thread
	_flatBufferServer = new FlatBufferServer(getSetting(settings::FLATBUFSERVER));
	QThread* fbThread = new QThread(this);
	fbThread->setObjectName("FlatBufferServerThread");
	_flatBufferServer->moveToThread(fbThread);
	connect(fbThread, &QThread::started, _flatBufferServer, &FlatBufferServer::initServer);
	connect(fbThread, &QThread::finished, _flatBufferServer, &FlatBufferServer::deleteLater);
	connect(this, &HyperionDaemon::settingsChanged, _flatBufferServer, &FlatBufferServer::handleSettingsUpdate);
	fbThread->start();

	// Create Proto server in thread
	_protoServer = new ProtoServer(getSetting(settings::PROTOSERVER));
	QThread* pThread = new QThread(this);
	pThread->setObjectName("ProtoServerThread");
	_protoServer->moveToThread(pThread);
	connect(pThread, &QThread::started, _protoServer, &ProtoServer::initServer);
	connect(pThread, &QThread::finished, _protoServer, &ProtoServer::deleteLater);
	connect(this, &HyperionDaemon::settingsChanged, _protoServer, &ProtoServer::handleSettingsUpdate);
	pThread->start();

	// Create Webserver in thread
	_webserver = new WebServer(getSetting(settings::WEBSERVER), false);
	QThread* wsThread = new QThread(this);
	wsThread->setObjectName("WebServerThread");
	_webserver->moveToThread(wsThread);
	connect(wsThread, &QThread::started, _webserver, &WebServer::initServer);
	connect(wsThread, &QThread::finished, _webserver, &WebServer::deleteLater);
	connect(this, &HyperionDaemon::settingsChanged, _webserver, &WebServer::handleSettingsUpdate);
	wsThread->start();

	// Create SSL Webserver in thread
	_sslWebserver = new WebServer(getSetting(settings::WEBSERVER), true);
	QThread* sslWsThread = new QThread(this);
	sslWsThread->setObjectName("SSLWebServerThread");
	_sslWebserver->moveToThread(sslWsThread);
	connect(sslWsThread, &QThread::started, _sslWebserver, &WebServer::initServer);
	connect(sslWsThread, &QThread::finished, _sslWebserver, &WebServer::deleteLater);
	connect(this, &HyperionDaemon::settingsChanged, _sslWebserver, &WebServer::handleSettingsUpdate);
	sslWsThread->start();

	// Create SSDP server in thread
	_ssdp = new SSDPHandler(_webserver,
							   getSetting(settings::FLATBUFSERVER).object()["port"].toInt(),
							   getSetting(settings::PROTOSERVER).object()["port"].toInt(),
							   getSetting(settings::JSONSERVER).object()["port"].toInt(),
							   getSetting(settings::WEBSERVER).object()["sslPort"].toInt(),
							   getSetting(settings::GENERAL).object()["name"].toString());
	QThread* ssdpThread = new QThread(this);
	ssdpThread->setObjectName("SSDPThread");
	_ssdp->moveToThread(ssdpThread);
	connect(ssdpThread, &QThread::started, _ssdp, &SSDPHandler::initServer);
	connect(ssdpThread, &QThread::finished, _ssdp, &SSDPHandler::deleteLater);
	connect(_webserver, &WebServer::stateChange, _ssdp, &SSDPHandler::handleWebServerStateChange);
	connect(this, &HyperionDaemon::settingsChanged, _ssdp, &SSDPHandler::handleSettingsUpdate);
	ssdpThread->start();
}

void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJsonDocument& config)
{
	if (settingsType == settings::LOGGER)
	{
		const QJsonObject& logConfig = config.object();

		std::string level = logConfig["level"].toString("warn").toStdString(); // silent warn verbose debug
		if (level == "silent")
		{
			Logger::setLogLevel(Logger::OFF);
		}
		else if (level == "warn")
		{
			Logger::setLogLevel(Logger::LogLevel::WARNING);
		}
		else if (level == "verbose")
		{
			Logger::setLogLevel(Logger::INFO);
		}
		else if (level == "debug")
		{
			Logger::setLogLevel(Logger::DEBUG);
		}
	}

	if (settingsType == settings::SYSTEMCAPTURE)
	{
		const QJsonObject& grabberConfig = config.object();

		_grabber_width = grabberConfig["width"].toInt(96);
		_grabber_height = grabberConfig["height"].toInt(96);
		_grabber_pixelDecimation = grabberConfig["pixelDecimation"].toInt(GrabberWrapper::DEFAULT_PIXELDECIMATION);
		_grabber_frequency = grabberConfig["fps"].toInt(GrabberWrapper::DEFAULT_RATE_HZ);

		_grabber_cropLeft = grabberConfig["cropLeft"].toInt(0);
		_grabber_cropRight = grabberConfig["cropRight"].toInt(0);
		_grabber_cropTop = grabberConfig["cropTop"].toInt(0);
		_grabber_cropBottom = grabberConfig["cropBottom"].toInt(0);

		#ifdef ENABLE_OSX
		QString type = grabberConfig["device"].toString("osx");
		#else
		QString type = grabberConfig["device"].toString("auto");
		#endif

		// auto eval of type
		if (type == "auto")
		{
			// dispmanx -> on raspi
			if (QFile::exists("/dev/vchiq"))
			{
				type = "dispmanx";
			}
			// amlogic -> /dev/amvideo exists
			else
			{
				if (QFile::exists("/dev/amvideo"))
				{
					type = "amlogic";

					QString amlDevice ("/dev/amvideocap0");
					if (!QFile::exists(amlDevice))
					{
						Error(_log, "grabber device '%s' for type amlogic not found!", QSTRING_CSTR(amlDevice));
					}
				}
				else
				{
					// x11 -> if DISPLAY is set
					QByteArray envDisplay = qgetenv("DISPLAY");
					if (!envDisplay.isEmpty())
					{
						#if defined(ENABLE_X11)
						type = "x11";
						#elif defined(ENABLE_XCB)
						type = "xcb";
						#else
						type = "qt";
						#endif
					}
					// qt -> if nothing other applies
					else
					{
						type = "qt";
					}
				}
			}
		}

		if (_prevType != type)
		{
			Info(_log, "set screen capture device to '%s'", QSTRING_CSTR(type));

			// stop all capture interfaces
			#ifdef ENABLE_FB
			if (_fbGrabber != nullptr)
			{
				_fbGrabber->stop();
				delete _fbGrabber;
				_fbGrabber = nullptr;
			}
			#endif
			#ifdef ENABLE_DISPMANX
			if (_dispmanx != nullptr)
			{
				_dispmanx->stop();
				delete _dispmanx;
				_dispmanx = nullptr;
			}
			#endif
			#ifdef ENABLE_AMLOGIC
			if (_amlGrabber != nullptr)
			{
				_amlGrabber->stop();
				delete _amlGrabber;
				_amlGrabber = nullptr;
			}
			#endif
			#ifdef ENABLE_OSX
			if (_osxGrabber != nullptr)
			{
				_osxGrabber->stop();
				delete _osxGrabber;
				_osxGrabber = nullptr;
			}
			#endif
			#ifdef ENABLE_X11
			if (_x11Grabber != nullptr)
			{
				_x11Grabber->stop();
				delete _x11Grabber;
				_x11Grabber = nullptr;
			}
			#endif
			#ifdef ENABLE_XCB
			if (_xcbGrabber != nullptr)
			{
				_xcbGrabber->stop();
				delete _xcbGrabber;
				_xcbGrabber = nullptr;
			}
			#endif
			#ifdef ENABLE_QT
			if (_qtGrabber != nullptr)
			{
				_qtGrabber->stop();
				delete _qtGrabber;
				_qtGrabber = nullptr;
			}
			#endif
			#ifdef ENABLE_DX
			if (_dxGrabber != nullptr)
			{
				_dxGrabber->stop();
				delete _dxGrabber;
				_dxGrabber = nullptr;
			}
			#endif

			// create/start capture interface
			if (type == "framebuffer")
			{
				if (_fbGrabber == nullptr)
				{
					createGrabberFramebuffer(grabberConfig);
				}
				#ifdef ENABLE_FB
					_fbGrabber->handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
					_fbGrabber->tryStart();
				#endif
			}
			else if (type == "dispmanx")
			{
				if (_dispmanx == nullptr)
				{
					createGrabberDispmanx(grabberConfig);
				}
				#ifdef ENABLE_DISPMANX
					_dispmanx->handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
					_dispmanx->tryStart();
				#endif
			}
			else if (type == "amlogic")
			{
				if (_amlGrabber == nullptr)
				{
					createGrabberAmlogic(grabberConfig);
				}
				#ifdef ENABLE_AMLOGIC
					_amlGrabber->handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
					_amlGrabber->tryStart();
				#endif
			}
			else if (type == "osx")
			{
				if (_osxGrabber == nullptr)
				{
					createGrabberOsx(grabberConfig);
				}
				#ifdef ENABLE_OSX
					_osxGrabber->handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
					_osxGrabber->tryStart();
				#endif
			}
			else if (type == "x11")
			{
				if (_x11Grabber == nullptr)
				{
					createGrabberX11(grabberConfig);
				}
				#ifdef ENABLE_X11
					_x11Grabber->handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
					_x11Grabber->tryStart();
				#endif
			}
			else if (type == "xcb")
			{
				if (_xcbGrabber == nullptr)
				{
					createGrabberXcb(grabberConfig);
				}
				#ifdef ENABLE_XCB
					_xcbGrabber->handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
					_xcbGrabber->tryStart();
				#endif
			}
			else if (type == "qt")
			{
				if (_qtGrabber == nullptr)
				{
					createGrabberQt(grabberConfig);
				}
				#ifdef ENABLE_QT
					_qtGrabber->handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
					_qtGrabber->tryStart();
				#endif
			}
			else if (type == "dx")
			{
				if (_dxGrabber == nullptr)
				{
					createGrabberDx(grabberConfig);
				}
				#ifdef ENABLE_DX
					_dxGrabber->handleSettingsUpdate(settings::SYSTEMCAPTURE, getSetting(settings::SYSTEMCAPTURE));
					_dxGrabber->tryStart();
				#endif
			}
			else
			{
				Error(_log, "Unknown platform capture type: %s", QSTRING_CSTR(type));
				return;
			}
			_prevType = type;
		}
	}
	else if (settingsType == settings::V4L2)
	{

#ifdef ENABLE_CEC
		const QJsonObject& grabberConfig = config.object();
		if (_cecHandler != nullptr && grabberConfig["cecDetection"].toBool(false))
		{
			QMetaObject::invokeMethod(_cecHandler, "start", Qt::QueuedConnection);
		}
		else
		{
			QMetaObject::invokeMethod(_cecHandler, "stop", Qt::QueuedConnection);
		}
#endif

#if defined(ENABLE_V4L2) || defined(ENABLE_MF)
		if (_videoGrabber == nullptr)
		{
			_videoGrabber = new VideoWrapper();
			_videoGrabber->handleSettingsUpdate(settings::V4L2, getSetting(settings::V4L2));

#if defined(ENABLE_MF)
			Debug(_log, "Media Foundation grabber created");
#elif defined(ENABLE_V4L2)
			Debug(_log, "V4L2 grabber created");
#endif

			// connect to HyperionDaemon signal
			connect(this, &HyperionDaemon::videoMode, _videoGrabber, &VideoWrapper::setVideoMode);
			connect(this, &HyperionDaemon::settingsChanged, _videoGrabber, &VideoWrapper::handleSettingsUpdate);
		}
#else
		Debug(_log, "The v4l2 grabber is not supported on this platform");
#endif
	}
}

void HyperionDaemon::createGrabberDispmanx(const QJsonObject& /*grabberConfig*/)
{
#ifdef ENABLE_DISPMANX
	_dispmanx = new DispmanxWrapper(
		_grabber_frequency,
		_grabber_pixelDecimation
		);
	_dispmanx->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);

	// connect to HyperionDaemon signal
	connect(this, &HyperionDaemon::videoMode, _dispmanx, &DispmanxWrapper::setVideoMode);
	connect(this, &HyperionDaemon::settingsChanged, _dispmanx, &DispmanxWrapper::handleSettingsUpdate);

	Info(_log, "DISPMANX frame grabber created");
#else
	Debug(_log, "The dispmanx framegrabber is not supported on this platform");
#endif
}

void HyperionDaemon::createGrabberAmlogic(const QJsonObject& /*grabberConfig*/)
{
#ifdef ENABLE_AMLOGIC
	_amlGrabber = new AmlogicWrapper(
		_grabber_frequency,
		_grabber_pixelDecimation
		);
	_amlGrabber->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);

	// connect to HyperionDaemon signal
	connect(this, &HyperionDaemon::videoMode, _amlGrabber, &AmlogicWrapper::setVideoMode);
	connect(this, &HyperionDaemon::settingsChanged, _amlGrabber, &AmlogicWrapper::handleSettingsUpdate);

	Info(_log, "AMLOGIC grabber created");
#else
	Debug(_log, "The AMLOGIC grabber is not supported on this platform");
#endif
}

void HyperionDaemon::createGrabberX11(const QJsonObject& /*grabberConfig*/)
{
#ifdef ENABLE_X11
	_x11Grabber = new X11Wrapper(
		_grabber_frequency,
		_grabber_pixelDecimation,
		_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom
		);
	_x11Grabber->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);

	// connect to HyperionDaemon signal
	connect(this, &HyperionDaemon::videoMode, _x11Grabber, &X11Wrapper::setVideoMode);
	connect(this, &HyperionDaemon::settingsChanged, _x11Grabber, &X11Wrapper::handleSettingsUpdate);

	Info(_log, "X11 grabber created");
#else
	Debug(_log, "The X11 grabber is not supported on this platform");
#endif
}

void HyperionDaemon::createGrabberXcb(const QJsonObject& /*grabberConfig*/)
{
#ifdef ENABLE_XCB
	_xcbGrabber = new XcbWrapper(
		_grabber_frequency,
		_grabber_pixelDecimation,
		_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom
		);
	_xcbGrabber->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);

	// connect to HyperionDaemon signal
	connect(this, &HyperionDaemon::videoMode, _xcbGrabber, &XcbWrapper::setVideoMode);
	connect(this, &HyperionDaemon::settingsChanged, _xcbGrabber, &XcbWrapper::handleSettingsUpdate);

	Info(_log, "XCB grabber created");
#else
	Debug(_log, "The XCB grabber is not supported on this platform");
#endif
}

void HyperionDaemon::createGrabberQt(const QJsonObject& grabberConfig)
{
#ifdef ENABLE_QT
	_qtGrabber = new QtWrapper(
		_grabber_frequency,
		grabberConfig["input"].toInt(0),
		_grabber_pixelDecimation,
		_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom
		);

	// connect to HyperionDaemon signal
	connect(this, &HyperionDaemon::videoMode, _qtGrabber, &QtWrapper::setVideoMode);
	connect(this, &HyperionDaemon::settingsChanged, _qtGrabber, &QtWrapper::handleSettingsUpdate);

	Info(_log, "Qt grabber created");
#else
	Debug(_log, "The Qt grabber is not supported on this platform");
#endif
}

void HyperionDaemon::createGrabberDx(const QJsonObject& grabberConfig)
{
#ifdef ENABLE_DX
	_dxGrabber = new DirectXWrapper(
		_grabber_frequency,
		grabberConfig["display"].toInt(0),
		_grabber_pixelDecimation,
		_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom
		);

	// connect to HyperionDaemon signal
	connect(this, &HyperionDaemon::videoMode, _dxGrabber, &DirectXWrapper::setVideoMode);
	connect(this, &HyperionDaemon::settingsChanged, _dxGrabber, &DirectXWrapper::handleSettingsUpdate);

	Info(_log, "DirectX grabber created");
#else
	Debug(_log, "The DirectX is not supported on this platform");
#endif
}

void HyperionDaemon::createGrabberFramebuffer(const QJsonObject& grabberConfig)
{
#ifdef ENABLE_FB
	// Construct and start the framebuffer grabber if the configuration is present

	int fbIdx = grabberConfig["input"].toInt(0);
	QString devicePath = QString("/dev/fb%1").arg(fbIdx);
	_fbGrabber = new FramebufferWrapper(
		_grabber_frequency,
		devicePath,
		_grabber_pixelDecimation
		);
	_fbGrabber->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);
	// connect to HyperionDaemon signal
	connect(this, &HyperionDaemon::videoMode, _fbGrabber, &FramebufferWrapper::setVideoMode);
	connect(this, &HyperionDaemon::settingsChanged, _fbGrabber, &FramebufferWrapper::handleSettingsUpdate);

	Info(_log, "Framebuffer grabber created");
#else
	Debug(_log, "The framebuffer is not supported on this platform");
#endif
}

	void HyperionDaemon::createGrabberOsx(const QJsonObject& grabberConfig)
{
#ifdef ENABLE_OSX
	// Construct and start the osx grabber if the configuration is present
	_osxGrabber = new OsxWrapper(
		_grabber_frequency,
		grabberConfig["input"].toInt(0),
		_grabber_pixelDecimation
		);
	_osxGrabber->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);

	// connect to HyperionDaemon signal
	connect(this, &HyperionDaemon::videoMode, _osxGrabber, &OsxWrapper::setVideoMode);
	connect(this, &HyperionDaemon::settingsChanged, _osxGrabber, &OsxWrapper::handleSettingsUpdate);

	Info(_log, "OSX grabber created");
#else
	Debug(_log, "The osx grabber is not supported on this platform");
#endif
}

void HyperionDaemon::createCecHandler()
{
#if defined(ENABLE_V4L2) && defined(ENABLE_CEC)
	_cecHandler = new CECHandler;

	QThread* thread = new QThread(this);
	thread->setObjectName("CECThread");
	_cecHandler->moveToThread(thread);
	thread->start();

	connect(_cecHandler, &CECHandler::cecEvent, [&](CECEvent event) {
		if (_videoGrabber != nullptr)
		{
			_videoGrabber->handleCecEvent(event);
		}
	});

	Info(_log, "CEC handler created");
#else
	Debug(_log, "The CEC handler is not supported on this platform");
#endif
}
