#include <QApplication>
#include <uvoscam.h>
#include <QSettings>
#include <QString>
#include <QDebug>
#include <QMessageBox>
#include <QSplashScreen>
#include <QDir>
#include <QThread>
#include <opencv2/core/mat.hpp>
#include <unistd.h>
#include <uvosunwrap/log.h>
#include <QCommandLineParser>

#include "cameras.h"
#include "./ui/cameradialog.h"
#include "profile.h"
#include "./ui/mainwindow.h"
#include "./ui/profiledialog.h"
#include "imagepipeline.h"


const char* organziation = "UVOS";
const char* application  = "UVOS";
const char* version  = "UVOS";

std::vector<cam::Camera::Description> showCameraSelectionDialog(bool* accepted = nullptr, QWidget* w = nullptr)
{
	std::vector<cam::Camera::Description> ret;
	CameraDialog diag(cam::Camera::getAvailableCameras(), w);

	diag.show();
	if(diag.exec() == QDialog::Accepted)
	{
		ret = diag.getDescriptions();
		if(accepted)
			*accepted = true;
	}
	else if(accepted)
	{
		*accepted = false;
	}
	return ret;
}

void showProfileDialog(Cameras* cameras, QWidget* w)
{
	qDebug()<<__FUNCTION__;
	cameras->stop();
	cameras->disable(true);
	std::vector<cam::Camera::Description> descs = cameras->getCameras();
	ProfileDialog diag(cameras, w);
	diag.show();
	diag.exec();
	cameras->disable(false);
}

