/***************************** 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 <math.h>

#include <QAbstractTextDocumentLayout>
#include <QDebug>
#include <QFile>
#include <QGraphicsDropShadowEffect>
#include <QMouseEvent>
#include <QPainter>
#include <QTextDocument>
#include <QTextStream>

#include "MvQCursorData.h"
#include "MvQPlotView.h"
#include "MgQPlotScene.h"
#include "MgQLayoutItem.h"
#include "MgQSceneItem.h"

#include "Transformation.h"

MvQCursorData::MvQCursorData(MgQPlotScene *scene,MvQPlotView *view,QGraphicsItem *parent) : 
	 MvQPlotItem(scene,view,parent)
{
	dataLayout_=0;
	sceneItem_=0;

	positionMode_ = FollowCursor;
	offsetFromCursor_=QPointF(20,20);
	probeMode_=FollowCursorProbe;

	font_= new QFont();
	font_->setPointSize(10);
	fontMetrics_= new QFontMetrics(*font_);

	//fontPen_=QPen(Qt::white);
	//fontBrush_=QBrush(Qt::white);

	fontPen_=QPen();
	fontBrush_=QBrush();
	
	
	areaPen_=QPen(QColor(255,255,255,190));
	areaBrush_=QBrush(QColor(0,0,255,190)); 

	zoomWasPerformedAfterMouseRelease_=true;
	
	QGraphicsDropShadowEffect *effect=new QGraphicsDropShadowEffect;
	effect->setXOffset(3.);
	effect->setYOffset(3.);
	setGraphicsEffect(effect);
	
	//Read css for the text formatting
	QString cssDoc;
	QFile f(":/uPlot/layerInfo.css");
	QTextStream in(&f);
	if(f.open(QIODevice::ReadOnly | QIODevice::Text))
	{
       		cssDoc=QString(f.readAll());
	}
	f.close();
	
	textDoc_=new QTextDocument();
	textDoc_->setDefaultStyleSheet(cssDoc);

}

MvQCursorData::~MvQCursorData()
{
}

void MvQCursorData::setActivated(bool flag)
{
	MvQPlotItem::setActivated(flag);

	if(flag)
	{
		show();
	}
	else
	{		
		hide();
	}
}

void MvQCursorData::prepareForReset()
{
	dataLayout_=0;
	sceneItem_=0;
}


void MvQCursorData::reset()
{
	setData(cursorPos_);
	update();
}

QRectF MvQCursorData::boundingRect() const
{
	return boundingRect_;
}

void MvQCursorData::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{		
	painter->setPen(QPen());
	painter->setBrush(Qt::white);
	painter->setRenderHint(QPainter::Antialiasing,true);
	painter->drawRoundedRect(boundingRect_.adjusted(1,1,-1,-1),5.,5.);
	
	//Render text	
	painter->translate(0,boundingRect_.height());
	painter->scale(1.,-1.);		
	textDoc_->drawContents(painter);	
}

void MvQCursorData::setPositionMode(MvQCursorData::PositionMode mode)
{
	positionMode_ = mode;

	if(positionMode_ == Anchored)
	{
		setPos(offsetFromCursor_);
	}
}

void MvQCursorData::changePositionMode(QPointF& cursor)
{
	if(positionMode_ ==  Anchored)
	{
		positionMode_=FollowCursor;
	}
	else
	{
		positionMode_=Anchored;
	}

	if(positionMode_ == FollowCursor)
	{
		offsetFromCursor_=pos()-cursor;
	}

}

void MvQCursorData::changeProbeMode(QPointF& cursor)
{
	if(probeMode_ ==  AnchoredProbe)
	{
		probeMode_=FollowCursorProbe;
	}
	else
	{
		probeMode_=AnchoredProbe;
	}
	
	emit probeEnabled(probeMode_==AnchoredProbe);
}

void MvQCursorData::setCursorPos(QPointF& cursor)
{
	cursorPos_=cursor;
	
  	if(positionMode_ == FollowCursor)
	{
		//setPos(cursor+offsetFromCursor_-boundingRect_.topLeft());
		setPos(cursor+offsetFromCursor_);
	}
	else if(probeMode_ == FollowCursorProbe)
	{
		update();
	}
	//update();
}


void MvQCursorData::setText(QPointF coord,QList<ValuesCollector> layerData,QString nameX, QString nameY, bool hasDistance)
{		
	QString s;
	
	s+="<table>";
	s+="<tr><td class=\"first\">" + nameX +"</td><td>";
	s+=QString::number(coord.x(),'f',3);
	s+="</td><td class=\"first\">" + nameY + "</td><td>";
	s+=QString::number(coord.y(),'f',3);
	s+="</td></tr>";	
	s+="</table>";
	
	bool collectedLayerExists=false;
	bool hasValue=false;
	bool hasScaledValue=false;
	bool hasVector=false;
	foreach(ValuesCollector d,layerData)
	{	  
		  if(d.collected())
		  {
		    	collectedLayerExists=true;
			if(d.hasValue())
				hasValue=true;
			if(d.scaled()) 
			  	hasScaledValue=true;  
		  
		  	if(!hasVector && d[0].size() > 0)
			{ 
				//Get values
				currentValues_.clear();
				d[0].at(0)->visit(*this);
				
				if(currentValues_.size() == 2)
				{
				  	hasVector=true;
				}
			}				
		  }
	}
	
 	if(!collectedLayerExists)
	{	
	  	setText(s);
  		return;
	}
	 
	//----------------------------------
	// If any of the layers have data
	//----------------------------------
	 	 
	s+="<br>";	
	s+="<table>";
		
	//Create table header
	s+="<tr><td class=\"first\">Layer</td>";
	if(hasValue)
	{
		int colSpan=1;
		QString valueLabel("Value");
		if(hasVector)
		{
		  	colSpan=2;
			valueLabel="Value(s)";
		}	
			  		  
		if(hasScaledValue)
		{
			s+="<td class=\"first\"> Scaled Value</td>";
		}
		
		s+="<td class=\"first\" colspan=\"" + QString::number(colSpan) + "\">" + valueLabel + "</td>";			
	}
		
	s+="<td class=\"first\">" + nameX + "</td><td class=\"first\">" + nameY + "</td>";
	if(hasDistance)
	{
		s+="<td class=\"first\">Dist (km)</td>";
	}
	s+="</tr>";
	
		
	//Loop for each layer
	foreach(ValuesCollector d,layerData)
	{
		if(d.collected() == false)
			continue;
			
		s+="<tr>";
				
		//Cell: layer name
		QStringList lst=QString::fromStdString(d.name()).split("/");
		if(!lst.isEmpty())
		{
			QString layerName=lst.last();
			layerName.truncate(8);
			s+="<td>" + layerName + "</td>"; 
		}
		else
		{
		 	s+="<td></td>"; 
		}				  
		
		//We only check the first point in the collector (at present there
		//must be only one point anyway!!)  
		if(d[0].size() == 0)
		{
			s+="<td>-</td><td>-</td><td>-</td>";  
			if(hasValue)
			{		
				s+="<td>-</td>";
				if(hasVector)  s+="<td>-</td>";
				if(hasScaledValue)
				{	
				  	s+="<td>-</td>";
					if(hasVector)  s+="<td>-</td>";	 	
				}
			}
		}	
		else
		{  
		
			ValuesCollectorData *data=d[0].at(0); 
			
			//Get values
			if(d.hasValue())
			{
				currentValues_.clear();
				d[0].at(0)->visit(*this);
				
				//----------------------------------------------------
				// Cell: Scalar value (can be scaled or/and unscaled)
				//----------------------------------------------------

				if(currentValues_.size() == 1)		  
				{
					double value;
					QString valueStr,units;
					QString scaledTxt, unscaledTxt;	
				
					if(d.scaled())
					{	
						value=data->scaledValue();
						units=unitsString(QString::fromStdString(d.scaledUnits()));
						
						if(data->missing())
						{
							valueStr=tr("Missing");
							scaledTxt=valueStr;
						}	
						else
						{  
							valueStr=formatNumber(value);
							scaledTxt=valueStr+units;
						}
					}
				
					value=data->value();
					units=unitsString(QString::fromStdString(d.units()));	
					if(data->missing())
					{
						valueStr=tr("Missing");
						unscaledTxt=valueStr;
					}
					else
					{
					  	valueStr=formatNumber(value);
						unscaledTxt=valueStr+units;
					}	
					
					if(hasScaledValue)
					{
						if(d.scaled())  	
						{
					  		s+="<td class=\"highlight\">"+scaledTxt+"</td>";
							//if(hasVector) s+="<td></td>";
							s+="<td>"+unscaledTxt+"</td>";
							if(hasVector) s+="<td></td>";
						}
						else
						{  	
					  		s+="<td></td>";
							//if(hasVector) s+="<td></td>"; 					
							s+="<td class=\"highlight\">"+unscaledTxt+"</td>";
							if(hasVector) s+="<td></td>";
						}
					}
					else
					{
						//s+="<td></td>";
						//if(hasVector) s+="<td></td>"; 					
						s+="<td class=\"highlight\">"+unscaledTxt+"</td>";
						if(hasVector) s+="<td></td>";	
					}
				}
			
				//----------------------------------------------------
				// Cell: vector value (no scaling at the moment)
				//----------------------------------------------------
				
				if(currentValues_.size() == 2)		  
				{
					double value;
					QString valueStr;
					QString  value1Txt,value2Txt;	
					QStringList units;
					
					value=currentValues_[0];
					units=QString::fromStdString(d.units()).split("/");
					for(int i=0; i < units.count(); i++)					  
					{
					  	units[i]=unitsString(units[i]);
					}
					for(int i=units.count(); i < 2; i++)
					{
					  	units << "";
					}	
										
					if(data->missing())
					{
						valueStr=tr("Missing");
						value1Txt=valueStr;
					}
					else
					{  
						valueStr=formatNumber(value);
						value1Txt=valueStr+units[0];
					}
					
				
					value=currentValues_[1];
					if(data->missing())
					{
						valueStr=tr("Missing");
						value2Txt=valueStr;
					}
					else
					{
					  	valueStr=formatNumber(value);
						
						if(units[1].contains("(deg)"))
						{
							valueStr=QString::number(value,'f',2);  	
						}	
							
						value2Txt=valueStr+units[1];
					}
					
				
					if(hasScaledValue)
					{					
						//s+="<td></td><td></td>";
						s+="<td></td>";
						s+="<td class=\"highlight\">"+value1Txt+"</td>";
						s+="<td class=\"highlight\">"+value2Txt+"</td>";
					}
					else				
					{
						s+="<td class=\"highlight\">"+value1Txt+"</td>";
						s+="<td class=\"highlight\">"+value2Txt+"</td>";				  
					}
					
				}
			}	
			
			//Cell: pos X			
			s+="<td>" + QString::number(data->x(),'f',2) +  "</td>";			
			
			//Cell: pos Y
			s+="<td>" + QString::number(data->y(),'f',2) +  "</td>";
				
			//Cell: distance
			if(hasDistance)
			{
				  s+="<td>" + QString::number(data->distance(),'f',2) +  "</td>";
			}
				
		}

		s+="</tr>";
	}	

	s+="</table>";
	
	setText(s);
	
	
}

void MvQCursorData::setText()
{
	QString s(QObject::tr("Cursor outside plot area!"));
	setText(s);	
}

void MvQCursorData::setText(QString str)
{
	text_=str;	
	textDoc_->setHtml(text_);
	
	QSizeF docSize=textDoc_->documentLayout()->documentSize();	
	QRectF rect(0,0,docSize.width(),docSize.height());
	
	if(boundingRect_ != rect )
	{
		prepareGeometryChange(); 
	}

	boundingRect_=rect;
}


void MvQCursorData::setData(QPointF pos)
{
 	//Scene position
	//QPointF pos = plotView_->mapToScene(event->pos());	

	MgQLayoutItem* dataLayout=0;
		
	//Now if dataLayout_ is not null it should contain the point
	if(plotScene_->identifyPos(pos,&sceneItem_,&dataLayout))
	{
		QPointF posM=pos;
		QPointF coord;
		if(pointInMagnifier_)
		{
			posM=magnifierScenePos_+(pos-magnifierScenePos_)/magnifierZoomFactor_;
		}

		double searchRadiusX=2.;
		double searchRadiusY=2.;
			
		float plotScale=plotScene_->plotScale();						
		double cx=dataLayout->coordRatioX();
		double cy=dataLayout->coordRatioY();			
		if(cx != 0.)
		{
			  searchRadiusX=20./fabs(cx*plotScale);
		}
		if(cy != 0.)
		{
			  searchRadiusY=20./fabs(cy*plotScale);
		}
			
			//qDebug() << "searchRadius:" <<  cx << cy << plotScale << searchRadiusX << searchRadiusY;
			
		dataLayout->mapFromSceneToGeoCoords(posM,coord);
			//cursorData_->setCoordinates(coord);

			/*QList<QPointF> posL;
			QPointF projCoord;
			dataLayout->mapFromSceneToProjectionCoords(pos,projCoord);
			posL << projCoord;*/

		QList<QPointF> posL;
		posL << coord;;
			
		QList<ValuesCollector> layerData;
		
			/*QList<QStringList> txtL;
			foreach(QPointF pp,posL)
			{
				txtL << QStringList();
			}

			sceneItem_->collectLayerDataForCurrentStep(posL,txtL);

			setText(coord,txtL[0]);
			
			
			emit positionChanged(txtL[0]);*/
			
		qDebug() << "cursor:" <<  coord << posL;
			
		sceneItem_->collectLayerDataForCurrentStep(posL,layerData,
								   searchRadiusX,searchRadiusY);
			
			
		const Transformation& transformation = dataLayout->layout().transformation();
		QString nameX, nameY;
		bool hasDistance=false;
		switch(transformation.coordinateType())
		{
		case Transformation::GeoType:
				nameX=tr("Lon");
				nameY=tr("Lat");
				hasDistance=true;
				break;
		case Transformation::XyType:
		default:
			  	nameX=tr("X");
				nameY=tr("Y");
				break;
		}
							
		setText(coord,layerData,nameX,nameY,hasDistance);
			
		//emit positionChanged(coord,layerData);									
		}
	else
	{
		setText();
	}
}



