/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <QDebug>
#include <QMouseEvent>
#include <QPainter>

#include "MvQAreaSelection.h"
#include "MvQPlotView.h"
#include "MgQPlotScene.h"
#include "MgQLayoutItem.h"

MvQAreaSelection::MvQAreaSelection(MgQPlotScene *scene,MvQPlotView *view,QGraphicsItem *parent) : 
	MvQPlotItem(scene,view,parent)
{	
	pen_=QPen(QColor(0,0,255,230),2);
	pen_.setCosmetic(true);
	
	brush_=QBrush(QColor(164,219,255,180));
	currentAction_=NoAction;
	zoomLayout_=0;
	rect_=QRectF();

	selectionPen_=pen_;
	selectionPen_.setCosmetic(true);
	selectionPen_.setStyle(Qt::DotLine);
	resizeId_=-1;
	physicalHandleSize_=8.;
	handleSize_=8.;
	for(int i=0;i< 8; i++)	
	{
		handle_ << QRectF(0.,0.,handleSize_,handleSize_);		
	}

	showHandles_=false;
	handleHover_=-1;

	
}

MvQAreaSelection::~MvQAreaSelection()
{
}

QRectF MvQAreaSelection::boundingRect() const
{
	checkHandleSize();
	
	//qDebug() << "brect" << rect_;
	return rect_.adjusted(-handleSize_/2.,-handleSize_/2.,handleSize_/2.,handleSize_/2.);
}

void MvQAreaSelection::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
	checkHandleSize();	
	
	painter->setPen(QPen());
	painter->setBrush(brush_);
	painter->drawRect(rect_);

	//Outline
	if(currentAction_==ResizeAction || currentAction_==MoveAction ||
	   showHandles_)
	{
		painter->setPen(selectionPen_);
	}
	else
	{
		painter->setPen(pen_);
	}

	painter->setBrush(QBrush());
	painter->drawRect(rect_);
 
	//Handlers
	if(showHandles_)
	{
		for(int i=0; i < handle_.count(); i++)
		{
			QRectF r=handle_[i];
			r.translate(rect_.topLeft());
			if(handleHover_ == i)
			{
				painter->setPen(Qt::blue);
				painter->setBrush(QColor(164,219,255));
			}
			else
			{
				painter->setPen(Qt::blue);
				painter->setBrush(Qt::blue);
			}
			
			painter->drawRect(r);
		}
	}
}

void MvQAreaSelection::mousePressEventFromView(QMouseEvent *event)
{
	checkHandleSize();	
	
	if(!activated_ || !acceptMouseEvents_)
		return;

	//
	handleHover_=-1;

	if(rect_.isNull())
	{
		//Get scene position
		zoomRectOrigin_ = mapFromScene(plotView_->mapToScene(event->pos()));

		if((zoomLayout_=plotScene_->findProjectorItem(mapToScene(zoomRectOrigin_))) == 0) 
		{
			currentAction_=NoAction;
		}
		else
		{
			currentAction_=DefineAction;
			rect_.setRect(zoomRectOrigin_.x(),zoomRectOrigin_.y(),0,0);	
			setVisible(true);
		}
	}

	else
	{
		float dh=handleSize_/2;
		QRectF cRect=controlRect();

		//qDebug() << "crect" << cRect;

		dragPos_=mapFromScene(plotView_->mapToScene(event->pos()));
				
		if((zoomLayout_=plotScene_->findProjectorItem(mapToScene(cRect.center())))!= 0 &&
		   cRect.adjusted(-dh,-dh,dh,dh).contains(dragPos_))
		{
			QPointF xp=dragPos_-cRect.topLeft();

			//qDebug() << "xp" << dragPos_<< xp << rect_.topLeft();

			updateHandles();

			resizeId_=-1;
			for(int i=0; i < handle_.count(); i++)
			{
				//qDebug() << "handle" << i << handle_[i];
				//If a handle is selected it starts a resize action 
				if(handle_[i].contains(xp))
				{
					resizeId_=i;

					Qt::CursorShape shape;
					
					switch(resizeId_)
					{
					case 0: shape=Qt::SizeBDiagCursor; break;
					case 1: shape=Qt::SizeHorCursor; break;	
					case 2: shape=Qt::SizeFDiagCursor; break;
					case 3: shape=Qt::SizeVerCursor; break;
					case 4: shape=Qt::SizeBDiagCursor; break;
					case 5: shape=Qt::SizeHorCursor; break;
					case 6: shape=Qt::SizeFDiagCursor; break;
					case 7: shape=Qt::SizeVerCursor; break;
					default: break;
					}

					setCursor(QCursor(shape));
					currentAction_=ResizeAction;

					//qDebug() << "resizeId" << resizeId_;
					return;
				}
			}		

			//If no resize action it checks if the drag point is 
			//in the selection rentangle and starts a move action 
			if(resizeId_== -1 && cRect.contains(dragPos_))
			{
				currentAction_=MoveAction;
				setCursor(QCursor(Qt::SizeAllCursor));
			}
			//Otherwise unselect
			else
			{
				currentAction_=NoAction;
				dragPos_=QPointF();
				if(showHandles_)
				{
					showHandles_=false;
					update();
				}
			}	

		}
		else
		{
			currentAction_=NoAction;
			dragPos_=QPointF();

			if(showHandles_)
			{
				showHandles_=false;
				update();
			}		
		}
	}
}

