#include "profile.h"
#include <opencv2/imgcodecs.hpp>
#include <QStandardPaths>
#include <QFile>
#include <QDir>
#include <QDebug>
#include <uvosunwrap/unwrap.h>

void CameraSetup::store(const QString &filename) const
{
	cv::FileStorage matf(filename.toStdString(), cv::FileStorage::APPEND);
	if(remapMap.xMat.data)
	{
		matf<<("_"+QString::number(id)+"_xmat").toStdString()<<remapMap.xMat<<("_"+QString::number(id)+"_ymat").toStdString()
		   <<remapMap.yMat<<("_"+QString::number(id)+"_origin").toStdString()<<remapMap.topLeftCoordinate;
	}
	if(darkmap.data)
	{
		matf<<("_"+QString::number(id)+"_darkmap").toStdString()<<darkmap;
	}
	if(bgmask.data)
	{
		matf<<("_"+QString::number(id)+"_bgmask").toStdString()<<bgmask;
	}
	matf<<("_"+QString::number(id)+"_bayermode").toStdString()<<bayerMode;
	matf.release();
}

void CameraSetup::load(const QString &filename, size_t cameraId)
{
	id = cameraId;
	cv::FileStorage matf(filename.toStdString(), cv::FileStorage::READ);
	if (matf.isOpened())
	{
		matf[("_"+QString::number(id)+"_darkmap").toStdString()]>>darkmap;
		matf[("_"+QString::number(id)+"_bgmask").toStdString()]>>bgmask;
		matf[("_"+QString::number(id)+"_xmat").toStdString()]>>remapMap.xMat;
		matf[("_"+QString::number(id)+"_ymat").toStdString()]>>remapMap.yMat;
		matf[("_"+QString::number(id)+"_origin").toStdString()]>>remapMap.topLeftCoordinate;
		matf[("_"+QString::number(id)+"_bayermode").toStdString()]>>bayerMode;
	}
	else
	{
		qDebug()<<"could not open file"<<filename;
	}
}

void LightingSetup::store(QSettings& settings) const
{
	settings.setValue(GROUP + QString("/brightness"), brightness);
	settings.setValue(GROUP + QString("/mask"), mask);
}

void LightingSetup::load(const QSettings& settings)
{
	brightness = settings.value(GROUP + QString("/brightness"), 0.25).toDouble();
	mask = settings.value(GROUP + QString("/mask"), 0).toUInt();
}

void Profile::store(QSettings& settings) const
{
	deleteProfile();
	qDebug()<<"storing to "<<settings.fileName();
	lighting.store(settings);
	settings.setValue(GROUP + QString("/id"), static_cast<unsigned long long>(id));
	settings.setValue(GROUP + QString("/exposureTime"), exposureTime);
	settings.setValue(GROUP + QString("/name"), name_);
	settings.setValue(GROUP + QString("/nodistort"), nodistort);
	settings.setValue(GROUP + QString("/kfact"), kFactor);

	if(lightmap.data)
		cv::imwrite((profileLocation() + QString::number(id) + ".lightmap.png").toStdString(), lightmap);

	settings.beginWriteArray(GROUP + QString("/cameras"), cameras.size());
	for(size_t i = 0; i < cameras.size(); ++i)
	{
		const CameraSetup& camera = cameras[i];
		settings.setArrayIndex(i);
		camera.store(profileLocation() + name_ + ".profile.mat");
		settings.setValue("id", static_cast<unsigned long long>(camera.id));
	}
	settings.endArray();

	cv::FileStorage matf((profileLocation() + name_ + ".calcurve.mat").toStdString(), cv::FileStorage::WRITE);
	matf<<"cal"<<calcurve;
	matf.release();

	cv::FileStorage matfl((profileLocation() + name_ + ".lightmap.mat").toStdString(), cv::FileStorage::WRITE);
	matfl<<"cal"<<lightmap;
	matfl.release();
}

