/*
 * (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 MgQPlotScene.cc
    \brief Definition of MgQPlotScene
    \author Graphics Section, ECMWF

    Started: March 2010
*/


#include "MgQPlotScene.h"

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

#include "MgQDriverObject.h"
#include "MgQHistoItem.h"
#include "MgQLayerItem.h"
#include "MgQLayoutItem.h"
#include "MgQRootItem.h"
#include "MgQSceneCacheItem.h"
#include "MgQSceneItem.h"
#include "MgQStepMetaData.h"


MgQPlotScene::MgQPlotScene(QObject *parent) : 
	MgQScene(parent),
	ignoreSceneItemChange_(false)
{
	
	dpiResolution_=75;
	
	driverObject_=0;

	currentSceneItemIndex_=-1;
	sceneLayerItem_=0;
	previewLayoutItem_=0;

	//cacheDevice_=new QPixmap(900,700);
	
	//Cache
	
	cacheDevice_=new QPixmap(2000,1500);

	cacheItem_=new MgQSceneCacheItem(cacheDevice_);
	addItem(cacheItem_);
	
	cachePainter_=new QPainter(cacheDevice_);
	cacheDevice_->fill(qRgba(255,255,255,255));

	antialias_=false;
	cachePainter_->setRenderHint(QPainter::Antialiasing,antialias_);

	highlightItem_=0;
	highlightedSceneItem_=0;

	highlightItemForBrief_=0;

	stepNum_=0;
	
	connect(this,SIGNAL(sceneRectChanged(const QRectF&)),
		this,SLOT(slotSceneRectChanged(const QRectF&)));
}
 
MgQPlotScene::~MgQPlotScene()
{
	if(sceneLayerItem_)
  		delete sceneLayerItem_;
	
  	if(driverObject_)
		delete driverObject_;

	foreach(QList<MgQLayerState*> sc,previousSceneState_)
	{
		foreach(MgQLayerState* st, sc)
		{
		  	delete st;
		}
		sc.clear();
	}		
}

void MgQPlotScene::clearBeforeNewRequest()
{
	prevSceneItemCurrentStep_.clear();
	
	//Animation
	foreach(MgQSceneItem* item,sceneItems_)
	{  
		prevSceneItemCurrentStep_ << item->currentStep();	
	}
	
	//we do not delete annotations
	plotRootItem_->clearContents();

	sceneItems_.clear();
	layerItems_.clear();

	if(sceneLayerItem_)
	{
		delete sceneLayerItem_;
	}

	sceneLayerItem_=0;
	previewLayoutItem_=0;
	stepNum_=0;
	highlightedSceneItem_=0;
}

void MgQPlotScene::saveStateBeforeNewRequest()
{
#if 0
  	foreach(QList<MgQLayerState*> sc,previousSceneState_)
	{
		foreach(MgQLayerState* st, sc)
		{
		  	delete st;
		}
		sc.clear();
	}	
	previousSceneState_.clear();

	for(int i=0; i < sceneItems_.count(); i++)
	{  
		QList<MgQLayerState*> vec;
		
		foreach(MgQLayerItem *layer,sceneItems_[i]->layerItems())  
		{	
			MgQLayerState *st=new MgQLayerState;
			layer->saveLayerState(st);
			vec << st;
		}	
		previousSceneState_ << vec;
		
	}
#endif
}
//Temporary solution until layers handled properly in Metview 4
void MgQPlotScene::restoreLayerState()
{
#if 0
  	/*qDebug() << "restoreLayerState";
  	foreach(QList<MgQLayerState*> sc,previousSceneState_)
	{
		qDebug() << "restoreLayerState";
		
		foreach(MgQLayerState* st, sc)
		{
		  qDebug() << "  " << st->name_ << st->id_ << st->stackLevel_ << st->alpha_ << st->visible_;	
		}
	}*/
	
	if(sceneItems_.count() != previousSceneState_.count())
		return;  
	
	
	for(int i=0; i < sceneItems_.count(); i++)
	{  
		bool sameNum=(sceneItems_[i]->layerItems().count() == previousSceneState_[i].count())?true:false;
	  
		int sameNameCnt=0;
		
	  	QSet<QString> usedNames;
		MgQLayerState stAct;
		for(int indexAct=0; indexAct < sceneItems_[i]->layerItems().count(); indexAct++)  
		{	
			MgQLayerItem *layer=sceneItems_[i]->layerItems().at(indexAct);			
			layer->saveLayerState(&stAct);	  
		  	for(int indexPrev=0; indexPrev < previousSceneState_[i].count(); indexPrev++)
			{  
				MgQLayerState *stPrev=previousSceneState_[i].at(indexPrev);
			  	if(stPrev->name_ == stAct.name_ && !usedNames.contains(stAct.name_))
				{
				  	layer->setLayerVisibility(stPrev->visible_);						
					layer->setLayerAlpha(stPrev->alpha_);		
					sameNameCnt++;
					usedNames << stAct.name_;
					break;
				}
				
			}
			
		}
		
		//If we have the same number of layers as before and the new and old names 
		//are the same (so the layers can be matched) we set the stack level!!!
		if(sameNum && sameNameCnt == sceneItems_[i]->layerItems().count())
		{
			for(int indexAct=0; indexAct < sceneItems_[i]->layerItems().count(); indexAct++)  
			{	
				MgQLayerItem *layer=sceneItems_[i]->layerItems().at(indexAct);			
				layer->saveLayerState(&stAct);	  
		  		for(int indexPrev=0; indexPrev < previousSceneState_[i].count(); indexPrev++)
				{  
					MgQLayerState *stPrev=previousSceneState_[i].at(indexPrev);
			  		if(stPrev->name_ == stAct.name_)
					{
						layer->setStackLevel(stPrev->stackLevel_);		
					}
				}
			}
		}	
		 
				
	}		  				
#endif
}	
 
