#include "pch.h"
#include "GraphPlott.h"
#include <QDebug>
#include <QPen>
#include <QPushButton>
#include <QLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include "HoverButton.h"
#include <QApplication>
#include <QDesktopWidget>


GraphPlott::GraphPlott(QWidget* parent, bool showSettingsButton, bool showInformationButton, bool showGridButton)
	: Plott(parent)
{
	QVBoxLayout* layoutOuter = new QVBoxLayout(this);
	buttonPanel = new QHBoxLayout();
	dialog = new GraphViewSettingDialog(this, "Settings");
	this->setMouseTracking(true);


	buttonPanel->insertStretch(0);
	displayLabel = new QLabel("");
	buttonPanel->addWidget(displayLabel);

	HoverButton* frameButton = new HoverButton();
	frameButton->setMinimumSize(20, 20);
	frameButton->setMaximumSize(20, 20);
	frameButton->setIcon(QIcon(":/metavis/Resources/frame.svg"));
	frameButton->setHoveredIcon(QIcon(":/metavis/Resources/frame_hovered.svg"));
	frameButton->setAttribute(Qt::WA_TranslucentBackground);
	frameButton->setStyleSheet(frameButton->styleSheet() + "border: none;");
	frameButton->setToolTip("Frame");
	buttonPanel->addWidget(frameButton);
	connect(frameButton, &QPushButton::released, this, &GraphPlott::frameGraphInView);


	if (showGridButton) {
		HoverButton* gridButton = new HoverButton();
		gridButton->setMinimumSize(20, 20);
		gridButton->setMaximumSize(20, 20);
		gridButton->setIcon(QIcon(":/metavis/Resources/gridIcon.svg"));
		gridButton->setHoveredIcon(QIcon(":/metavis/Resources/gridIcon_hovered.svg"));
		gridButton->setAttribute(Qt::WA_TranslucentBackground);
		gridButton->setStyleSheet(gridButton->styleSheet() + "border: none;");
		gridButton->setToolTip("Toggle Grid");
		buttonPanel->addWidget(gridButton);
		connect(gridButton, &QPushButton::released, this, [this]() {this->enableGrid = !this->enableGrid; update(); });
	}
	if (showInformationButton) {
		
		informationButton->setMinimumSize(20, 20);
		informationButton->setMaximumSize(20, 20);
		informationButton->setIcon(QIcon(":/metavis/Resources/information_icon.svg"));
		informationButton->setHoveredIcon(QIcon(":/metavis/Resources/information_icon_hovered.svg"));
		informationButton->setAttribute(Qt::WA_TranslucentBackground);
		informationButton->setStyleSheet(informationButton->styleSheet() + "border: none;");
		informationButton->setToolTip("Information");
		connect(informationButton, &QPushButton::released, this, &GraphPlott::showInformation);
		buttonPanel->addWidget(informationButton);
	}
	
	if (showSettingsButton) {
		HoverButton* settingsButton = new HoverButton();
		settingsButton->setMinimumSize(20, 20);
		settingsButton->setMaximumSize(20, 20);
		settingsButton->setIcon(QIcon(":/metavis/Resources/settingIcon.svg"));
		settingsButton->setHoveredIcon(QIcon(":/metavis/Resources/settingIcon_hovered.svg"));
		settingsButton->setAttribute(Qt::WA_TranslucentBackground);
		settingsButton->setStyleSheet(settingsButton->styleSheet() + "border: none;");
		settingsButton->setToolTip("Settings");
		buttonPanel->addWidget(settingsButton);
		connect(settingsButton, &QPushButton::released, dialog, &GraphViewSettingDialog::openDialog);
	}




	buttonPanel->setSpacing(0);
	buttonPanel->setContentsMargins(0, 0, 0, 0);
	layoutOuter->addLayout(buttonPanel);
	layoutOuter->insertStretch(1);
	layoutOuter->setContentsMargins(0, 0, 0, 0);

	//QMenu
	selectMenu->addAction(displayAreaAction);
	selectMenu->addAction(scratchpadAction);
	displayAreaAction->setShortcut(QKeySequence(Qt::CTRL));
	scratchpadAction->setShortcut(QKeySequence(Qt::ALT));
	scratchpadAction->setEnabled(false);
	//setUpTimer
	timer->setSingleShot(true);
	connect(timer, &QTimer::timeout, this, &GraphPlott::searchForPointUnderCursor);

}

GraphPlott::~GraphPlott()
{
}

