/*
 * (C) Copyright 1996-2016 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

/*!
    \file MgQSceneItem.cc
    \brief Definition of MgQSceneItem
    \author Graphics Section, ECMWF

    Started: June 2011
*/

#include "MgQSceneItem.h"

#include <QDebug>
#include <QGraphicsItem>
#include <QImage>
#include <QPixmap>
#include <QPainter>
#include <QProgressDialog>
#include <QStyleOptionGraphicsItem>

#include "MgQDriverObject.h"
#include "MgQHistoItem.h"
#include "MgQLayerItem.h"
#include "MgQLayoutItem.h"
#include "MgQStepMetaData.h"

#include "MgQPlotScene.h"

MgQSceneItem::MgQSceneItem(const Layout& layout) : MgQLayoutItem(layout)							
{
	prevCurrentStep_=0;
	driverObject_=0;

	sceneLayerItem_=0;
	previewLayoutItem_=0;
	
	antialias_=false;
	
	stepNum_=0;
}
 
MgQSceneItem::~MgQSceneItem()
{
	if(driverObject_)
		delete driverObject_;

	foreach(MgQLayerState *st,previousLayerState_)
	{
		delete st;
	}	
}

void MgQSceneItem::clearBeforeNewRequest()
{
/*	if(rootItem_)
	{
		removeItem(rootItem_);
		delete rootItem_;
		rootItem_=0;
	}
*/
	//Animation
	prevCurrentStep_=currentStep_;


	layerItems_.clear();
	sceneLayerItem_=0;
	previewLayoutItem_=0;
	stepNum_=0;
}

void MgQSceneItem::saveStateBeforeNewRequest()
{
	foreach(MgQLayerState *st,previousLayerState_)
	{
		delete st;
	}	
	previousLayerState_.clear();

	foreach(MgQLayerItem *item,layerItems_)
	{
		MgQLayerState *st=new MgQLayerState;
		item->saveLayerState(st);
		previousLayerState_.push_back(st);		
	}
}

MgQLayoutItem* MgQSceneItem::findProjectorItem(QPointF scenePos)
{
	foreach(MgQLayoutItem* item, projectorItems_)
	{			
	  	//qDebug() << "projector" << item->name() << scenePos << item->mapFromScene(scenePos);
	  	if(item->contains(item->mapFromScene(scenePos)))
		{			  
			return item;
		}
	}
	return 0;	
}

MgQLayoutItem* MgQSceneItem::firstProjectorItem()
{		
	if(projectorItems_.count() > 0)
	{
		 return projectorItems_.at(0);	

	}
	
	return 0;
}


MgQLayoutItem* MgQSceneItem::findPreviewLayout()
{
	return 0;

	/*foreach(QGraphicsItem *item, items())
	{
		if(item->data(MgQ::ItemType).toInt() == MgQ::PreviewLayoutItem)
		{
			
			MgQLayoutItem *layout=static_cast<MgQLayoutItem*>(item);
			return layout;
		}
	}

	return 0;*/		
}

MgQLayoutItem* MgQSceneItem::findMagnifierLayout()
{
/*	foreach(QGraphicsItem *item, items())
	{
		if(checkItemType(item,MgQ::MagnifierLayoutItem))
		{			
			MgQLayoutItem *layout=static_cast<MgQLayoutItem*>(item);
			return layout;
		}
	}
*/
	return 0;		
}


//-------------------------------------
// Animation
//-------------------------------------

void MgQSceneItem::updateAnimation()
{
  	MgQPlotScene *sc=static_cast<MgQPlotScene*>(scene());
 
	sc->sceneItemChanged();
	
	//Regenarete the cache
	//updateCache();
	
	//Repaint everything
	//update();
}

void MgQSceneItem::selectCurrentStepForAnimation()
{
	setCurrentStep(prevCurrentStep_);
}

int MgQSceneItem::currentStep()
{
	return currentStep_;
}

int MgQSceneItem::stepNum()
{
	return stepNum_;
}