void MgQPlotScene::addSceneItem(MgQSceneItem *item)
{
  	//item->setParentItem(plotRootItem_);
	sceneItems_ << item;
	
	if(prevSceneItemCurrentStep_.count() >= sceneItems_.count())
	{
	  	int i=sceneItems_.count()-1;
		sceneItems_[i]->setPrevCurrentStep(prevSceneItemCurrentStep_[i]);
	}	
		
	//connect(item,SIGNAL(changed()),
	//	this,SLOT(slotSceneItemChanged()));
}

void MgQPlotScene::setCurrentSceneItem(MgQSceneItem *item)
{
    	currentSceneItemIndex_=sceneItems_.indexOf(item);
}  


MgQSceneItem* MgQPlotScene::currentSceneItem()
{
	if(currentSceneItemIndex_ >=0 && currentSceneItemIndex_ < sceneItems_.count())  
	{
	  	return sceneItems_[currentSceneItemIndex_];
	}
	return 0;
}	

MgQLayoutItem* MgQPlotScene::firstProjectorItem()
{
	foreach(MgQSceneItem* item,sceneItems_)
	{  
		MgQLayoutItem* res=item->firstProjectorItem();
		if(res)
		  	return res;
	}
	
	return 0;
}

MgQLayoutItem* MgQPlotScene::findProjectorItem(QPointF scenePos)
{
	foreach(MgQSceneItem* item,sceneItems_)
	{  
		MgQLayoutItem* res=item->findProjectorItem(scenePos);
		if(res)
		  	return res;
	}
	
	return 0;
}

MgQSceneItem* MgQPlotScene::findSceneItem(QPointF scenePos)
{
	foreach(MgQSceneItem* item,sceneItems_)
	{  
		if(item->sceneBoundingRect().contains(scenePos))
	  		return item;
	}
	return 0;  
} 


bool MgQPlotScene::identifyPos(QPointF scenePos, MgQSceneItem** sceneItem,MgQLayoutItem** projectorItem)
{
	MgQSceneItem *scn=*sceneItem;
  	MgQLayoutItem *prn=0;
  			
	//Find zoomable layout, i.e. subpage!!!
	if(scn)
	{
		prn=scn->findProjectorItem(scenePos);		
		if(prn == 0)
		{
			scn=findSceneItem(scenePos);
			if(scn)	
			{
			  	prn=scn->findProjectorItem(scenePos);
			}
		}
	}
	else
	{  
		scn=findSceneItem(scenePos);
		if(scn)	
		{
			  prn=scn->findProjectorItem(scenePos);
		}
	}
	
	
	*sceneItem=scn;
	*projectorItem=prn;
	
	
	return (scn && prn);
}	
	
