/******************************** LICENSE ********************************


 Copyright 2007 European Centre for Medium-Range Weather Forecasts (ECMWF)
 
 Licensed under the Apache License, Version 2.0 (the "License"); 
 you may not use this file except in compliance with the License. 
 You may obtain a copy of the License at 
 
 	http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software 
 distributed under the License is distributed on an "AS IS" BASIS, 
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 See the License for the specific language governing permissions and 
 limitations under the License.


 ******************************** LICENSE ********************************/

/*!
    \file MgQLayerItem.cc
    \brief Implementation of the MgQLayerItem class.
    \author Graphics Section, ECMWF

    Started: February 2010
*/

#include <QDebug>
#include <QImage>
#include <QPainter>

#include "MgQLayerItem.h"

#include "MgQHistoItem.h"
#include "MgQLayoutItem.h"
#include "MgQRootItem.h"
#include "MgQScene.h"
#include "MgQStepItem.h"

using namespace magics;

QImage* MgQLayerItem::alphaDevice_=0;
QPainter* MgQLayerItem::alphaPainter_=0;


MgQLayoutItem* MgQSceneLayerItem::findProjectorItem(QPointF scenePos)
{
	foreach(MgQLayoutItem* item, projectorItems_)
	{			
	  	if(item->contains(item->mapFromScene(scenePos)))
		{			  
			return item;
		}
	}
	return 0;	
}


MgQLayerItem::MgQLayerItem(Layer &layer,MgQLayoutItem *layout,int stepNum) : 
	  layer_(layer), layout_(layout)
{
	rootItem_=new MgQRootItem;
	rootItem_->setParentItem(this);
	rootItemOwner_=MainScene;
	
	magnifierLayout_=0;
	magnifierDataEnabled_=false;

	alphaScene_=0;
	
	previewScene_=0;
	previewDevice_=0;
	previewPainter_=0;
	
	histoScene_=0;
	histoPainter_=0;
	histoDevice_=0;
	
	//rootItem_->setTransform(layout->sceneTransform());

	for(int i=0; i < stepNum; i++)
	{
		steps_ << 0;
	}

	for(int i=0; i < stepNum; i++)
	{
		histoItems_ << 0;
	}
	if(stepNum == 0)
	{
		histoItems_ << 0;
	}
}

MgQLayerItem::~MgQLayerItem()
{ 
  	if(alphaScene_)
	  	delete alphaScene_;
	if(previewScene_)
	  	delete previewScene_;
	if(histoScene_)
	  	delete histoScene_;
	
	if(alphaPainter_)
	{	delete alphaPainter_;
		alphaPainter_=0;
	}
	if(previewPainter_)
		delete previewPainter_;
	if(histoPainter_)
		delete histoPainter_;
	
	if(alphaDevice_)
	{
	  	delete alphaDevice_;
		alphaDevice_=0;
	}
	if(previewDevice_)
		delete previewDevice_;
	if(histoDevice_)
		delete histoDevice_;	
}

MgQMagnifierLayoutItem * MgQLayerItem::magnifierLayoutItem()
{
	if(!magnifierLayout_)//Init magnifier layout item
	{
		MgQLayoutItem *topLayout=topLayoutItem();
		if(!topLayout)
			return 0;

		//it is outside the root item
		magnifierLayout_= new MgQMagnifierLayoutItem(topLayout);
		magnifierLayout_->setParentItem(this);
		magnifierLayout_->setParentItemInMainScene(this);
		magnifierLayout_->setData(MgQ::ItemIsVisibleKey,false);
	}
	
	return magnifierLayout_;
}


bool MgQLayerItem::layerVisibility() const
{
	return layer_.visibility();
}

void MgQLayerItem::setLayerVisibility(bool visible)
{	
	layer_.visibility(visible);
	//setVisible(b);
	setData(MgQ::ItemIsVisibleKey,visible);
}

float MgQLayerItem::layerAlpha() const
{
	int tr=layer_.transparency();
	return (100-tr)/100.;
}

void MgQLayerItem::setLayerAlpha(float alpha) 
{
//	const float oriAlpha=layerAlpha();
	layer_.transparency((1.-alpha)*100.);
//	if(fabs(alpha-oriAlpha) > 0.05)
//		update();
}