void Profile::load(QSettings &settings)
{
	lighting.load(settings);
	id = settings.value(GROUP + QString("/id")).toULongLong();
	exposureTime = settings.value(GROUP + QString("/exposureTime"), 1.0/60.0).toDouble();
	name_ = settings.value(GROUP + QString("/name"), "NULL").toString();
	nodistort = settings.value(GROUP + QString("/nodistort"), "NULL").toBool();

	kFactor = settings.value(GROUP + QString("/kfact"), 0).toDouble();

	lightmap = cv::imread((profileLocation() + QString::number(id) + ".lightmap.png").toStdString());

	int size = settings.beginReadArray(GROUP + QString("/cameras"));
	for(int i = 0; i < size; ++i)
	{
		settings.setArrayIndex(i);
		cameras.push_back(CameraSetup());
		cameras.back().load(profileLocation() + name_ + ".profile.mat", settings.value("id", 0).toULongLong());
	}
	settings.endArray();

	cv::FileStorage matf((profileLocation() + name_ + ".calcurve.mat").toStdString(), cv::FileStorage::READ);
	matf["cal"]>>calcurve;
	matf.release();

	if(calcurve.data && (calcurve.type() != CV_32FC1 || calcurve.rows != 2))
		calcurve.release();

	cv::FileStorage matfl((profileLocation() + name_ + ".lightmap.mat").toStdString(), cv::FileStorage::READ);
	matfl["cal"]>>lightmap;
	matfl.release();
}

void Profile::load(const QString& name)
{
	qDebug()<<profileLocation();
	QSettings settings(profileLocation() + name + ".profile.ini", QSettings::IniFormat);
	name_=name;
	load(settings);
}

void Profile::store(const QString& name)
{
	QSettings settings(profileLocation() + name + ".profile.ini", QSettings::IniFormat);
	name_=name;
	store(settings);
	settings.sync();
}

void Profile::load()
{
	load(name_);
}

void Profile::store()
{
	store(name_);
}

void Profile::deleteProfile() const
{
	QFile::remove(profileLocation() + name_ + ".profile.ini");
	QFile::remove(profileLocation() + name_ + ".lightmap.mat");
	QFile::remove(profileLocation() + name_ + ".calcurve.mat");
	QFile::remove(profileLocation() + name_ + ".profile.mat");
}

QList<QString> Profile::avaiableProfiles()
{
	QDir dir(profileLocation());
	dir.setFilter(QDir::Files);
	QStringList filters;
	filters<<"*.ini";
	dir.setNameFilters(filters);
	QList<QString> ret;
	QStringList list = dir.entryList();

	for(QString string : list)
		ret.push_back(string.split(".")[0]);
	return  ret;
}

void Profile::setCamerasSetupsFromDescription(const std::vector<cam::Camera::Description>& descs)
{
	for(size_t i = 0; i < cameras.size(); ++i)
	{
		bool found = false;
		for(auto& cameraDesc : descs)
		{
			if(cameras[i].id == cameraDesc.getId())
			{
				found = true;
				break;
			}
		}
		if(!found)
		{
			cameras.erase(cameras.begin()+i);
			--i;
		}
	}

	for(size_t i = 0; i < descs.size(); ++i)
	{
		bool found = false;
		for(auto& camera : cameras)
		{
			if(camera.id == descs[i].getId())
			{
				found = true;
				break;
			}
		}
		if(!found)
		{
			CameraSetup tmp;
			tmp.load(profileLocation() + name_ + ".profile.mat", descs[i].getId());
			cameras.push_back(tmp);
		}
	}
}

bool Profile::camerasSufficant(const std::vector<cam::Camera::Description>& desc) const
{
	bool invalid = false;
	for(auto& cameraProfile : cameras)
	{
		bool found = false;
		for(auto& camera : desc)
		{
			if(camera.getId() == cameraProfile.id)
			{
				found = true;
				break;
			}
		}

		if(!found)
		{
			invalid = true;
			break;
		}
	}
	return  !invalid;
}


QString Profile::profileLocation()
{
	return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/profiles/";
}

Profile::Profile(const QString& name): name_(name)
{
	id = QRandomGenerator::system()->generate64();
}