void MgQPlotScene::updateCache()
{
	if ( !plotRootItem_ )
		return;
	
	if(cacheDevice_->width() < sceneRect().width() || 
	   cacheDevice_->height() < sceneRect().height())
	{
		delete cachePainter_;
		delete cacheDevice_;

		cacheDevice_=new QPixmap(sceneRect().width()+100,sceneRect().height()+100);

		cachePainter_=new QPainter(cacheDevice_);	
		cachePainter_->setRenderHint(QPainter::Antialiasing,antialias_);
		
		cacheItem_->setPixmap(cacheDevice_);
				
		MagLog::debug() << "MgQPlotScene::updateCache() ---> Pixmap size changed to: "  <<
		          cacheDevice_->width() << "x" << cacheDevice_->height() << endl;
			  
	}
	
	cacheItem_->setClipRect(sceneRect());
	
	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);

	plotRootItem_->setVisible(false);

	renderItemRecursively(plotRootItem_,painter,&options);

	plotRootItem_->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();

	QPen pen(Qt::black);
	pen.setWidth(2);
	painter->setPen(pen);
	painter->drawRect(targetRect);
	
	//cacheDevice_->save("/var/tmp/cgr/test.png");

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

	//cacheItem_->setPixmap(*cacheDevice_);
	
	//setBackgroundBrush(QPixmap::fromImage(*cacheDevice_));
}

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

void MgQPlotScene::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 MgQPlotScene::updateAfterNewRequest()
{	
  	ignoreSceneItemChange_=true;
	
	foreach(MgQSceneItem* item,sceneItems_)
	{  
		item->selectCurrentStepForAnimation();
		item->renderLayerPreview();
	}
		
	ignoreSceneItemChange_=false;	
}


void MgQPlotScene::sceneItemChanged()
{
	if(!ignoreSceneItemChange_)
	{
	  	updateCache();
		update();
	}	
}

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

		updateCache();
		update();
	}
}

void MgQPlotScene::highlightSceneItem(MgQSceneItem* item, bool status)
{
	if(!highlightItem_)
	{
	  	highlightItem_=new QGraphicsRectItem;
		highlightItem_->setBrush(QColor(0,0,255,10));
		QPen pen(QColor(0,0,0,100));
		pen.setWidth(2);
		highlightItem_->setPen(pen);
		
		highlightItem_->setVisible(false);
		//highlightItem_->setScale(plotRootItem_->scale());
		addItem(highlightItem_);		
	}
		
	if(status && item)
	{
		 highlightItem_->setRect(item->sceneBoundingRect());
		 highlightItem_->setVisible(true);
		 highlightedSceneItem_=item;
	}
	else
	{
		 highlightItem_->setVisible(false);
	}
}	


void MgQPlotScene::highlightSceneItemForBrief(MgQSceneItem* item,bool status)
{
  	if(!highlightItemForBrief_)
	{
	  	highlightItemForBrief_=new QGraphicsRectItem;
		highlightItemForBrief_->setBrush(QColor(255,0,0,10));
		QPen pen(QColor(0,0,0,100));
		pen.setWidth(2);
		highlightItemForBrief_->setPen(pen);
		
		highlightItemForBrief_->setVisible(false);
		//highlightItem_->setScale(plotRootItem_->scale());
		addItem(highlightItemForBrief_);		
	}
		
	if(status && item)
	{
		 highlightItemForBrief_->setRect(item->sceneBoundingRect());
		 highlightItemForBrief_->setVisible(true);
		 //highlightedSceneItem_=item;
	}
	else
	{
		 highlightItemForBrief_->setVisible(false);
	}  
}

void MgQPlotScene::setPlotScale(float scaling, PlotScaleMode mode)
{
	if(mode == RelativeToCurrentSize)
	{  	
		float currentScaling=plotRootItem_->scale();
		float newScaling=scaling*currentScaling;
				
		plotRootItem_->setScale(newScaling);
		annotationRootItem_->setScale(newScaling);
		QRectF r=sceneRect();
		r.setWidth(r.width()*scaling);
		r.setHeight(r.height()*scaling);
		setSceneRect(r);
	}
	else if(mode == RelativeToOriSize)
	{
		plotRootItem_->setScale(scaling);
		annotationRootItem_->setScale(scaling);
		QRectF r=oriSceneRect_;
		r.setWidth(r.width()*scaling);
		r.setHeight(r.height()*scaling);
		setSceneRect(r);
	}	
}			

float MgQPlotScene::plotScale()
{
 	return plotRootItem_->scale();	
}	
			
void MgQPlotScene::slotSceneRectChanged(const QRectF& rect)
{
	if(highlightItem_ &&  highlightedSceneItem_ && highlightItem_->isVisible())
	{
		 highlightItem_->setRect(highlightedSceneItem_->sceneBoundingRect());
		 //highlightItem_->setScale(plotRootItem_->scale()); 
	}
}	