void GraphPlott::addSeries(std::vector<GraphDataPoint>* line, std::string runName, QString description, QColor color, GraphPlottSeries::SeriesType type)
{
	if (line->empty()) {
		qDebug() << "Series is empty!";
		return;
	}
	GraphPlottSeries lgs;
	lgs.runName = runName;
	lgs.description = description;
	lgs.data = line;
	lgs.type = type;
	lgs.color = color;
	//set min max x y
	auto pairX = std::minmax_element(lgs.data->begin(), lgs.data->end(), [](const GraphDataPoint& a, const GraphDataPoint& b) -> bool {return a.x < b.x; });
	lgs.xMin = pairX.first->x;
	lgs.xMax = pairX.second->x;
	auto pairY = std::minmax_element(lgs.data->begin(), lgs.data->end(), [](const GraphDataPoint& a, const GraphDataPoint& b) -> bool {return a.y < b.y; });
	lgs.yMin = pairY.first->y;
	lgs.yMax = pairY.second->y;
	seriesVec.push_back(lgs);
}




void GraphPlott::removeRunData(RunData* run)
{
	//Remove all series with this rundata
	auto iter = std::remove_if(seriesVec.begin(), seriesVec.end(), [run](GraphPlottSeries& series) {return (run->name == series.runName); });
	seriesVec.erase(iter, seriesVec.end());
	displayLabel->setText("");
}

void GraphPlott::removeAll()
{
	seriesVec.clear();
}

void GraphPlott::setInformation(QString information)
{
	this->informationButtonString = information;
	popupWidget->setInformation(information);
}

std::vector<GraphPlottSeries>& GraphPlott::getSeriesVector()
{
	return seriesVec;
}

void GraphPlott::resetToDefaultWindow()
{
	frameGraphInView();
}

void GraphPlott::frameGraphInView()
{
	if(seriesVec.empty()) return;
	window.xMin = std::min_element(std::begin(seriesVec), std::end(seriesVec), [](const GraphPlottSeries& a, const GraphPlottSeries& b) -> bool {return a.xMin < b.xMin; })->xMin;
	window.xMax = std::max_element(std::begin(seriesVec), std::end(seriesVec), [](const GraphPlottSeries& a, const GraphPlottSeries& b) -> bool {return a.xMax < b.xMax; })->xMax;
	window.yMin = std::min_element(std::begin(seriesVec), std::end(seriesVec), [](const GraphPlottSeries& a, const GraphPlottSeries& b) -> bool {return a.yMin < b.yMin; })->yMin;
	window.yMax = std::max_element(std::begin(seriesVec), std::end(seriesVec), [](const GraphPlottSeries& a, const GraphPlottSeries& b) -> bool {return a.yMax < b.yMax; })->yMax;
	window.ZoomOut(0.05);
	update();
}

void GraphPlott::setDisplayLabel(QString file)
{
	displayLabel->setText("\"" + file + "\"   ");
}

void GraphPlott::setScratchpad(Scratchpad* pad)
{
	this->pad = pad;
	scratchpadAction->setEnabled(true);
}


void GraphPlott::showInformation()
{
	QPoint position = QCursor::pos();
	popupWidget->setInformation(informationButtonString + graphplottInformation);
	popupWidget->width();
	QRect screen = QApplication::desktop()->screenGeometry();
	if (position.x() + popupWidget->width() > screen.width()) {
		popupWidget->move(screen.width() - popupWidget->width(), position.y());
	}
	else {
		popupWidget->move(position.x(), position.y());

	}
	popupWidget->show();

}

void GraphPlott::drawData(QPainter& painter)
{
	QPen linePen;
	QRect graphDisplayRect = getDisplayRect();
	QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
	double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin);
	double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin);

	for (const GraphPlottSeries& graphSeries : seriesVec) {
		if (!graphSeries.data) {
			qDebug() << "Pointer to nothing pls help";
		}
		if (graphSeries.data->empty() || graphSeries.hide) continue;
		linePen.setColor(graphSeries.color);

		if (graphSeries.type == GraphPlottSeries::SeriesType::Line || graphSeries.type == GraphPlottSeries::SeriesType::LineDot) {
			linePen.setWidth(graphSeries.lineWidth/* + ((&graphSeries == highlightSeries) ? 5 : 0)*/);
			painter.setPen(linePen);
			QPainterPath painterPath;

			QPointF oldpoint = graphSeries.data->at(0).toQPointF();
			painterPath.moveTo(transformGraphToView(oldpoint, stregth_factorX, stregth_factorY, translation));
			for (int i = 1; i < graphSeries.data->size(); i++) {
				QPointF newpoint = graphSeries.data->at(i).toQPointF();
				bool inBoundOldPoint = window.inBoundX(oldpoint);
				bool inBoundNewPoint = window.inBoundX(newpoint);
				if (!inBoundOldPoint && !inBoundNewPoint) {

				}
				else if (!inBoundOldPoint && inBoundNewPoint) {
					painterPath.moveTo(transformGraphToView(oldpoint, stregth_factorX, stregth_factorY, translation));
					painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation));
				}
				else if (inBoundOldPoint && inBoundNewPoint) {
					painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation));
				}
				else if (inBoundOldPoint && !inBoundNewPoint) {
					painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation));
					break;
				}
				oldpoint = newpoint;
			}

			//painterPath.translate(translation);
			painter.drawPath(painterPath);
		}
		if (graphSeries.type == GraphPlottSeries::SeriesType::Dot || graphSeries.type == GraphPlottSeries::SeriesType::LineDot) {
			linePen.setWidth(2);
			linePen.setColor(Qt::transparent);
			painter.setPen(linePen);
			painter.setBrush(graphSeries.color);
			const bool useColor = graphSeries.useDataPointColor;
			for (auto data : *graphSeries.data) {
				if (useColor){
					linePen.setColor(data.color);
					painter.setPen(linePen);
					painter.setBrush(data.color);
				}
				if (window.inBound(data.toQPointF())) {
					painter.drawEllipse(transformGraphToView(data.toQPointF(), stregth_factorX, stregth_factorY, translation), graphSeries.circleRadius, graphSeries.circleRadius);
				}
			}
			painter.setBrush(Qt::BrushStyle::NoBrush);
		}
	}
}

