#include "cvimageviewer.h"
#include <QPicture>
#include <QDebug>
#include <QMouseEvent>
#include <QFileDialog>
#include <opencv2/imgcodecs.hpp>
#include <statisticsdialog.h>

CvImageViewer::CvImageViewer(QWidget *parent, size_t lastId) :
	QWidget(parent),
	lastId_(lastId),
	saveAction_("Save Image", nullptr),
	zoomAction_("Zoom to selection", nullptr),
	resetAction_("Reset Zoom", nullptr),
	statisticsAction_("Get selection properties", nullptr),
	xPlotAction_("Plot x axis", nullptr),
	yPlotAction_("Plot y axis", nullptr),
	roi_(0,0,0,0)
{
	qimage_.load(":/images/noimage.png");

	connect(&saveAction_, &QAction::triggered, this, &CvImageViewer::saveImage);
	connect(&zoomAction_, &QAction::triggered, this, &CvImageViewer::zoomToSelection);
	connect(&resetAction_, &QAction::triggered, this, &CvImageViewer::resetZoom);
	connect(&statisticsAction_, &QAction::triggered, this, &CvImageViewer::showSatDiag);
	connect(&xPlotAction_, &QAction::triggered, this, &CvImageViewer::plotOnX);
	connect(&yPlotAction_, &QAction::triggered, this, &CvImageViewer::plotOnY);

	imageContextMenu_.addAction(&saveAction_);
	imageContextMenu_.addAction(&zoomAction_);
	imageContextMenu_.addAction(&resetAction_);
	imageContextMenu_.addAction(&statisticsAction_);
	imageContextMenu_.addAction(&xPlotAction_);
	imageContextMenu_.addAction(&yPlotAction_);

	plot.setMinimumWidth(800);
	plot.setMinimumHeight(600);

	setMouseTracking(true);
}

CvImageViewer::~CvImageViewer()
{
}

void CvImageViewer::saveImage()
{
	QString fileName;
	if(origImage_.type() == CV_8UC3 || origImage_.type() == CV_8SC3 || origImage_.type() == CV_8UC1 || origImage_.type() == CV_8SC1)
	{
		fileName = QFileDialog::getSaveFileName(this, "Save Image", lastSavePath_, "*.mat *.png" );
	}
	else
	{
		fileName = QFileDialog::getSaveFileName(this, "Save Image", lastSavePath_, "*.mat" );
	}
	if(!fileName.isEmpty())
	{
		lastSavePath_= fileName.mid(0, fileName.lastIndexOf('/'));
		QStringList tokens = fileName.split('.');
		if(tokens.back() != "mat" && tokens.back() != "png")
			fileName.append(".mat");
		tokens = fileName.split('.');
		if(tokens.back() == "png")
		{
			imwrite(fileName.toStdString(), origImage_);
		}
		else
		{
			cv::FileStorage matf(fileName.toStdString(), cv::FileStorage::WRITE);
			matf<<"image"<<origImage_;
			matf.release();
		}
	}
}

cv::Rect CvImageViewer::roiFromSelection()
{
	int xA;
	int yA;
	int xB;
	int yB;
	transfromToSourceCoordinates(selectionRect_.x(), selectionRect_.y(), xA, yA);
	transfromToSourceCoordinates(selectionRect_.x()+selectionRect_.width(), selectionRect_.y()+selectionRect_.height(), xB, yB);
	selectionRect_ = QRect(0, 0, 0, 0);
	cv::Rect roi;
	roi = cv::Rect(std::min(xA, xB), std::min(yA, yB), std::abs(xA-xB), std::abs(yA-yB));
	return roi;
}

void CvImageViewer::showSatDiag()
{
	qDebug()<<selectionRect_.width()<<selectionRect_.height();
	if(!origImage_.data || origImage_.channels() > 1 || abs(selectionRect_.width()) < 2 || abs(selectionRect_.height()) < 2)
		return;

	cv::Mat roiImage = origImage_(roiFromSelection());
	if(roiImage.type() != CV_64FC1)
		roiImage.convertTo(roiImage, CV_64FC1);

	std::vector<double> data;
	data.reserve(roiImage.total());
	for (cv::MatIterator_<double> it = roiImage.begin<double>(); it != roiImage.end<double>(); ++it)
		data.push_back(*it);

	StatisticsDialog diag(data, this);
	diag.show();
	diag.exec();
}

void CvImageViewer::setClamp(double max)
{
	if(origImage_.data)
	{
		clamp_ = max;
		convertImage(origImage_);
		update();
		roi_ = cv::Rect(0, 0, image_.size().width, image_.size().height);
	}
}