void MgQSceneItem::setCurrentStep(int step,bool update)
{
	currentStep_=step;

	if(currentStep_< 0 || currentStep_ >= stepNum_)
		return;

	for(int i=0; i < stepNum_; i++)
	{
		if(i!= currentStep_)
		{
			setStepVisible(i,false);
		}
		else
		{
			setStepVisible(i,true);	
			//updateCache(); //Why we need to call it here????
		}
	}
	
	if(update)
        	updateAnimation();
}

void MgQSceneItem::setStepVisible(int step,bool visible)
{
	if(visible == true && stepCached(step) ==false)
	{
		//Notify scene item about the new step
		//sceneLayer_.reset();
		sceneLayerItem_->sceneLayer().getReady(step);
		foreach(MgQLayerItem *item,layerItems_)
		{
			if(item->stepNum() > 0 && item->stepCached(step) == false)
			{
				//driverObject_->driver().executeStep(step,item);
				driverObject_->driver().executeStep(step,item,sceneLayerItem_->sceneLayer());
				//item->setStepVisible(step,visible);
			}
		}
		
	}

	foreach(MgQLayerItem *item,layerItems_)
	{
		if(item->stepNum() > 0)
		{
			item->setStepVisible(step,visible);
		}
	}
}

bool MgQSceneItem::stepCached(int step)
{
	foreach(MgQLayerItem *item,layerItems_)
	{
		if(item->stepNum() > 0 && item->stepCached(step) == false)
		{
			return false;
		}
		
	}

	return true;
}

void MgQSceneItem::stepMetaData(MgQStepMetaData* metaData)
{
	if(!sceneLayerItem_)
		return;

	const magics::SceneLayer& snl=sceneLayerItem_->sceneLayer(); 
	
	MetaDataCollector stepData;
	MetaDataAttribute attr;
	attr.setSource(MetaDataAttribute::AnySource); //to enable any metadata query method

	for(int step=0; step < stepNum_; step++)
	{	
		metaData->addStep("");

		for(vector<Layer*>::iterator it = snl.beginLayer(step); it != snl.endLayer(step); ++it) 
		{
			//Layer& l=*it;

			foreach(QString str,metaData->keys())
			{
				stepData[str.toStdString()]="";
				stepData.setAttribute(str.toStdString(),attr);
			}

			(*it)->collect(stepData);
			
			for(map<string,string>::iterator itK=stepData.begin(); itK!= stepData.end(); itK++)
			{
				QString keyStr=QString::fromStdString(itK->first);
				QString valueStr=QString::fromStdString(itK->second);
				if(valueStr != "")
				{
					valueStr+="\n";

					/*if(keyStr == "MV_Format")
					{
						valueStr="GRIB";
						valueStr+="\n";
					}*/

					metaData->appendToStepData(keyStr,step,valueStr);
				}
			}
		}
	}

	/*

	foreach(MgQLayerItem *item,layerItems_)
	{
		if(item->stepNum() > 0)
		{
			int step=0;
			Layer& l=item->layer();
			StepLayer *layer=static_cast<StepLayer*>(&l);
			for(vector<SingleLayer*>::iterator it=layer->firstStep(); it != layer->endStep(); it++)
			{
				foreach(QString str,metaData->keys())
				{
					stepData[str.toStdString()]="";
				}				
				
				(*it)->metadata(stepData);
				
				if(firstLayer || metaData->stepNum() <= step)
				{
					metaData->addStep("");
				}

				for(map<string,string>::iterator itK=stepData.begin(); itK!= stepData.end(); itK++)
				{
					QString keyStr=QString::fromStdString(itK->first);
					QString valueStr=QString::fromStdString(itK->second);
					valueStr+="\n";


					if(keyStr == "MV_Format")
					{
						valueStr="GRIB";
						valueStr+="\n";
					}


					metaData->appendToStepData(keyStr,step,valueStr);
				}

				step++;
			}
			
			firstLayer=false;
		
		}
	}	*/
}


//-------------------------------------
// Layers
//-------------------------------------

void MgQSceneItem::updateLayers()
{
	MgQPlotScene *sc=static_cast<MgQPlotScene*>(scene());
 	
	sc->sceneItemChanged();

	//Regenarete the cache
//	updateCache();
	
	//Repaint everything
//	update();
}
 