void GraphPlott::mouseMoveEvent(QMouseEvent* event)
{
	Plott::mouseMoveEvent(event);
	lastPosition = event->pos();
	timer->start(stayStillTimeMS);
	QToolTip::hideText();
}

void GraphPlott::handleSelectedWindow(VisibleWindow& window, QMouseEvent* event)
{
	if (event->modifiers() & Qt::ControlModifier) {
		setWindow(window);
	}
	else if (event->modifiers() & Qt::AltModifier) {
		addPointsInWindowToScratchPad(window);
	}
	else {
		QAction* used = selectMenu->exec(this->mapToGlobal(event->pos()));
		if(used == displayAreaAction){
			setWindow(window);
		}
		else if (used == scratchpadAction) {
			addPointsInWindowToScratchPad(window);
		}
	}
	
}

void GraphPlott::searchForPointUnderCursor()
{
	//check if mouse stayed still
	QPoint globalPosition = QCursor::pos();
	QPointF pos = this->mapFromGlobal(globalPosition);
	if (pos != lastPosition) {
		return;
	}
	if (seriesVec.empty() || seriesVec[0].data->empty()) {
		return;
	}

	QRect graphDisplayRect = getDisplayRect();
	QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
	double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin);
	double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin);

	const GraphDataPoint* min = &seriesVec[0].data->at(0);
	double minSqaredDistance = sqaredDistance(transformGraphToView(min->toQPointF(), stregth_factorX, stregth_factorY, translation), pos);
	std::vector<GraphPlottSeries>::iterator actualBestSeries = seriesVec.begin();
	for (auto seriesIter = seriesVec.begin(); seriesIter != seriesVec.end(); seriesIter++) {
		for (const GraphDataPoint& point : *seriesIter->data) {
			double distance = sqaredDistance(transformGraphToView(point.toQPointF(), stregth_factorX, stregth_factorY, translation), pos);
			if (distance <= minSqaredDistance) {
				minSqaredDistance = distance;
				min = &point;
				actualBestSeries = seriesIter;
			}
		}
	}
	if (minSqaredDistance <= 20) {
		QPointF pointInWidget = transformGraphToView(min->toQPointF(), stregth_factorX, stregth_factorY, translation);
		QToolTip::showText(this->mapToGlobal(QPoint(pointInWidget.x(), pointInWidget.y())) - QPoint(0, 35), actualBestSeries->description);
	}
}

double GraphPlott::sqaredDistance(const QPointF& a, const QPointF& b)
{
	return std::pow(a.x() - b.x(), 2) + std::pow(a.y() - b.y(), 2);
}

void GraphPlott::addPointsInWindowToScratchPad(VisibleWindow& window)
{
	if (!pad) {
		qDebug() << "NoPad";
		return;
	}
	for (auto seriesIter = seriesVec.begin(); seriesIter != seriesVec.end(); seriesIter++) {
		if (seriesIter->hide) continue;
		for (const GraphDataPoint& point : *seriesIter->data) {
			if (window.inBound(point.toQPointF()) && point.existLink()) {
				pad->addPoint(*point.orginalPoint);
			}
		}
	}
}

void GraphPlott::keyPressEvent(QKeyEvent* event)
{
	Plott::keyPressEvent(event);
	switch (event->key()) {
	case Qt::Key_T:
		frameGraphInView();
		break;
	default:
		break;
	}
}
