#include "MapView2D.h"

#include <QPainter>
#include <QResizeEvent>
#include <QSlider>
#include <QGridLayout>
#include <QGestureEvent>

#include <Indoor/floorplan/v2/Floorplan.h>
#include <Indoor/floorplan/v2/FloorplanHelper.h>

#include "Floor2D.h"
#include "ColorPoints2D.h"
#include "Path2D.h"
#include "WiFiCalibTool.h"

#include "../../Icons.h"
#include "../../UIHelper.h"


MapView2D::MapView2D(QWidget *parent) : QWidget(parent) {

	setFocusPolicy(Qt::StrongFocus);
	setRenderHeight(0);


	colorPoints = new ColorPoints2D();
	elementsB.push_back(colorPoints);

	pathToDest = new Path2D();
	pathToDest->setColor(Qt::blue);
	pathToDest->setWidth(10);
	elementsB.push_back(pathToDest);

	pathWalked = new Path2D();
	pathToDest->setColor(Qt::black);
	pathToDest->setWidth(5);
	elementsB.push_back(pathWalked);

	// buttons
	//menu = new QWidget(this);
	QGridLayout* lay = new QGridLayout(this);
	int row = 0;


	lay->addItem(new QSpacerItem(0,0,QSizePolicy::Minimum,QSizePolicy::Expanding), row, 0, 1, 1);

	++ row;

//	// map-layer slider
//	sldLayer = new QSlider();
//	sldLayer->setOrientation(Qt::Horizontal);
//	connect(sldLayer, &QSlider::sliderMoved, this, &MapView2D::onLayerSelect);
//	connect(sldLayer, &QSlider::sliderReleased, this, &MapView2D::onLayerSelect);
//	lay->addWidget(sldLayer, row, 0, 1, 1);

	// show/hide button
	const int bs = UIHelper::getButtonSize(this);
	btnColorPoints = new QPushButton(Icons::getIcon("dots", bs), "");
	btnColorPoints->connect(btnColorPoints, &QPushButton::clicked, [&] () {colorPoints->setVisible(!colorPoints->isVisible()); emit update();} );
	lay->addWidget(btnColorPoints, row, 0, 1, 1);

	btnLayerMinus = new QPushButton("-");
	connect(btnLayerMinus, &QPushButton::clicked, this, &MapView2D::onLayerMinus);
	lay->addWidget(btnLayerMinus, row, 1, 1, 1);

	btnLayerPlus = new QPushButton("+");
	connect(btnLayerPlus, &QPushButton::clicked, this, &MapView2D::onLayerPlus);
	lay->addWidget(btnLayerPlus, row, 2, 1, 1);


	// start with invisible particles. speeds things up a bit
	colorPoints->setVisible(false);

	// we want to receive pinch gestures
	//setAttribute(Qt::WA_AcceptTouchEvents, true);
	grabGesture(Qt::PinchGesture);

}

void MapView2D::onLayerSelect() {
	setRenderHeight(sldLayer->value());
}

void MapView2D::onLayerMinus() {
	if (layerHeight_m <= 0) {return;}
	layerHeight_m -= 1;
	setRenderHeight(layerHeight_m);
}
void MapView2D::onLayerPlus() {
	if (layerHeight_m >= 16) {return;}
	layerHeight_m += 1;
	setRenderHeight(layerHeight_m);
}

void MapView2D::setMap(WiFiCalibrationDataModel* mdl, Floorplan::IndoorMap* map) {

	for (Floorplan::Floor* floor : map->floors) {
		Floor2D* f = new Floor2D(floor);
		elementsA.push_back(f);
	}

	wifiCalib = new WiFiCalibTool(mdl, map);
	elementsB.push_back(wifiCalib);

	const BBox3 bbox3 = FloorplanHelper::getBBox(map);
	const BBox2 bbox2 = BBox2(bbox3.getMin().xy(), bbox3.getMax().xy());

	scaler.setMapBBox(bbox2);
	scaler.setCenterM(Point2(bbox2.getCenter().x, bbox2.getCenter().y));

}