void MvQCursorData::mousePressEventFromView(QMouseEvent *event)
{
	if(activated_ && acceptMouseEvents_)
	{
		if(event->buttons() & Qt::LeftButton)
		{		
     			//Get scene position
			QPointF pos=plotView_->mapToScene(event->pos());
			changePositionMode(pos);	
		}
		/*if(event->buttons() & Qt::MidButton)
		{		
     			//Get scene position
			QPointF pos=plotView_->mapToScene(event->pos());
			changeProbeMode(pos);	
		}*/
	}	
}

void MvQCursorData::mouseMoveEventFromView(QMouseEvent *event)
{
	if(!activated_ || !acceptMouseEvents_)
		return;
  
  	//Scene position
	QPointF pos = plotView_->mapToScene(event->pos());
		
	if(probeMode_ == FollowCursorProbe)
	{	
		setData(pos);
		setCursorPos(pos);
	}	
	else
	{
		setCursorPos(pos);
	}  
	  
}

 void MvQCursorData::mouseReleaseEventFromView(QMouseEvent *event)
 {
	if(activated_ && acceptMouseEvents_)
	{	
		if(zoomWasPerformedAfterMouseRelease_ == false)
		{
			QPointF pos=plotView_->mapToScene(event->pos());
			changePositionMode(pos);
		}
	}
 }