void MgQSceneItem::addLayerItem(MgQLayerItem* item) 
{
	layerItems_.push_back(item);
	//item->setStackLevel(layerItems_.count()-1);
	ASSERT(item->layer().zindex() >= 0);
	item->setStackLevel(item->layer().zindex());
}


MgQLayerItem* MgQSceneItem::layerItem(const Layer& layer)
{
	foreach(MgQLayerItem *item,layerItems_)
	{
		if(&item->layer() == &layer)
		{
			return item;
		}
	}
	
	return 0;
}

void MgQSceneItem::restoreLayerState()
{
	if(previousLayerState_.count() == 0)
		return;

	return;
	
	QList<MgQLayerItem*> newItems;
	QMap<int,MgQLayerItem*> stStackLevelToItem;	
	QSet<MgQLayerState*> stToItem;
	
	foreach(MgQLayerItem *item,layerItems_)
	{
		bool found=false;
		foreach(MgQLayerState *st,previousLayerState_)
		{
			if(stToItem.contains(st) ==  false &&
			   st->id_ == QString::fromStdString(item->layer().id()))
			{
				stToItem.insert(st);
				stStackLevelToItem[st->stackLevel_]=item;
				item->setLayerAlpha(st->alpha_);
				item->setLayerVisibility(st->visible_);
				found=true;
				break;
			}
		}
		if(!found)
		{
			newItems.push_back(item);
		}
	}	

	//Set stack level for the "old" items
	int currenStackLevel=0;
	foreach(int level,stStackLevelToItem.keys())
	{
		//qDebug() << "restore" << QString::fromStdString(stStackLevelToItem[level]->layer().id()) << level << currenStackLevel;
		stStackLevelToItem[level]->setStackLevel(currenStackLevel);
		currenStackLevel++;
	}
	
	//Set stack level for the "new" items 
	foreach(MgQLayerItem *item,newItems)
	{
		item->setStackLevel(currenStackLevel);
		currenStackLevel++;
	}
}

void MgQSceneItem::collectLayerData(QList<QPointF> &pos,QMap<int,QList<ValuesCollector> > &val,
				    double searchRadiusX, double searchRadiusY)
{
	for(int step=0; step < stepNum_; step++)
	{	
		collectLayerData(pos,val[step],step,searchRadiusX,searchRadiusY);
	}
}

void MgQSceneItem::collectLayerDataForCurrentStep(QList<QPointF> &pos,QList<ValuesCollector> &val,
						  double searchRadiusX, double searchRadiusY)
{
	collectLayerData(pos,val,currentStep_,searchRadiusX,searchRadiusY);
}

void MgQSceneItem::collectLayerData(QList<QPointF> &pos,QList<ValuesCollector> &val,int step,
				    double searchRadiusX,  double searchRadiusY)
{
	if(!sceneLayerItem_)
		return;

	const magics::SceneLayer& snl=sceneLayerItem_->sceneLayer(); 	
	QString str; 

	QList<Layer*> layerLst;
		
	foreach(MgQLayerItem* item,layerItems_)	 
	{	  
		if(item->stepNum() == 0)
		{
			layerLst << item->layer().get();
		}
	}	
	
	for(vector<Layer*>::iterator it = snl.beginLayer(step); it != snl.endLayer(step); ++it) 
	{		
		if(layerLst.indexOf(*it) == -1)
		{  
			layerLst << *it;
		}	
	}  
	  
	foreach(Layer *layer,layerLst)  
	{  
	  	if(!layer)
		  	continue;
		
		if(layer->visibility() == true)
		{
			ValuesCollector layerData(layer->name());
			layerData.setSearchRadius(searchRadiusX,searchRadiusY);
			
			foreach(QPointF pp,pos)
			{		
				layerData.push_back(ValuesCollectorPoint(pp.x(),pp.y()));
			}		
	
			layer->collect(layerData);
	
			val << layerData;
		}	
	}

}

void MgQSceneItem::collectLayerDataForCurrentStep(MgQLayerItem *item,ValuesCollector& data)
{
	return collectLayerData(item,data,currentStep_);
}