void MapView2D::showParticles(const std::vector<K::Particle<MyState>>* particles) {
	this->colorPoints->setFromParticles(*particles);
}

void MapView2D::setCurrentEstimation(const Point3 pos_m, const Point3 dir) {
	(void) dir;
	setRenderHeight(pos_m.z);
	scaler.setCenterM(pos_m.xy());
}

void MapView2D::setRenderHeight(const float height_m) {
	renderParams.clip.aboveHeight_m = height_m + 1.5;
	renderParams.clip.belowHeight_m = height_m - 1.5;
	emit update();
}

void MapView2D::showGridImportance(Grid<MyGridNode>* grid) {
	colorPoints->showGridImportance(grid);
}

void MapView2D::resizeEvent(QResizeEvent* evt) {
	(void) evt;
	int s = UIHelper::getButtonSize(this->parent()) * 1.5;
	//sldLayer->setMinimumHeight(s);
	//sldLayer->setMinimum(0);
	//sldLayer->setMaximum(16);
	btnColorPoints->setMinimumHeight(s);
	btnColorPoints->setMinimumWidth(s);
	btnLayerMinus->setMinimumHeight(s);
	btnLayerMinus->setMinimumWidth(s);
	btnLayerPlus->setMinimumHeight(s);
	btnLayerPlus->setMinimumWidth(s);

	scaler.setScreenSize(width(), height());
	scaler.setScale( UIHelper::isLarge(this->parent()) ? 2 : 1 );

}

void MapView2D::mousePressEvent(QMouseEvent* evt) {
	move.startCenter_px = scaler.getCenterPX();
	move.startMouse_px = Point2(evt->x(), evt->y());
}

void MapView2D::mouseMoveEvent(QMouseEvent* evt) {
	Point2 pt(evt->x(), evt->y());
	pt -= move.startMouse_px;
	pt.x = -pt.x;
	pt += move.startCenter_px;
	scaler.setCenterPX(pt);
	emit update();
}

void MapView2D::mouseReleaseEvent(QMouseEvent* evt) {

	if (!wifiCalib) {return;}

	const Point2 p1(evt->x(), evt->y());

	// fingerprint node pressed?
	const int fpSize = UIHelper::isLarge(this->parent()) ? (40) : (25);
	int idx = 0;
	for (const Point2 p2 : wifiCalib->getNodes()) {
		const float dist = p1.getDistance(p2);
		if (dist < fpSize) { wifiCalib->selectNode(idx); emit update(); break; }
		++idx;
	}

}

void MapView2D::wheelEvent(QWheelEvent* event) {
	if (event->delta() < 0) {
		scaler.mulScale(0.5);
		emit update();
	} else {
		scaler.mulScale(2.0);
		emit update();
	}
}

void MapView2D::paintEvent(QPaintEvent*) {

	QPainter qp(this);

	// clear
	qp.fillRect(0, 0, width(), height(), Qt::white);

	// render elements
	for (Renderable2D* r : elementsA) {r->render(qp, scaler, renderParams);}
	for (Renderable2D* r : elementsB) {r->render(qp, scaler, renderParams);}

	qp.end();

}

bool MapView2D::event(QEvent *event) {

	switch (event->type()) {
		case QEvent::Gesture:
			return gestureEvent(static_cast<QGestureEvent*>(event));

		case QEvent::TouchBegin:
		case QEvent::TouchUpdate:
		case QEvent::TouchEnd:

			// prevent [additional] mouse events for undetected gestures [more than 1 finger]
			// TODO: not yet stable... improvements?
			if (static_cast<QTouchEvent*>(event)->touchPoints().count() == 2) {return true;}
			break;

		default:
			break;
	}

	// event not consumed. bubble it to following stages.
	// this will e.g. generate mouseMoveEvent etc.
	return QWidget::event(event);

}

bool MapView2D::gestureEvent(QGestureEvent *event) {

	if (QGesture* pinch = event->gesture(Qt::PinchGesture)) {
		const QPinchGesture* pg = static_cast<QPinchGesture *>(pinch);
		scaler.mulScale(pg->scaleFactor());
		emit update();
		return true;	// event consumed
	}

	// event not consumed
	return false;

}