void MvQAreaSelection::mouseMoveEventFromView(QMouseEvent *event)
{
	if(!activated_ || !acceptMouseEvents_)
		return;

	//qDebug() << "mouseMove in area";

	QPointF pos = mapFromScene(plotView_->mapToScene(event->pos()));
	
	if(currentAction_ == DefineAction)
	{
		QPointF dp = pos - zoomRectOrigin_;

		if(dp.x() <= 0. || dp.y() >= 0.)
			return;

		QRectF r=updateControlRect(currentAction_,dp);
		if(r.isEmpty())
		{
			return;
		}
		
		prepareGeometryChange();
		updateItemByControlRect(r);
		update();
	}
	else if(currentAction_ == ResizeAction)
	{	
		QPointF dp = pos - dragPos_;

		qDebug() << "resize" << pos << dragPos_;
				
		//Current control rect
		QRectF rOri=controlRect();
		QRectF r=updateControlRect(currentAction_,dp);
	
		
		if(r.isEmpty() || 
		   fabs(r.width()) <= 8. || fabs(r.height()) <=8. || 
		   r.width() < 0. || r.height() < 0.)
		{
			
			dragPos_=pos;
			return;
		}
		
		qDebug() << "resize" << r;
		
		if(r.width() < rOri.width() || r.height() < rOri.height())
		{
			QRectF plotRect=mapRectFromScene(zoomLayout_->mapToScene(zoomLayout_->boundingRect()).boundingRect());
			if(!plotRect.contains(pos))
			{
			  	qDebug() << "   resize" << "out";
			  	dragPos_=pos;
				return;
			}
		}
		
		
		

		prepareGeometryChange();
		updateItemByControlRect(r);
		dragPos_=pos;
		showHandles_=false;
		update();		
	}
	else if(currentAction_ == MoveAction)
	{	
		QPointF dp = pos - dragPos_;

		//qDebug() << "move" << dp << rect_; 


		QRectF r=updateControlRect(currentAction_,dp);
		if(r.isEmpty())
		{
			return;
		}

		prepareGeometryChange();
		updateItemByControlRect(r); 
		dragPos_=pos;
		showHandles_=false;
		update();		
	}
	else if(event->buttons() ==  Qt::NoButton)
	{
		if(showHandles_==false)
			return;

		QRectF r=controlRect(); //controlRect
		QPointF dp= pos-r.topLeft();

		updateHandles();
		int oriHover=handleHover_;
		handleHover_=-1;
		for(int i=0; i < handle_.count(); i++)
		{			
			if(handle_[i].contains(dp))
			{
				handleHover_=i;	
				if(handleHover_ != oriHover)
				{
					update();
				}
				break;
			}
		}
		
		if(oriHover != -1)
		{
			update();
		}	
	}
}

 void MvQAreaSelection::mouseReleaseEventFromView(QMouseEvent *event)
 {
	if(!activated_ || !acceptMouseEvents_)
		return;

	QPointF pos = mapFromScene(plotView_->mapToScene(event->pos()));

	if(currentAction_ == DefineAction)
	{	
		QPointF dp = pos - zoomRectOrigin_;
		if(dp.x() <= 0. || dp.y() >= 0.)
		{
			clearArea();
			return;
		}

		QRectF r=updateControlRect(currentAction_,dp);
		if(r.isEmpty() || fabs(r.width()) < 8. || fabs(r.height()) <8.  )
		{
			clearArea();
			return;
		}
		
		prepareGeometryChange();
		updateItemByControlRect(r);
		update();

		QPointF gp1,gp2;
		getAreaGeoCorners(gp1,gp2);

		currentAction_=NoAction;
		zoomLayout_=0;
		coordPoint_.clear();
		coordPoint_ << gp1 << gp2;

		//blLat,blLon,trLat,trLon
		emit areaIsDefined(gp1.y(),gp1.x(),gp2.y(),gp2.x());
	}	
	else if(currentAction_ == ResizeAction)
	{
		QPointF dp = pos - dragPos_;

		QRectF r=updateControlRect(currentAction_,dp);

		if(r.isEmpty() || 
		   fabs(r.width()) < 8. || fabs(r.height()) <8. || 
		   r.width() < 0. || r.height() < 0.)
		{
			r=rect_;
			//clearArea();
			//return;
		}
		
		prepareGeometryChange();
		updateItemByControlRect(r);
		//update();

		QPointF gp1,gp2;
		getAreaGeoCorners(gp1,gp2);

		currentAction_=NoAction;
		zoomLayout_=0;
		coordPoint_.clear();
		coordPoint_ << gp1 << gp2;
		dragPos_=QPointF();

		//blLat,blLon,trLat,trLon
		emit areaIsDefined(gp1.y(),gp1.x(),gp2.y(),gp2.x());

		updateHandles();
		showHandles_=true;
		update();	
		unsetCursor();
	}	
	else if(currentAction_ == MoveAction)
	{
		QPointF dp = pos - dragPos_;

		QRectF r=updateControlRect(currentAction_,dp);
		if(r.isEmpty())
		{
			return;
		}

		prepareGeometryChange();
		updateItemByControlRect(r); 

		QPointF gp1,gp2;
		getAreaGeoCorners(gp1,gp2);

		currentAction_=NoAction;
		zoomLayout_=0;
		coordPoint_.clear();
		coordPoint_ << gp1 << gp2;
		dragPos_=QPointF();

		//blLat,blLon,trLat,trLon
		emit areaIsDefined(gp1.y(),gp1.x(),gp2.y(),gp2.x());

		updateHandles();
		showHandles_=true;
		update();	
		unsetCursor();
	}	
 }