void CvImageViewer::convertImage(cv::Mat image)
{
	image_ = image;
	if(image_.type() == CV_8UC3 || image_.type() == CV_8SC3)
	{
		qimage_ = QImage(image_.data, image_.cols, image_.rows, image_.step, QImage::Format_RGB888);
		statisticsAction_.setDisabled(true);
		xPlotAction_.setDisabled(true);
		yPlotAction_.setDisabled(true);
	}
	else if(image_.type() == CV_8UC1 || image_.type() == CV_8SC1)
	{
		qimage_ = QImage(image_.data, image_.cols, image_.rows, image_.step, QImage::Format_Grayscale8);
		statisticsAction_.setDisabled(false);
	}
	else if(image_.type() == CV_32FC1 || image_.type() == CV_64FC1)
	{
		double min, max;
		cv::minMaxIdx(image_, &min, &max);
		sigMax(max);
		if(max > clamp_)
		{
			max = clamp_;

			qDebug()<<"clamped to"<<clamp_;
		}
		double a = 255.0/(max - min);
		double b = 0-(min*a);
		qDebug()<<min<<max<<a<<b;
		image_.convertTo(image_, CV_8UC1, a, b);
		qimage_ = QImage(image_.data, image_.cols, image_.rows, image_.step, QImage::Format_Grayscale8);
		statisticsAction_.setDisabled(false);
		xPlotAction_.setDisabled(false);
		yPlotAction_.setDisabled(false);
	}
	else if(image_.type() == CV_32FC3 || image_.type() == CV_64FC3)
	{
		double min, max;
		cv::minMaxIdx(image_, &min, &max);
		sigMax(max);
		if(max > clamp_)
			max = clamp_;
		double a = 255.0/(max - min);
		double b = 0-(min*a);
		qDebug()<<min<<max<<a<<b;
		image_.convertTo(image_, CV_8UC3, a, b);
		qimage_ = QImage(image_.data, image_.cols, image_.rows, image_.step, QImage::Format_Grayscale8);
		statisticsAction_.setDisabled(true);
		xPlotAction_.setDisabled(true);
		yPlotAction_.setDisabled(true);
	}
	else
	{
		image_.convertTo(image_, CV_8UC1, 255, 0);
		qimage_ = QImage(image_.data, image_.cols, image_.rows, image_.step, QImage::Format_Grayscale8);
		statisticsAction_.setDisabled(true);
		xPlotAction_.setDisabled(true);
		yPlotAction_.setDisabled(true);
	}
}

void CvImageViewer::setImage(Camera::Image img)
{
	origImage_=img.mat;
	clamp_ = std::numeric_limits<double>::max();
	qDebug()<<"viwer got"<<image_.rows<<'x'<<image_.cols<<" type "<<image_.type()<<"image from camera"<<img.cameraId;
	convertImage(img.mat);
	update();
	roi_ = cv::Rect(0, 0, image_.size().width, image_.size().height);
}

void CvImageViewer::transfromToSourceCoordinates(int inX, int inY, int& outX, int& outY)
{
	qDebug()<<roi_.x<<inX<<imgrect_.x()<<imgrect_.width()<<roi_.width;
	outX = roi_.x+(inX-imgrect_.x())/static_cast<double>(imgrect_.width())*roi_.width;
	outY = roi_.y+(inY-imgrect_.y())/static_cast<double>(imgrect_.height())*roi_.height;
}


void CvImageViewer::resetZoom()
{
	selectionRect_ = QRect(0, 0, 0, 0);
	convertImage(origImage_);
	update();
	roi_ = cv::Rect(0, 0, image_.size().width, image_.size().height);
}

void CvImageViewer::zoomToSelection()
{
	if(abs(selectionRect_.width()) > 2 && abs(selectionRect_.height()) > 2)
	{
		convertImage(origImage_);
		roi_ = roiFromSelection();
		cv::Mat cropped;
		cropped = image_(roi_);
		convertImage(cropped);
		update();
	}
}

void CvImageViewer::plotOnX()
{
	if(!xLine_.isNull() && origImage_.data && (origImage_.type() == CV_32FC1 || origImage_.type() == CV_64FC1))
	{
		plot.clear();
		QVector<double> keys;
		QVector<double> values;
		int x, y;
		transfromToSourceCoordinates(xLine_.p1().x(), xLine_.p1().y(), x, y);
		double max = 0;
		for(int i = 0; i < origImage_.cols; ++i)
		{
			keys.push_back(i);
			double value;
			if(origImage_.type() == CV_32FC1)
				value = origImage_.at<float>(y, i);
			else
				value = origImage_.at<double>(y, i);
			if(max < value)
				max = value;
			values.push_back(value);
		}

		plot.setMaxValue(max);

		plot.addData(keys, values, true);
		plot.show();
		plot.repaint();
	}
}

