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

 Copyright 2013 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 <QApplication>
#include <QDataStream>
#include <QDrag>
#include <QDropEvent>
#include <QDebug>
#include <QMouseEvent>
#include <QMenu>
#include <QPainter>
#include <QScrollBar>
#include <QShortcut>
#include <QTimer>

#include "MvQListFolderViewBase.h"

#include "IconClass.h"
#include "IconInfo.h"
#include "IconObject.h"
#include "Folder.h"

#include "MvQFolderViewDelegate.h"
#include "MvQContextMenu.h"
#include "MvQIconMimeData.h"
#include "MvQIconProvider.h"

MvQListFolderViewBase::MvQListFolderViewBase(MvQFolderModel *folderModel,QWidget *parent) :  
    QListView(parent), 
    MvQFolderViewBase(folderModel,parent),
    allowMoveAction_(true),
    enterFolders_(true),
    canDrag_(false),
    defaultShortCut_(0)
{	
	setContextMenuPolicy(Qt::CustomContextMenu);

	//Drag and drop
	setDragEnabled(true);
 	setAcceptDrops(true);
 	setDropIndicatorShown(true);
	setDragDropMode(QAbstractItemView::DragDrop);
	
	//Delegate
	delegate_=new MvQIconDelegate(this);
	setItemDelegate(delegate_);
	
	connect(delegate_,SIGNAL(repaintIt(const QModelIndex&)),
		this,SLOT(update(const QModelIndex&)));

		
	//Set the model. This will call reset on the view. 
	setModel(filterModel_);				
	
	setMouseTracking(true);
	
	setEditTriggers(QAbstractItemView::NoEditTriggers);
	setSelectionMode(QAbstractItemView::ExtendedSelection);
	
	//Context menu 
    	connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
                this, SLOT(slotContextMenu(const QPoint &)));
				
	//Selection in the view
	connect(this,SIGNAL(doubleClicked(const QModelIndex&)),
		this,SLOT(slotDoubleClickItem(const QModelIndex)));
		
	connect(this,SIGNAL(entered(const QModelIndex&)),
		this,SLOT(slotEntered(const QModelIndex&)));
				
	//We let the delegate to paint the items
	delegate_->setEnablePaint(true);
	
	//
	setProperty("helper","true");
}


MvQListFolderViewBase::~MvQListFolderViewBase()
{
	//delete filterModel_;
}

void MvQListFolderViewBase::doReset()
{
  	reset();
}	

QWidget* MvQListFolderViewBase::concreteWidget()
{
  	return this;
}	

//-----------------------------------------
// Event
//-----------------------------------------

bool MvQListFolderViewBase::event(QEvent* event) 
{	
	//We cannot call shortcut setup in the constructor beacuse it calls a pure virtual function! So we
	//call it the first time it is possible.
	if(isVisible())
	  	setupShortCut();
	
	return QListView::event(event);	
}

//------------------------------------------
// Item info. See also mousemove event!
//------------------------------------------

void  MvQListFolderViewBase::slotEntered(const QModelIndex& index)
{  	
	itemInfo_=itemInfo(index,FilterModelIndex);
  	emit itemEntered(itemInfo_);
}	

void MvQListFolderViewBase::leaveEvent(QEvent *event)
{
  	itemInfo_=QString();
  	emit itemEntered(itemInfo_);
	QWidget::leaveEvent(event);
}	


void  MvQListFolderViewBase::slotSelectItem(const QModelIndex& /*index*/)
{
	/*if(changeFolder(index,FilterModelIndex))
	{
	  	changeFolder(index,FilterModelIndex);
		emit currentFolderChanged(currentFolder());
	}*/
}	

void  MvQListFolderViewBase::slotDoubleClickItem(const QModelIndex& index)
{	 	
	QModelIndexList lst=selectedIndexes();
	if(lst.count() == 1 && lst.at(0)  == index)
		 handleDoubleClick(index);
}

void  MvQListFolderViewBase::setupShortCut()
{
	if(shortCutInit_)
	  	return;
	else
	  	shortCutInit_=true;
	
	if(MvQContextItemSet *cms=cmSet())
	{
		foreach(MvQContextItem *cm,cms->icon())
		{
	  		if(QShortcut *sc=cm->makeShortCut(this))
			{
		  		connect(sc,SIGNAL(activated()),
					this,SLOT(slotIconShortCut()));
			}
		}	
		
		if(QShortcut* sc=cms->makeDefaultIconShortCut(this))
		{
			defaultShortCut_=sc;
			connect(defaultShortCut_,SIGNAL(activated()),
				this,SLOT(slotDefaultShortCut()));
		}	
	}	
}