void MgQLayerItem::setStackLevel(int i)
{
	float z=1.1+0.01*static_cast<float>(i);
	layer_.zindex(i);
	setZValue(z); 
}

int MgQLayerItem::stackLevel()
{
	return layer_.zindex();

	//int i=static_cast<int>((zValue()-1.1) *100.);
	//return i;
}

void MgQLayerItem::saveLayerState(MgQLayerState* st)
{
	st->name_=QString::fromStdString(layer_.name());
	st->id_=QString::fromStdString(layer_.id());
	st->alpha_=layerAlpha();
	st->visible_=layerVisibility();
	st->stackLevel_=stackLevel();
}

void MgQLayerItem::updateLayer(const MgQLayerItem *item)
{
	if(item->name().toStdString() != layer_.name())
		return;

	setLayerVisibility(item->layerVisibility());
	setLayerAlpha(item->layerAlpha());
}

void MgQLayerItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                QWidget *widget)
{
	//Non-trasparent 
	if(layerAlpha() >= 0.99)
	{
		addContentsToScene(MainScene);
	}
	//Transparent
	else
	{
		addContentsToScene(AlphaScene);

		bool clipping=(painter->clipRegion().isEmpty())?false:true;
		QRect clipRect=painter->clipRegion().boundingRect();

		float sx=painter->transform().m11();
		float sy=painter->transform().m22();

		QRectF sourceRect,targetRect;
		QRect alphaRect, magRect;

		alphaDevice_->fill(qRgba(0,0,0,0));

		//Magnifier
		if(clipping)
		{
			sourceRect=clipRect;
			magRect=QRect(sourceRect.x()*sx,sourceRect.y()*sy,
				      sourceRect.width()*sx,sourceRect.height()*sy);	
		}
		//Non magnifier!!
		else
		{
			//The rect in local coordinates
			sourceRect=layout_->boundingRect();	
		}
		
		
		//Physical target rect on the alpha device
		alphaRect=QRect(0.,0.,sourceRect.width()*sx,sourceRect.height()*sy);
				
		//qDebug() <<  "LAYER-MAGNIFY" << scale() << sx << sy << layout_->boundingRect()  << alphaRect <<  clipRect;
				
		//Check if the alpha device needs to be resized
		checkAlphaDeviceSize(alphaRect);
			
  		//Set antialising for alpha painter
		alphaPainter_->setRenderHint(QPainter::Antialiasing,painter->renderHints() & QPainter::Antialiasing);
			
		//Render the layer into the alpha device: from sourceRect to alphaRect!			
		alphaScene_->renderContents(alphaPainter_,option,alphaRect,sourceRect);
				
		//Save the painter transform
		QTransform trOri=painter->transform();
			
		//Set the scaling to 1 for the painter
		QTransform tr=trOri;
		tr.setMatrix(1.,tr.m12(),tr.m13(),tr.m12(),1.,tr.m23(),tr.m31(),tr.m32(),tr.m33());
		painter->setTransform(tr);
			
		//Paint alphaRect form the alphaDevice
		painter->setOpacity(layerAlpha());
		
		//Magnifier
		if(clipping)
		{
			painter->drawImage(magRect,alphaDevice_->copy(alphaRect));
		}
		//Non magnifier!!
		else
		{
			painter->drawImage(alphaRect,alphaDevice_->copy(alphaRect));  
		}
		
		//Reset the painter tranfor to the original value
		painter->setTransform(trOri);						
	}
}

void MgQLayerItem::checkAlphaDeviceSize(QRect rect)
{
  	if(!alphaDevice_)
		return;
	
	int w=(alphaDevice_->width() < rect.width())?rect.width():alphaDevice_->width();
	int h=(alphaDevice_->height() < rect.height())?rect.height():alphaDevice_->height();
	
	if(alphaDevice_->width() < w || alphaDevice_->height() < h)
	{
		delete alphaPainter_;
		delete alphaDevice_;

		alphaDevice_=new QImage(w+2,h+2,QImage::Format_ARGB32);
		alphaPainter_=new QPainter(alphaDevice_);			  
	}
}	