int main(int argc, char *argv[])
{
	Log::level = Log::DEBUG;

	uvosled led;
	int uvosledRet = uvosled_connect(&led);
	int ret;
	
	{
		QApplication a(argc, argv);
		QCoreApplication::setOrganizationName("UVOS");
		QCoreApplication::setOrganizationDomain("uvos.xyz");
		QCoreApplication::setApplicationName("MAClient");
		QCoreApplication::setApplicationVersion("0.5");

		 //parse comand line
		QCommandLineParser parser;
		parser.addHelpOption();
		parser.addVersionOption();
		QCommandLineOption noQuirkOption(QStringList() << "q" << "no-quirk", QCoreApplication::translate("main", "Do not enable camera specific quirks"));
		parser.addOption(noQuirkOption);
		QCommandLineOption serialOption(QStringList() << "s" << "serial", QCoreApplication::translate("main", "Take images in sequence instead of all at once"));
		parser.addOption(serialOption);
		QCommandLineOption simpleStichOption(QStringList() << "x" << "simple-stich", QCoreApplication::translate("main", "use simple sticher"));
		parser.addOption(simpleStichOption);
		QCommandLineOption quirkDurationOption(QStringList() << "d" << "quirk-duration", QCoreApplication::translate("main", "PhotonFocus quirk duration time"), QCoreApplication::translate("main", "time"));
		parser.addOption(quirkDurationOption);
		QCommandLineOption cameraBootDurationOption(QStringList() << "t" << "boot-duration", QCoreApplication::translate("main", "Camera boot time"), QCoreApplication::translate("main", "time"));
		parser.addOption(cameraBootDurationOption);
		QCommandLineOption viewerOption(QStringList() << "c" << "viewer", QCoreApplication::translate("main", "Open app as viewer"));
		parser.addOption(viewerOption);
		parser.process(a);

		QSplashScreen splash(QPixmap(":/images/splash.png"));
		splash.show();

		QDir().mkpath(Profile::profileLocation());

		qRegisterMetaType<cv::Mat>("cv::Mat");
		qRegisterMetaType<size_t>("size_t");
		qRegisterMetaType<Camera::Image>("Camera::Image");
		qRegisterMetaType<Camera::Image>("Image");
		qRegisterMetaType<std::vector<Camera::Image>>("std::vector<Camera::Image>");

		QSettings settings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());

		if(uvosledRet >= 0 && !parser.isSet(viewerOption))
		{
			qDebug()<<"uvosled_poweron";
			uvosled_poweron(&led);
			// Give cameras some time to power on
			// TODO: figure out how to do this better
			if(parser.isSet(cameraBootDurationOption))
				sleep(parser.value(cameraBootDurationOption).toDouble());
			else
				sleep(20);
		}

		Cameras cameras(uvosledRet < 0 ? nullptr : &led, !parser.isSet(noQuirkOption), parser.isSet(serialOption));
		cameras.setFree(false);
		ImagePipeline pipe(&cameras, parser.isSet(simpleStichOption));

		if(parser.isSet(quirkDurationOption))
			cameras.quirkTime = parser.value(quirkDurationOption).toDouble();

		MainWindow w(parser.isSet(viewerOption));

		if(!parser.isSet(viewerOption))
		{
			QObject::connect(&cameras, &Cameras::cameraAdded, &w, &MainWindow::addCamera);
			QObject::connect(&cameras, &Cameras::cameraRemoved, &w, &MainWindow::removeCamera);
			QObject::connect(&cameras, &Cameras::enableCapture, &w, &MainWindow::enableCapture);
			QObject::connect(&w, &MainWindow::sigCapture, [&cameras](){cameras.start(); cameras.trigger();});

			QTimer temperatureTimer;
			temperatureTimer.setSingleShot(false);
			QObject::connect(&temperatureTimer, &QTimer::timeout, [&cameras, &w](){w.setTemperature(cameras.getMeanTemp());});
			temperatureTimer.start(1000);

			QObject::connect(&w, &MainWindow::sigChooseCameras, [&cameras, &w]()
			{
				bool accepted;
				std::vector<cam::Camera::Description> descs = showCameraSelectionDialog(&accepted, &w);
				if(accepted)
					cameras.setCameras(descs);
			});

			QObject::connect(&w, &MainWindow::sigEditProfiles, [&cameras, &w](){showProfileDialog(&cameras, &w); w.refreshProfiles();});
			QObject::connect(&pipe, &ImagePipeline::sigResult, w.mainImageViewer(), &CvImageViewer::setImage, Qt::QueuedConnection);
			QObject::connect(&pipe, &ImagePipeline::sigResult, [&w](){w.statusMsg("idle");});
			QObject::connect(&pipe, &ImagePipeline::sigInvalidProfile, &w, &MainWindow::profileInconpatible);

			QObject::connect(&w, &MainWindow::sigProfile, [&pipe](QString name)
			{
				Profile profile;
				profile.load(name);
				if(profile.cameras.size() != 0)
					qDebug()<<"loading profile"<<name<<"with"<<profile.cameras.size()<<"cameras and first camera"<<profile.cameras.at(0).id;
				else
					qDebug()<<"empty profile!!";
				pipe.setProfile(profile);
			});

			cameras.load(settings);
		}

		splash.hide();
		w.show();

		if(uvosledRet < 0 && !parser.isSet(viewerOption))
			QMessageBox::warning(&w, "UVOS LED", "Can not connect to the UVOS LED device");

		if(!parser.isSet(viewerOption) && cameras.getCameras().empty())
		{
			QMessageBox::information(&w, "Cameras", "No cameras configured, please choose at least one camera.");
			cameras.setCameras(showCameraSelectionDialog());
		}

		QString name = w.getProfileName();
		if(!name.isEmpty())
		{
			Profile profile;
			profile.load(w.getProfileName());
			if(profile.cameras.size() != 0)
				qDebug()<<"loading profile"<<name<<"with"<<profile.cameras.size()<<"cameras and first camera"<<profile.cameras.at(0).id;
			else
				qDebug()<<"empty profile!!";
			pipe.setProfile(profile);
		}

		ret =  a.exec();
		
		if(!parser.isSet(viewerOption))
			cameras.store(settings);
	}

	if(uvosledRet >= 0)
		uvosled_poweroff(&led);

	return ret;
}