void MvQListFolderViewBase::slotIconShortCut()
{
	QShortcut *sc=static_cast<QShortcut*>(QObject::sender());
  	if(sc)
	{	QModelIndexList lst=selectedIndexes();
		if(lst.count() > 0)
            handleIconShortCut(sc,lst);
	}	
}

void MvQListFolderViewBase::slotDefaultShortCut()
{
  	if(defaultShortCut_)
	{
		QModelIndexList lst=selectedIndexes();
		if(lst.count() == 1)
		  	handleDoubleClick(lst.at(0));
	}	
}

void MvQListFolderViewBase::slotContextMenu(const QPoint& position)
{
	QModelIndexList lst=selectedIndexes();
	QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value());
	handleContextMenu(indexAt(position),lst,mapToGlobal(position),position+scrollOffset,this);
	
  	/*if(lst.count() == 1)
	{ 
		QModelIndex index=indexAt(position);	
		QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value());
	
		handleContextMenu(index,mapToGlobal(position),position+scrollOffset,this);
	}
	else if(lst.count() > 1)
	{
	  	handleContextMenu(lst,mapToGlobal(position),position+scrollOffset,this);
	} */ 	
}

void MvQListFolderViewBase::folderChanged()
{
  	emit currentFolderChanged(currentFolder());	
}

void MvQListFolderViewBase::iconCommand(QString name,IconObjectH obj)
{
	emit iconCommandRequested(name,obj);
}

void MvQListFolderViewBase::desktopCommand(QString name,QPoint pos)
{	     
  	emit desktopCommandRequested(name,pos);  
}

void MvQListFolderViewBase::keyPressEvent(QKeyEvent * event)
{
	setupShortCut();
  	QAbstractItemView::keyPressEvent(event);
}

void MvQListFolderViewBase::reset()
{	
	QListView::reset();
	if(filterModel_)
	  	filterModel_->sort(0);	
}	

//=================================================
//
// Icon positions
//
//==================================================

QRect MvQListFolderViewBase::itemRect(QList<IconObject*> objLst)
{  
	QRect bbox;
	foreach(IconObject* obj,objLst)
	{
		bbox=bbox.united(visualRect(filterModel_->mapFromSource(folderModel_->indexFromObject(obj))));
	}
				
  	return bbox;
}  

QRect MvQListFolderViewBase::itemRect(IconObject *obj)
{
  	return visualRect(filterModel_->mapFromSource(folderModel_->indexFromObject(obj)));
}	

QRect MvQListFolderViewBase::pixmapRect(QList<IconObject*> objLst)
{  
	QRect bbox;
	foreach(IconObject* obj,objLst)
	{
		bbox=bbox.united(pixmapRect(obj));
	}
				
  	return bbox;
}  


QRect MvQListFolderViewBase::pixmapRect(IconObject *obj)
{
  	QRect r=visualRect(filterModel_->mapFromSource(folderModel_->indexFromObject(obj)));
	return QRect(r.center().x()-getIconSize()/2,r.y(),getIconSize(),getIconSize());
}	


//=================================================
//
//  Drag and drop. We need this cutom implementation 
//  because the solution offered by the model-view
//  framework was not satisfactory.
//
//=================================================

//===========================
// Drag
//===========================

void MvQListFolderViewBase::mousePressEvent(QMouseEvent *event)
{
	canDrag_=false;	
	
	if(event->button() == Qt::LeftButton || event->button() == Qt::MidButton)
	{
	  	startPos_=event->pos();
		
		QModelIndex index=indexAt(event->pos());
		if(index.isValid())
		{			 				  		  		  		  
		  	QModelIndexList lst=selectedIndexes();
		
			if(lst.count() <= 1 || !lst.contains(index))
			{
	  			setCurrentIndex(index);
	  			QRect rect=visualRect(index);
				if(event->pos().y() > rect.top()+getIconSize())
				{
					edit(index); 
					return;
				}
			}
			
			canDrag_=true;			
			
			//This prevents rubberband selection
			setState(NoState);
			//return;
		}			 	
	}
	
	QListView::mousePressEvent(event);
}