void MvQAreaSelection::setArea(double blLat,double blLon,double trLat,double trLon)
{
	//double west, double north, double east, double south)		
	QPointF sp1,sp2;

	if(rect_.isEmpty())
	{		
	  	if((zoomLayout_=plotScene_->firstProjectorItem()) == 0)
		{
			return;
		}	
	}
	else
	{
		sp1.setX(rect_.x());
		sp1.setY(rect_.y());
		if((zoomLayout_=plotScene_->findProjectorItem(mapToScene(sp1))) == 0)
		{
			return;

		}
	}

	QPointF gp1(blLon,blLat);
	QPointF gp2(trLon,trLat);

	if(zoomLayout_->containsGeoCoords(gp1) && 
	   zoomLayout_->containsGeoCoords(gp2))
	{
		zoomLayout_->mapFromGeoToSceneCoords(gp1,sp1);	
		zoomLayout_->mapFromGeoToSceneCoords(gp2,sp2);

		sp1=mapFromScene(sp1);
		sp2=mapFromScene(sp2);
				
		//qDebug() << gp1 << gp2 << sp1 << sp2;

		QRectF r=QRectF(sp1.x(),sp1.y(),sp2.x()-sp1.x(),sp2.y()-sp1.y());
		if(r.width() <0 || r.height() < 0)
		{
			if(rect_.isEmpty() == false)
			{	
				getAreaGeoCorners(gp1,gp2);
				//blLat,blLon,trLat,trLon
				emit areaIsDefined(gp1.y(),gp1.x(),gp2.y(),gp2.x());
			}
			return;
		}
		//qDebug() << rect_;
	
		coordPoint_.clear();
		coordPoint_ << gp1 << gp2;
		prepareGeometryChange();
		rect_=r;
		updateHandles();
		update();
	}
	else
	{
		QPointF gp1,gp2;
		getAreaGeoCorners(gp1,gp2);
		//blLat,blLon,trLat,trLon
		emit areaIsDefined(gp1.y(),gp1.x(),gp2.y(),gp2.x());
	}
}
	