void MgQSceneItem::collectLayerData(MgQLayerItem *item,ValuesCollector& data,int step)
{	
	if(!item || !sceneLayerItem_ || 
	   !layerItems_.contains(item))
		return;

	if(item->stepNum() == 0)
	{
		item->layer().collect(data);
	}
	else
	{	
		const magics::SceneLayer& snl=sceneLayerItem_->sceneLayer();
		Layer *stepLayer=snl.findLayer(&item->layer(),step);
		if(stepLayer)
		{
		  	 stepLayer->collect(data);
		}	
	}
}


void MgQSceneItem::layerMetaDataForCurrentStep(MgQLayerItem *item,MetaDataCollector& data)
{
	return layerMetaData(item,data,currentStep_);
}

void MgQSceneItem::layerMetaData(MgQLayerItem *item,MetaDataCollector& data,int step)
{	
	if(!item || !sceneLayerItem_ || 
	   !layerItems_.contains(item))
		return;

	if(item->stepNum() == 0)
	{
		item->layer().collect(data);
	}
	else
	{	
		const magics::SceneLayer& snl=sceneLayerItem_->sceneLayer();
		Layer *stepLayer=snl.findLayer(&item->layer(),step);
		if(stepLayer)
		{
		  	 stepLayer->collect(data);
		}	
	}
}

void MgQSceneItem::layerDataIndexForCurrentStep(MgQLayerItem *item,DataIndexCollector& data)
{
	return layerDataIndex(item,data,currentStep_);
}

void MgQSceneItem::layerDataIndex(MgQLayerItem *item,DataIndexCollector& data,int step)
{	
	if(!item || !sceneLayerItem_ || 
	   !layerItems_.contains(item))
		return;

	if(item->stepNum() == 0)
	{
		item->layer().collect(data);
	}
	else
	{	
		const magics::SceneLayer& snl=sceneLayerItem_->sceneLayer();
		Layer *stepLayer=snl.findLayer(&item->layer(),step);
		if(stepLayer)
		{
		  	 stepLayer->collect(data);
		}	
	}
}


QPixmap MgQSceneItem::layerInfoImageForCurrentStep(MgQLayerItem *item,QHash<QString,QString> imageId)
{
	return layerInfoImage(item,currentStep_,imageId);
}

QPixmap MgQSceneItem::layerInfoImage(MgQLayerItem *item,int step,QHash<QString,QString> imageId)
{
	if(!item || !sceneLayerItem_ || 
	   !layerItems_.contains(item))
		return QPixmap();
		
	MgQHistoItem *histoItem=item->histoItem(step);
	if(!histoItem)	
		return QPixmap();
		
	if(!histoItem->cached() || histoItem->pixmapId() != imageId)
	{
		if(histoItem->cached())
		{  
			histoItem=item->resetHistoItem(step);
		}
		
		histoItem->setPixmapId(imageId);
		
		Layer *stepLayer;

		if(item->stepNum() == 0)
		{
			stepLayer=&item->layer();
		}
		else
		{			
			const magics::SceneLayer& snl=sceneLayerItem_->sceneLayer(); 	
			stepLayer=snl.findLayer(&item->layer(),step);
		}

		if(!stepLayer)
			return QPixmap();
			
		QString visdefName=imageId.value("visdefName");
		QString visdefClass=imageId.value("visdefClass");
		driverObject_->driver().executeHisto(stepLayer,histoItem,visdefName,visdefClass);
	}	

	return item->histoPixmap(step,QSize(300,200));
}

void MgQSceneItem::layerIconsForCurrentStep(MgQLayerItem *item,MgQIconList& icons)
{
	return layerIcons(item,icons,currentStep_);
}