void MvQListFolderViewBase::mouseMoveEvent(QMouseEvent *event)
{
  	if(!itemInfo_.isEmpty() && !indexAt(event->pos()).isValid())
	{
	  	itemInfo_=QString();
	  	emit itemEntered(itemInfo_);
	}
	
  	if(canDrag_ && event->buttons() & (Qt::LeftButton | Qt::MidButton))
	{
		//qDebug() << "should start drag" << event->pos() << startPos_;
		
	  	int distance = (event->pos() - startPos_).manhattanLength();
		if(distance >= QApplication::startDragDistance())
		{
			canDrag_=false;
			setState(DraggingState);
			
			QModelIndexList lst=selectedIndexes();
			
			///qDebug() << "selected" << lst.count();
		  
		  	if(allowMoveAction_)
			{  		  
		  		if(event->buttons() & Qt::LeftButton)		  
		  			performDrag(Qt::MoveAction,startPos_);
				else if(event->buttons() & Qt::MidButton)
			  		performDrag(Qt::CopyAction,startPos_);
			}
			else
			{
			  	performDrag(Qt::CopyAction,startPos_);
			}
			
			setState(NoState);
		}	
		
		return;
	}
	
	QListView::mouseMoveEvent(event);
}

void MvQListFolderViewBase::performDrag(Qt::DropAction dropAction,QPoint pos)
{
	QModelIndex viewIndex=indexAt(pos);
	QModelIndex index=filterModel_->mapToSource(viewIndex);	
	
	//The object that was dragged
	IconObject* dragObj=folderModel_->objectFromIndex(index);
	
	//List of objects to drag
	QList<IconObject*> objLst;
	foreach(QModelIndex idx,selectedIndexes())
	{
		objLst << folderModel_->objectFromIndex(filterModel_->mapToSource(idx));
	}		
			
	//Need a solution!!!!!!!!!!!!!!!!!1		
	bool fromHelper=(!allowMoveAction_);
	
	QDrag *drag=buildDrag(dragObj,objLst,fromHelper,this);	
	if(drag)
	{
		drag->exec(dropAction,dropAction);	
	}	
}	

//===========================
// Drop
//===========================

void MvQListFolderViewBase::dragEnterEvent(QDragEnterEvent *event)
{
	qDebug() << event->mimeData()->formats();
	qDebug() << event->mimeData()->text();
	qDebug() << event->proposedAction();
	
	if(event->source() &&
	   (event->proposedAction() == Qt::CopyAction ||
	    event->proposedAction() == Qt::MoveAction))
	{
		//event->setDropAction(Qt::CopyAction);
		event->accept();
	}
	else  
		event->ignore();
}

void MvQListFolderViewBase::dragMoveEvent(QDragMoveEvent *event)
{
	if(event->source() &&
	   (event->proposedAction() == Qt::CopyAction ||
	    event->proposedAction() == Qt::MoveAction))
	{
		event->accept();
	}
	else
		event->ignore();
}

void MvQListFolderViewBase::dropEvent(QDropEvent *event)
{
	qDebug() << "dropEvent" << folderModel_->fullName();
	
	if(folderModel_->folder()->locked())
	{
	  	event->ignore();
		return;
	}
	
	if(!event->source())
	{
	  	event->ignore();
		return;
	}	
	
	if(event->proposedAction() != Qt::CopyAction &&
	   event->proposedAction() != Qt::MoveAction)
	{
	  	event->ignore();
		return;
	}	
	
	QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value());
	QPoint pos=event->pos()+scrollOffset;
	
	//--------------------------------------
	// Drag and drop from another folder
	//--------------------------------------
	
	if(event->mimeData()->hasFormat("metview/icon"))
	{	
		const MvQIconMimeData* mimeData=qobject_cast<const MvQIconMimeData*>(event->mimeData());
				
		if(!mimeData)
		{
			event->ignore();
			return;
		}	
	
		IconObject* dragObj=mimeData->dragObject();
		
		QList<IconObject*> objLst=mimeData->objects();
		MvQFolderModel *model=mimeData->model();
				
		if(model && dragObj)
		{			
			//QPoint hotSpot=mimeData->hotSpotInVisRect();		  	
			//pos-=QPoint(hotSpot.x(),hotSpot.y());		
			//if(pos.x() < 1) pos.setX(1);
			
			//performDrop(dragObj,objLst,pos,(model == folderModel_));
			performDrop(event->proposedAction(),mimeData,pos,(model == folderModel_));
										
			event->accept();
			return;
		}
	}
	
	event->ignore();
}

void MvQListFolderViewBase::rename(IconObject* obj)
{
 	if(!obj) return;	
  	QModelIndex index=filterModel_->mapFromSource(folderModel_->indexFromObject(obj));
	edit(index); 
}  

void MvQListFolderViewBase::blink(const QModelIndex& index)
{
  	scrollTo(index);
  	delegate_->blink(index);
}	