void MgQLayerItem::addContentsToScene(RootItemOwner newOwner)
{
  	if(rootItemOwner_ == newOwner)
	  	return;
  
	if(rootItemOwner_ == MainScene)
	{   
  		scene()->removeItem(rootItem_);

	}
	else if(rootItemOwner_ == AlphaScene)
	{   
  		if(!alphaScene_)
			return;

		alphaScene_->removePlotRootItemFromScene();
	}
	else if(rootItemOwner_ == PreviewScene)
	{   
		if(!previewScene_)
			return;

		previewScene_->removePlotRootItemFromScene();
	}
	
	
	if(newOwner == MainScene)
	{
		rootItem_->setTransform(QTransform());
		rootItem_->setParentItem(this);	
	}  	  
	else if(newOwner == AlphaScene)
	{	
		//rootItem_->setTransform(layout_->sceneTransform());
		if(!alphaScene_)
		{
			alphaScene_=new MgQScene(rootItem_);
		}	

		if(!alphaDevice_)
		{
			alphaDevice_=new QImage(1100,785,QImage::Format_ARGB32);
			alphaPainter_=new QPainter(alphaDevice_);
		}
	
		alphaScene_->addPlotRootItemToScene();
	}
	else if(newOwner == PreviewScene)
	{	
		rootItem_->setTransform(layout_->sceneTransform());

		if(!previewScene_)
		{
			previewScene_=new MgQScene(rootItem_);
			previewDevice_=new QImage(200,200,QImage::Format_ARGB32);
			previewPainter_=new QPainter(previewDevice_);
 		}

		previewScene_->addPlotRootItemToScene();
	}
	
	rootItemOwner_=newOwner;	
}

void MgQLayerItem::setStepVisible(int step,bool visible)
{
	if(steps_.count() ==0 || 
	  step < 0 || step >= steps_.count())
		return;

	if(steps_[step])
	{		
		steps_[step]->setData(MgQ::ItemIsVisibleKey,visible);
		//steps_[step]->setVisible(visible);	
	}
}

void  MgQLayerItem::clearStep(int step)
{		
	if(steps_.count() ==0 || 
	   step < 0 || step >= steps_.count())
		return;

	if(steps_[step])
	{
		delete steps_[step];
		steps_[step]=0;
	}
}

bool MgQLayerItem::stepCached(int step)
{		
	if(steps_.count() ==0 || 
	   step < 0 || step >= steps_.count())
		return false;

	if(steps_[step])
		return steps_[step]->cached();
	else
		return false;
}

MgQLayoutItem* MgQLayerItem::topLayoutItem()
{
	foreach(MgQStepItem* step,steps_)
	{
		if(step != 0)
		{
			foreach(QGraphicsItem *item, step->childItems())
			{
				if(item->type() == MgQ::LayoutItemType)
				{			
					MgQLayoutItem *layout=static_cast<MgQLayoutItem*>(item);
					return layout;
				}
			}
		}
	}
	return 0;		
}	

void MgQLayerItem::renderPreview()
{
	RootItemOwner prevOwner=rootItemOwner_;
	addContentsToScene(PreviewScene);
	
	//QRectF layoutRect=layout_->boundingRect();
	//QRectF targetRect((layout_->mapToScene( layoutRect).boundingRect()));
	//QRectF painterRect=painter->transform().mapRect(layout_->boundingRect());
				
	QRectF sourceRect=QRectF((layout_->mapToScene(layout_->boundingRect()).boundingRect()));

	const float w=sourceRect.width();
	const float h=sourceRect.height();
//	float r=w/h;

	const float preW=previewDevice_->width();
	const float preH=previewDevice_->height();

	QRectF targetRect;
	if(h*preW/w > preH)
	{
		targetRect=QRectF(0.,0.,w*preH/h,preH);
	}
	else
	{
		targetRect=QRectF(0.,0.,preW,h*preW/w);
	}
		
	previewDevice_->fill(qRgba(255,255,255,255));
	previewScene_->renderContents(previewPainter_,targetRect,sourceRect);

	QRect targetRectInt(targetRect.x(),targetRect.y(),targetRect.width(),targetRect.height());
	if(h*100/w > 50)
	{
		previewImg_=previewDevice_->copy(targetRectInt).scaledToHeight(50,Qt::SmoothTransformation).mirrored(false,true);
	}
	else
	{
		previewImg_=previewDevice_->copy(targetRectInt).scaledToWidth(100,Qt::SmoothTransformation).mirrored(false,true);
	}


	//previewImg_.save("/var/tmp/cgr/layer.png");


	//QRect targetRectInt(targetRect.x(),targetRect.y(),targetRect.width(),targetRect.height());

	/*painter->setOpacity(layerAlpha());
	painter->drawImage(layout_->boundingRect(),
			   previewDevice_->copy(targetRectInt));*/

	addContentsToScene(prevOwner);
}	