void CvImageViewer::plotOnY()
{
	if(!xLine_.isNull() && origImage_.data && (origImage_.type() == CV_32FC1 || origImage_.type() == CV_64FC1))
	{
		plot.clear();
		QVector<double> keys;
		QVector<double> values;
		int x, y;
		transfromToSourceCoordinates(yLine_.p1().x(), yLine_.p1().y(), x, y);
		double max = 0;
		for(int i = 0; i < origImage_.rows; ++i)
		{
			keys.push_back(i);
			double value;
			if(origImage_.type() == CV_32FC1)
				value = origImage_.at<float>(i, x);
			else
				value = origImage_.at<double>(i, x);
			if(max < value)
				max = value;
			values.push_back(value);
		}

		plot.setMaxValue(max);

		plot.addData(keys, values, true);
		plot.show();
		plot.repaint();
	}
}

void CvImageViewer::mousePressEvent(QMouseEvent *event)
{
	if(origImage_.data)
	{
		if(event->button() == Qt::RightButton)
		{
			saveAction_.setEnabled(origImage_.data);
			imageContextMenu_.popup(event->globalPos());
		}
		else if(origImage_.data && event->x() > imgrect_.x() && event->y() > imgrect_.y() && event->x() < imgrect_.x()+imgrect_.width() && event->y() < imgrect_.y()+imgrect_.height())
		{
			selectionStarted_ = true;
			selectionRect_.setTopLeft(event->pos());
			selectionRect_.setBottomRight(event->pos());
			int x;
			int y;
			transfromToSourceCoordinates(event->x(), event->y(), x, y);
			if(origImage_.type() == CV_32FC1)
			{
				if(x >= 0 && y >= 0 && x <= origImage_.cols && y < origImage_.rows)
					sigValue(x, y, origImage_.at<float>(y,x));
			}
			else
			{
				sigValue(x, y, 0);
			}
		}
	}
	QWidget::mousePressEvent(event);
}

void CvImageViewer::mouseMoveEvent(QMouseEvent *event)
{
	if(origImage_.data && event->x() > imgrect_.x() && event->y() > imgrect_.y() && event->x() < imgrect_.x()+imgrect_.width() && event->y() < imgrect_.y()+imgrect_.height())
	{
		if(selectionStarted_)
		{
			selectionRect_.setBottomRight(event->pos());
		}
		xLine_ = QLine(imgrect_.x(), event->y(), imgrect_.x()+imgrect_.width(), event->y());
		yLine_ = QLine(event->x(), imgrect_.y(), event->x(), imgrect_.y()+imgrect_.height());
		repaint();
	}
	else if(!xLine_.isNull() || !selectionRect_.isNull())
	{
		xLine_ = QLine();
		yLine_ = QLine();
		selectionRect_ = QRect();
		repaint();
	}
}

void CvImageViewer::leaveEvent(QEvent *event)
{
	if((!xLine_.isNull() || !selectionRect_.isNull()) && !imageContextMenu_.isActiveWindow() )
	{
		xLine_ = QLine();
		yLine_ = QLine();
		selectionRect_ = QRect();
		repaint();
	}
	QWidget::leaveEvent(event);
}

void CvImageViewer::mouseReleaseEvent(QMouseEvent *event)
{
	selectionStarted_ = false;
	QWidget::mouseReleaseEvent(event);
}

void CvImageViewer::resizeEvent(QResizeEvent *event)
{
	selectionRect_ = QRect(0, 0, 0, 0);
	QWidget::resizeEvent(event);
}

void CvImageViewer::paintEvent(QPaintEvent* event)
{
	Q_UNUSED(event)
	QPainter painter(this);

	double ratio = qimage_.size().height() / (double)qimage_.size().width();
	if(rect().width()*ratio <= rect().height())
		imgrect_.setRect(0, (rect().height()-rect().width()*ratio)/2, rect().width(), rect().width()*ratio);
	else
		imgrect_.setRect((rect().width()-rect().height()/ratio)/2, 0, rect().height()/ratio, rect().height());
	painter.drawImage(imgrect_, qimage_);

	painter.setPen(QPen(QBrush(QColor(200,200,255,255)),1,Qt::DashLine));
	painter.drawLine(xLine_);
	painter.drawLine(yLine_);
	painter.setPen(Qt::NoPen);
	painter.setBrush(QBrush(QColor(255,255,255,120)));
	painter.drawRect(selectionRect_);
}
