/*UVOS*/

/* This file is part of MAClient copyright © 2021 Carl Philipp Klemm.
 *
 * MAClient is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPL) version 
 * 3 as published by the Free Software Foundation.
 *
 * MAClient is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MAClient.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "configurecameradialog.h"
#include "ui_configurecameradialog.h"

#include <QFileDialog>
#include <QMessageBox>
#include <uvosunwrap/bgremoval.h>
#include <uvosunwrap/charuco.h>
#include <uvosunwrap/unwrap.h>
#include <opencv2/imgproc.hpp>
#include <QDebug>

ConfigureCameraDialog::ConfigureCameraDialog(const CameraSetup& setup, std::shared_ptr<Camera> camera, double exposureTime, bool nodistort, QWidget *parent):
	QDialog(parent),
	setup_(setup),
	camera_(camera),
	profileExposure_(exposureTime),
	nodistort_(nodistort),
	ui(new Ui::ConfigureCameraDialog)
{
	ui->setupUi(this);
	qDebug()<<"profileExposure_"<<profileExposure_;
	ui->doubleSpinBox->setValue(profileExposure_);
	setExposure(profileExposure_);

	if(nodistort)
	{
		ui->ledRemap->setHidden(true);
		ui->label_2->setHidden(true);
		ui->pushButtonRemapClear->setHidden(true);
		ui->pushButtonRemapCreate->setHidden(true);
	}

	switch(setup.bayerMode)
	{
		case cam::Camera::BAYER_BLUE:
			ui->comboBox_bayer->setCurrentIndex(0);
			break;
		case cam::Camera::BAYER_GREEN:
			ui->comboBox_bayer->setCurrentIndex(1);
			break;
		case cam::Camera::BAYER_RED:
			ui->comboBox_bayer->setCurrentIndex(2);
			break;
		case cam::Camera::BAYER_PASSTHOUGH:
			ui->comboBox_bayer->setCurrentIndex(3);
			break;
		case cam::Camera::BAYER_DEBAYER:
			ui->comboBox_bayer->setCurrentIndex(4);
			break;
		default:
			break;
	}

	ui->doubleSpinBoxGain->setValue(setup_.gain);

	camera_->cam()->setGain(setup_.gain);
	camera_->cam()->setBayerMode(setup_.bayerMode);

	uint64_t min, max;
	camera_->cam()->getExposureTimeLimits(min, max);
	ui->doubleSpinBox->setMaximum(max/1000000.0);
	ui->doubleSpinBox->setMinimum(min/1000000.0);

	connect(ui->doubleSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ConfigureCameraDialog::setExposure);
	connect(ui->pushButtonBgClear, &QPushButton::clicked, [this](){setup_.bgmask.release(); checkConfig();});
	connect(ui->pushButtonRemapClear, &QPushButton::clicked, [this](){setup_.remapMap = RemapMap(); checkConfig();});
	connect(ui->pushButtonDarkImageClear, &QPushButton::clicked, [this](){setup_.darkmap.release(); checkConfig();});
	connect(ui->pushButtonBgCreate, &QPushButton::clicked, this, &ConfigureCameraDialog::captureBg);
	connect(ui->pushButtonRemapCreate, &QPushButton::clicked, this, &ConfigureCameraDialog::captureRemap);
	connect(ui->pushButtonDarkImageCreate, &QPushButton::clicked, this, &ConfigureCameraDialog::captureDark);
	connect(ui->pushButtonCapture, &QPushButton::clicked, this, &ConfigureCameraDialog::takeImage);
	connect(camera_.get(), &Camera::newImage, this, &ConfigureCameraDialog::gotImage);
	connect(ui->pushButtonBgShow, &QPushButton::clicked, [this](){if(setup_.bgmask.data) ui->widget_4->setImage(Camera::Image(setup_.bgmask, 0));});
	connect(ui->pushButtonDarkImageShow, &QPushButton::clicked, [this](){if(setup_.darkmap.data) ui->widget_4->setImage(Camera::Image(setup_.darkmap, 0));});
	connect(ui->comboBox_bayer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ConfigureCameraDialog::bayerIndexChanged);
	connect(ui->doubleSpinBoxGain,  QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ConfigureCameraDialog::setExposure);

	checkConfig();
}

ConfigureCameraDialog::~ConfigureCameraDialog()
{
	delete ui;
}

void ConfigureCameraDialog::setExposure(double value)
{
	 if(!camera_->cam()->setExposureTime(value*1000000.0))
		QMessageBox::warning(this, "Warning", "Failed to set exposure");
	 else
		 qDebug()<<"set exposure to "<<value*1000000.0;
}

void ConfigureCameraDialog::setGain(double value)
{
	 if(!camera_->cam()->setGain(value))
		QMessageBox::warning(this, "Warning", "Failed to set exposure");
	 else
		 qDebug()<<"set gain to "<<value;
	 setup_.gain = value;
}

void ConfigureCameraDialog::bayerIndexChanged(int index)
{
	switch(index)
	{
		case 0:
			setup_.bayerMode = cam::Camera::BAYER_BLUE;
			break;
		case 1:
			setup_.bayerMode = cam::Camera::BAYER_GREEN;
		break;
		case 2:
			setup_.bayerMode = cam::Camera::BAYER_RED;
		break;
		case 3:
			setup_.bayerMode = cam::Camera::BAYER_PASSTHOUGH;
		break;
		case 4:
			setup_.bayerMode = cam::Camera::BAYER_DEBAYER;
		break;
		default:
			break;
	}
	camera_->cam()->setBayerMode(setup_.bayerMode);
	qDebug()<<"set bayer mode to "<<setup_.bayerMode;
}

void ConfigureCameraDialog::gotImage(Camera::Image img)
{
	switch(mode_)
	{
	case MODE_IDLE:
		ui->widget_4->setImage(img);
		break;
	case MODE_BG_GET:
		if(!fgImage.data)
		{
			QMessageBox::information(this, "Remove object", "please remove the item from the sphere");
			fgImage = img.mat;
			takeImage();
		}
		else
		{
			mode_ = MODE_IDLE;
			if(!createMask(img.mat, setup_.bgmask, fgImage))
				QMessageBox::warning(this, "Failed", "Unable to create mask from captured images");
			else
				ui->widget_4->setImage(Camera::Image(setup_.bgmask, camera_->id()));
			fgImage.release();
		}
		break;
	case MODE_DARK_GET:
		mode_ = MODE_IDLE;
		setup_.darkmap = img.mat;
		ui->widget_4->setImage(Camera::Image(setup_.darkmap, camera_->id()));
		break;
	case MODE_REMAP_GET:
		mode_ = MODE_IDLE;
		cv::Mat masked;
		if(setup_.bgmask.data)
			img.mat.copyTo(masked, setup_.bgmask);
		else
			masked = img.mat;
		std::vector<DetectedPoint> points = detectCharucoPoints(img.mat, false);
		if(points.size() < 8)
		{
			QMessageBox::warning(this, "Failed", "Error creating map, insufficant points detected.");
			break;
		}
		RemapMap map;
		if(createRemapMap(masked, map, points))
			setup_.remapMap = map;
		else
			QMessageBox::warning(this, "Failed", "Error creating map");
		for(size_t i = 0; i < points.size(); ++i)
			cv::circle(img.mat, points[i].point, img.mat.cols/50, cv::Scalar(255,255,255), img.mat.cols/200);
		ui->widget_4->setImage(Camera::Image(img.mat, camera_->id()));
		break;
	}
	checkConfig();
}

void ConfigureCameraDialog::takeImage()
{
	camera_->cam()->setExposureTime(ui->doubleSpinBox->value());
	camera_->cam()->setAcquisitionMode(cam::Camera::MODE_SINGLE);
	camera_->cam()->setTriggerMode(cam::Camera::TRIGGER_SOFTWARE);
	camera_->cam()->startAcquisition();
	camera_->cam()->trigger();
}

void ConfigureCameraDialog::captureBg()
{
	QMessageBox::information(this, "Insert object", "Please insert Test Article A");
	mode_ = MODE_BG_GET;
	takeImage();
}

void ConfigureCameraDialog::captureRemap()
{
	QMessageBox::information(this, "Insert object", "Please insert Test Article A.");
	mode_ = MODE_REMAP_GET;
	takeImage();
}

void ConfigureCameraDialog::captureDark()
{
	QMessageBox::information(this, "Cover lense", "Please cover the lense of the camera.");
	mode_ = MODE_DARK_GET;
	ui->doubleSpinBox->setValue(profileExposure_);
	setExposure(profileExposure_);
	takeImage();
}

void ConfigureCameraDialog::accept()
{
	if(checkConfig())
		QDialog::accept();
	else
		QMessageBox::information(this, "Unfinished", "Can not accept unfinished camera setup");
}

bool ConfigureCameraDialog::checkConfig()
{
	bool bgOk = setup_.bgmask.data;
	bool remapMapOk = setup_.remapMap.xMat.data;
	bool darkMapOK = setup_.darkmap.data;

	ui->pushButtonBgClear->setEnabled(bgOk);
	ui->pushButtonDarkImageClear->setEnabled(darkMapOK);
	ui->pushButtonRemapClear->setEnabled(remapMapOk);
	ui->pushButtonDarkImageShow->setEnabled(darkMapOK);
	ui->pushButtonBgShow->setEnabled(bgOk);

	ui->ledBg->setLit(bgOk);
	ui->ledDark->setLit(darkMapOK);
	ui->ledRemap->setLit(remapMapOk);

	return remapMapOk || nodistort_;
}