MgQHistoItem* MgQLayerItem::histoItem(int step)
{
	if(!histoScene_)
	{
		histoScene_=new QGraphicsScene;
		histoDevice_=new QImage(300,200,QImage::Format_ARGB32);
		histoPainter_=new QPainter(histoDevice_);
 	}

	if(stepNum() == 0)
	{
		if(!histoItems_[0])
		{
			histoItems_[0]=new MgQHistoItem;
			histoItems_[0]->setVisible(false);
			histoScene_->addItem(histoItems_[0]);			
		}
		return histoItems_[0];
	}
	else
	{
		if(step < 0 || step >= steps_.count() || !steps_[step] )
			return 0;
			
		if(!histoItems_[step])
		{
			histoItems_[step]=new MgQHistoItem;	
			histoItems_[step]->setVisible(false);	
			histoScene_->addItem(histoItems_[step]);
		}
		return histoItems_[step];
	}
}

MgQHistoItem* MgQLayerItem::resetHistoItem(int step)
{
	MgQHistoItem* item=histoItem(step);
		
	if(item)
	{
		histoScene_->removeItem(item);	
		delete item;
	
		if(stepNum() == 0)		
		{
		 	 histoItems_[0]=0;
			 
		}
		else if(step >= 0 && step < steps_.count() && steps_[step])
		{
		  	histoItems_[step]=0;
		}
		else
		{  
		  	return 0;

		}
	}	
	
	return histoItem(step);	
}  

void MgQLayerItem::addHistoItem(MgQHistoItem *item)
{		
	/*if(steps_.count() ==0 || step < 0 || 
           step >= steps_.count() || !steps_[step] )
		return;*/

	/*item->scene()->removeItem(item);
	
	if(!histoScene_)
	{
		histoScene_=new QGraphicsScene;
		histoDevice_=new QImage(200,200,QImage::Format_ARGB32);
		histoPainter_=new QPainter(histoDevice_);
 	}
	
	histoScene_->addItem(item);*/
}

QPixmap MgQLayerItem::histoPixmap(int step,QSize size)
{
	MgQHistoItem *item=histoItem(step);

	if(!item)
		return QPixmap();

	/*if(item->pixmap().isNull() && item->cached())
	{
		return QPixmap();
	}*/

	if(item->cached() && item->requestedPixmapSize() == size)
	{
		return item->pixmap();
	}
	else
	{
		/*foreach(QGraphicsItem *item,histoScene_->items())
		{
			qDebug() << item << item->childItems().count() <<  item->boundingRect() << item->sceneBoundingRect();
		}

		qDebug() << histoScene_->sceneRect() << item->boundingRect() << item->sceneBoundingRect() ;*/

		if(histoDevice_->size().width() < size.width() ||
		   histoDevice_->size().height() < size.height())
		{
			
			delete histoDevice_;
			histoDevice_ = new QImage(size,QImage::Format_ARGB32);
		}

		item->setVisible(true);			
		histoDevice_->fill(qRgba(255,255,255,255));
		histoScene_->render(histoPainter_,QRectF(0,0,size.width(),size.height()));
		item->setVisible(false);

		/*float ratio=1;
		QRectF scr=histoScene_->sceneRect();
		if(scr.height() > 0)
			ratio=scr.width()/scr.height();
	
		QImage img=histoDevice_->copy(0,0,size.width(),size.width()/ratio+1).mirrored(false,true);*/
		QImage img=histoDevice_->copy(0,0,size.width(),size.height()).mirrored(false,true);
		
		item->setPixmap(QPixmap::fromImage(img),size);
		item->setCached(true);
		
		return item->pixmap();		
	}

	return QPixmap(); 
}