QString  MvQCursorData::unitsString(QString val)
{
	QString res;
	
	if(val == "m s**-1")
	  	res="m/s";
	else if(val == "deg c")
	  	res="&#176;C";
	else if(val == "degrees")
	  	res="deg";
	else res=val;
		
	if(!res.isEmpty())						
		res=" (" + res + ")";
	
	return res;
}	


QString MvQCursorData::formatNumber(double value)
{
	QString valueStr;
	double  absValue = abs(value);

	if (trunc(value) == value)					// is the number an integer?
		valueStr = QString::number(value,'f',0);// - cut off the decimal places
	else if (absValue < 0.000001 ||				// is the number very small?
	         absValue > 1000000000)				// or very large?
		valueStr = QString::number(value,'e');	// - use scientific notation
	else										// otherwise
		valueStr = QString::number(value,'f');	// - use 6 decimal places (default)

	return valueStr;
}



void MvQCursorData::setMagnifierInfo(bool pointInMag,QPointF magPos,float magZoomFactor)
{
	pointInMagnifier_=pointInMag;
	magnifierScenePos_=magPos;
	magnifierZoomFactor_=magZoomFactor;
}

void MvQCursorData::visit(const ValuesCollectorData& data)
{	
	currentValues_.push_back(data.value());  
}

void MvQCursorData::visit(const ValuesCollectorUVData& data)
{
	currentValues_.push_back(data.xComponent()); 
	currentValues_.push_back(data.yComponent()); 
}

void MvQCursorData::visit(const ValuesCollectorSDData& data)
{
	currentValues_.push_back(data.speed()); 
	currentValues_.push_back(data.direction()); 
}