void MgQSceneItem::layerIcons(MgQLayerItem *item,MgQIconList& icons,int step)
{	
	if(!item || !sceneLayerItem_ || 
	   !layerItems_.contains(item))
		return;

		
	for(vector<MetviewIcon>::const_iterator it=item->layer().iconsBegin(); it != item->layer().iconsEnd(); it++)
	{
		icons << MgQIcon(QString::fromStdString((*it).iconName()),
				 QString::fromStdString((*it).iconClass()),
				 QString::fromStdString((*it).iconId()));
	}
		
	/*if(item->stepNum() == 0)
	{
		for(vector<pair<string, string>  >::const_iterator it=item->layer().iconsBegin(); it != item->layer().iconsEnd(); it++)
		{
			icons << QPair<QString,QString>(QString::fromStdString(it->first),QString::fromStdString(it->second));
		}	
	}
	else
	{	
		const magics::SceneLayer& snl=sceneLayerItem_->sceneLayer();
		Layer *stepLayer=snl.findLayer(&item->layer(),step);
		if(stepLayer)
		{
		  	for(vector<pair<string, string>  >::const_iterator it=stepLayer->iconsBegin(); it != stepLayer->iconsEnd(); it++)
			{
				icons << QPair<QString,QString>(QString::fromStdString(it->first),QString::fromStdString(it->second));	
			}
		}	
	}*/
}

void MgQSceneItem::renderLayerPreview()
{
	foreach(MgQLayerItem *item,layerItems_)
	{
		item->renderPreview();
	}
	
	/*preview_=QImage();
	
	foreach(MgQLayerItem *item,layerItems_)
	{
		QImage img=item->preview();
		preview_
	}*/
	
}

//----------------------------------------
// Magnifier
//---------------------------------------

MgQMagnifierLayoutItem* MgQSceneItem::updateMagnifier(float zoomFactor)
{
	//magnifierZoom_=zoom;
#if 0	
	MgQLayerItem *magLayerItem=0;	

	foreach(MgQLayerItem *item,layerItems_)
	{
		if(item->stepNum() > 0) //&& item->stepCached(currentStep_) == false)
		{
			magLayerItem=item;
			break;
			//driverObject_->driver().executeMagnifier(currentStep_,item);
				//item->setStepVisible(step,visible);
		}
	}

	if(!magLayerItem) 
		return 0;

	const magics::SceneLayer& snl=sceneLayerItem_->sceneLayer(); 	
	QString str;
	
	Layer *magLayer=snl.findLayer(&magLayerItem->layer(),currentStep_);

	MgQMagnifierLayoutItem *magLayoutItem=magLayerItem->magnifierLayoutItem();

	if(!magLayoutItem)
	{
		return 0;
	}		

	magLayoutItem->clearPlotContents(); 

	/*if(zoomFactor  != item->zoomFactor())
	{
		item->clearPlotContents();
	}
	else
	{ 
		return;
	}*/	

	magLayoutItem->setZoomFactor(zoomFactor);

	/*QVector<QPointF> pp;

	pp.push_back(QPointF(item->layout().minX(),item->layout().minY()));
	pp.push_back(QPointF(item->layout().maxX(),item->layout().minY()));
	pp.push_back(QPointF(item->layout().maxX(),item->layout().maxY()));
	pp.push_back(QPointF(item->layout().minX(),item->layout().maxY()));

	item->setArea(pp);*/

	float textWidth = 70.+20;
	float textHeight = 40.;
		
	magLayoutItem->setResolutionX(textWidth/zoomFactor);
	magLayoutItem->setResolutionY(textHeight/zoomFactor);

	driverObject_->driver().executeMagnifier(magLayer,magLayoutItem);

	return magLayoutItem;


	/*if(!animation_)
		return;

	item->setStep(animation_->currentStepObject());
		
	//MagLog::dev() << "GridValArea>  dx: " << fabsf(ldx) << " dy: " << fabsf(ldy) << endl;  
	for(int i=0; i <  pp.count() ; i++)
	{
		MagLog::dev() << " " << pp[i].x() << " " << pp[i].y() << endl;
	}	

	animation_->driver().redisplayMagnifier(item);*/
	
#endif	
}

void MgQSceneItem::clearMagnifier()
{
	/*MgQLayoutItem *litem=findMagnifierLayout();
	if(!litem) return;

	MgQMagnifierLayoutItem *item=static_cast<MgQMagnifierLayoutItem*>(litem);

	item->clearPlotContents();*/
}

/*void MgQSceneItem::drawItems(QPainter *painter, int numItems,
                             QGraphicsItem *items[],
                             const QStyleOptionGraphicsItem options[],
                             QWidget *widget)
 {
     for (int i = 0; i < numItems; ++i) {
          // Draw the item
          painter->save();
          painter->setMatrix(items[i]->sceneMatrix(), true);
          items[i]->paint(painter, &options[i], widget);
          painter->restore();
      }
 }
*/