void MvQAreaSelection::clearArea()
{
	currentAction_=NoAction;
	prepareGeometryChange();
	rect_=QRectF();
	coordPoint_.clear();
	showHandles_=false;
	update();
	emit areaIsUndefined();
}


// It should be called only after zoom!!!
// If the projection is changed the area should be set "undefined"!!

void MvQAreaSelection::reset()
{
	if(coordPoint_.isEmpty() || rect_.isNull())
		return;

	//We need to find the projectorItem!!
	QPointF sp0,sp1;

	if((zoomLayout_=plotScene_->firstProjectorItem()) == 0)
	{
		clearArea();
		return;
	}

	QPointF gp0=coordPoint_[0];
	QPointF gp1=coordPoint_[1];

	//qDebug() << coordPoint_;

	if(zoomLayout_->containsGeoCoords(gp0) && 
	   zoomLayout_->containsGeoCoords(gp1))
	{
		zoomLayout_->mapFromGeoToSceneCoords(gp0,sp0);	
		zoomLayout_->mapFromGeoToSceneCoords(gp1,sp1);

		sp0=mapFromScene(sp0);
		sp1=mapFromScene(sp1);
				
		
		//qDebug() << sp0 << sp1;

		prepareGeometryChange();
		rect_=QRectF(sp0.x(),sp0.y(),sp1.x()-sp0.x(),sp1.y()-sp0.y());
		updateHandles();
		update();
	}
	else
	{
		clearArea();
	}
}

void MvQAreaSelection::selectAllArea()
{
	if((zoomLayout_=plotScene_->firstProjectorItem()) != 0)
	{
		prepareGeometryChange();
		rect_=mapRectFromScene(zoomLayout_->mapRectToScene(zoomLayout_->boundingRect()));

		QPointF gp1,gp2;
		getAreaGeoCorners(gp1,gp2); //this requires zoomLayout_ !!!
		coordPoint_.clear();
		coordPoint_ << gp1 << gp2;

		currentAction_=NoAction;
		zoomLayout_=0;
		dragPos_=QPointF();

		showHandles_=false;
		update();

		//blLat,blLon,trLat,trLon
		emit areaIsDefined(gp1.y(),gp1.x(),gp2.y(),gp2.x());
	}
}

void MvQAreaSelection::updateHandles()
{
	QRectF r=controlRect();

	checkHandleSize();	
	
	float dh=handleSize_/2;
	float w=r.width();
	float h=r.height();

	//Check handles!!!
	handle_[0].moveTo(-dh,-dh);
	handle_[1].moveTo(-dh,h/2.-dh);
	handle_[2].moveTo(-dh,h-dh);
	handle_[3].moveTo(w/2.-dh,h-dh);
	handle_[4].moveTo(w-dh,h-dh);
	handle_[5].moveTo(w-dh,h/2.-dh);
	handle_[6].moveTo(w-dh,-dh);	
	handle_[7].moveTo(w/2.-dh,-dh);
}


void MvQAreaSelection::setActivated(bool b)
{
	if(showHandles_==true)
	{
		showHandles_=false;
		update();
	}
	activated_=b;
}




QRectF MvQAreaSelection::updateControlRect(CurrentAction action,QPointF &dp)
{
	//Current control rect
	QRectF r=controlRect();

	if(!zoomLayout_)
	{
		return QRectF();
	}

	//Plot area in local coordinates
	QRectF plotRect=mapRectFromScene(zoomLayout_->mapToScene(zoomLayout_->boundingRect()).boundingRect());

	if(action == DefineAction)
	{
		r=QRectF(zoomRectOrigin_.x(),zoomRectOrigin_.y()+dp.y(),dp.x(),-dp.y());
		
		if(r.right() > plotRect.right())
		{
			dp.setX(dp.x()-(r.right()-plotRect.right()));
		}
		else if(r.left() < plotRect.left())
		{
			dp.setX(dp.x()+plotRect.left()-r.left());	
		}
		if(r.bottom() > plotRect.bottom())
		{
			dp.setY(dp.y()-(r.bottom()-plotRect.bottom()));	
		}
		else if(r.top() < plotRect.top())
		{
			dp.setY(dp.y()+plotRect.top()-r.top());	
		}

		r=QRectF(zoomRectOrigin_.x(),zoomRectOrigin_.y()+dp.y(),dp.x(),-dp.y());
	}
	else if(action == ResizeAction)
	{
		r=rect_;
		
		switch(resizeId_)
		{
		case 0:
			r.adjust(dp.x(),dp.y(),0,0);
			break;
		case 1:
			r.adjust(dp.x(),0,0,0);
			break;	
		case 2:
			r.adjust(dp.x(),0,0,dp.y());
			break;	
		case 3:
			r.adjust(0,0,0,dp.y());
			break;	
		case 4:
			r.adjust(0,0,dp.x(),dp.y());
			break;	
		case 5:
			r.adjust(0,0,dp.x(),0);
			break;	
		case 6:
			r.adjust(0,dp.y(),dp.x(),0);
			break;	
		case 7:
			r.adjust(0,dp.y(),0,0);
			break;	
		default:
			break;
		}
		
		if(r.width() < 0)
		{
			//r.setWidth(8.);
			r=rect_;
		}
		if(r.height() <0)
		{
			//r.setHeight(8.);
			r=rect_;
		}

		//qDebug() << "RESIZE" << rect_ << r;

		r=plotRect.intersected(r);
	}
	else if(action == MoveAction)
	{
		r=rect_.translated(dp);

		if(r.right() > plotRect.right())
		{
			dp.setX(dp.x()-(r.right()-plotRect.right()));
		}
		else if(r.left() < plotRect.left())
		{
			dp.setX(dp.x()+plotRect.left()-r.left());	
		}
		if(r.bottom() > plotRect.bottom())
		{
			dp.setY(dp.y()-(r.bottom()-plotRect.bottom()));	
		}
		else if(r.top() < plotRect.top())
		{
			dp.setY(dp.y()+plotRect.top()-r.top());	
		}

		r=rect_.translated(dp);
	}
	

	QPointF p1=mapToScene(r.topLeft());
	QPointF p2=mapToScene(r.bottomRight());

	if(!zoomLayout_->containsSceneCoords(p1) ||
	   !zoomLayout_->containsSceneCoords(p2))
	{
		//qDebug() << "false";
		return QRectF();;
	}

	return r;
}

void MvQAreaSelection::updateItemByControlRect(QRectF r)
{
	rect_=r;
}

QRectF MvQAreaSelection::controlRect()
{
	return rect_;

}

void MvQAreaSelection::getAreaGeoCorners(QPointF &gp1,QPointF &gp2)
{
	if(!zoomLayout_)
		return;

	//Get geo coordinates of the bl and tr rect corners on the map.
	//The y axis is inverted!! So we need tl and br corners of the
	//qt rectangle!!!!!!!
	QPointF sp1=mapToScene(rect_.topLeft());
	QPointF sp2=mapToScene(rect_.bottomRight());
	zoomLayout_->mapFromSceneToGeoCoords(sp1,gp1);
	zoomLayout_->mapFromSceneToGeoCoords(sp2,gp2);
	
	
}

void MvQAreaSelection::checkHandleSize() const
{  
	handleSize_=physicalHandleSize_/parentItem()->scale();
	for(int i=0;i< 8; i++)	
	{
		handle_[i].setWidth(handleSize_);
		handle_[i].setHeight(handleSize_);
	}
	
}