void MgQSceneItem::updateCache()
{	
#if 0  
  
  	if ( !rootItem_ )
		return;
	
	
	
	QPainter *painter=cachePainter_;
	QStyleOptionGraphicsItem options;

	QRectF sourceRect = sceneRect();
        QRectF targetRect(0, 0, painter->device()->width(), painter->device()->height());

	cacheDevice_->fill(qRgba(255,255,255,255));

	painter->save();

	QTransform painterTransform;
    	painterTransform *= QTransform()
                        .translate(targetRect.left(), targetRect.top())
                        //.scale(xratio, yratio)
                        .translate(-sourceRect.left(), -sourceRect.top());
    	painter->setWorldTransform(painterTransform, true);

	rootItem_->setVisible(false);

	renderItemRecursively(rootItem_,painter,&options);

	rootItem_->setVisible(false);

	/*foreach(QGraphicsItem *item, items(Qt::AscendingOrder))
	{
		if(item->zValue() < 1.2 && item->isVisible())
		{
			painter->save();
          		painter->setTransform(item->sceneTransform(), true);
          		item->paint(painter, &options, 0);
          		painter->restore();			
		}
	}*/

	painter->restore();

	//cacheDevice_->save("/var/tmp/cgr/test.png");

	//rootItem_->setFlag(QGraphicsItem::ItemHasNoContents,true);
	rootItem_->setVisible(false); 


	//setBackgroundBrush(QPixmap::fromImage(*cacheDevice_));
#endif

}

void MgQSceneItem::drawBackground ( QPainter * painter, const QRectF & rect ) 
{	
	/*qDebug() << "bg" << rect << sceneRect();

	QRectF targetRect(0, 0, sceneRect().width(), sceneRect().height());
	//painter->drawPixmap(sceneRect(),*cacheDevice_,targetRect);

	painter->drawPixmap(rect,*cacheDevice_,rect);*/
}

void MgQSceneItem::renderForMagnifier(QPainter *painter, const QRectF &targetRect, const QRectF &sourceRect)
{
	/*QStyleOptionGraphicsItem option;
	painter->setRenderHint(QPainter::Antialiasing,antialias_);
	renderContents(painter,&option,targetRect,sourceRect,true);*/
}

void MgQSceneItem::renderForPrinter(QPainter *painter)
{
	/*QStyleOptionGraphicsItem option;
	
	QRectF sourceRect = sceneRect();
        QRectF targetRect(0, 0, painter->device()->width(), painter->device()->height());

	painter->setRenderHint(QPainter::Antialiasing,antialias_);
	renderContents(painter,&option,targetRect,sourceRect);*/
}

void MgQSceneItem::renderForVideo(QPainter *painter,QProgressDialog *progress,QString path,QStringList &files)
{	
	/*QPixmap *pix=static_cast<QPixmap*>(painter->device());
	QStyleOptionGraphicsItem option;
	
	QRectF sourceRect = sceneRect();
        QRectF targetRect(0, 0, painter->device()->width(), painter->device()->height());
		
	int oriCurrentStep=currentStep_;

	for(int i=0; i < stepNum_ && i < files.count() ; i++)
	{		
		progress->setValue(i);

		pix->fill();
		
		bool oriCached=stepCached(i);

		setStepVisible(i,true);	
		for(int j=0; j < stepNum_; j++)
		{
			if(j != i)
				setStepVisible(j,false);
		}

		renderContents(painter,&option,targetRect,sourceRect);
		
		pix->save(path + files[i]);
		
		if(oriCached == false)
		{
			foreach(MgQLayerItem *item,layerItems_)
			{
				item->clearStep(i);
			}
		}
	}
	
	currentStep_=-1;
	setCurrentStep(oriCurrentStep);*/
}

void MgQSceneItem::setEnableAntialias(bool status)
{
	/*if(antialias_!=status)
	{
		antialias_=status;
		cachePainter_->setRenderHint(QPainter::Antialiasing,antialias_);

		updateAnimation();
	}*/
}


