/***************************** 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 <QAction>
#include <QApplication>
#include <QDataStream>
#include <QDebug>
#include <QDrag>
#include <QHeaderView>
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QScrollBar>
#include <QShortcut>

#include "MvQDetailedFolderView.h"

#include "MvQActionList.h"
#include "MvQContextMenu.h"
#include "MvQDropTarget.h"
#include "MvQFolderViewDelegate.h"
#include "MvQFolderModel.h"
#include "MvQDesktopSettings.h"
#include "MvQIconMimeData.h"
#include "MvQIconProvider.h"

#include "Folder.h"
#include "IconClass.h"

static QList<MvQDetailedFolderView*> views; 

MvQDetailedFolderView::MvQDetailedFolderView(MvQFolderModel *folderModel,
					    MvQActionList* iconActions,
					    MvQActionList* desktopActions,QWidget *parent) : 
    QTreeView(parent), 
    MvQFolderViewBase(folderModel,parent),
    moveActionEnabled_(true),
    defaultShortCut_(0), 
    appIconActions_(iconActions),
    appDesktopActions_(desktopActions)
{
	views << this;
  
  	setRootIsDecorated(false);
	setSortingEnabled(true); 
	sortByColumn(0,Qt::AscendingOrder); 
	//setAlternatingRowColors(true);
        setAllColumnsShowFocus(true);
	setUniformRowHeights(true);
	
	//Drag and drop
	setDragEnabled(true);
 	setAcceptDrops(true);
 	setDropIndicatorShown(true);
	setDragDropMode(QAbstractItemView::DragDrop);
	
	//Delegate
	delegate_=new MvQDetailedViewDelegate(this);
	setItemDelegate(delegate_);
	
	connect(delegate_,SIGNAL(repaintIt(const QModelIndex&)),
		this,SLOT(update(const QModelIndex&)));
	
	setMouseTracking(true);
	
	setSelectionMode(QAbstractItemView::ExtendedSelection);
	
	//Context menu 
    	setContextMenuPolicy(Qt::CustomContextMenu);
	
	connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
                this, SLOT(slotContextMenu(const QPoint &)));
				
	connect(this,SIGNAL(doubleClicked(const QModelIndex&)),
		this,SLOT(slotDoubleClickItem(const QModelIndex)));
	
	connect(this,SIGNAL(entered(const QModelIndex&)),
		this,SLOT(slotEntered(const QModelIndex&)));
		
	//Selection form the list
	connect(this,SIGNAL(clicked(const QModelIndex&)),
		this,SLOT(slotSelectItem(const QModelIndex)));
			
	//Set header ContextMenuPolicy	
#ifdef METVIEW_QT5
	header()->setSectionsMovable(false);
#else
	header()->setMovable(false);
#endif
	header()->setContextMenuPolicy(Qt::CustomContextMenu);

	connect(header(),SIGNAL(customContextMenuRequested(const QPoint &)),
                this, SLOT(slotHeaderContextMenu(const QPoint &)));

	//Init header
	updateHeader();	
		
	for(int i=0; i < folderModel_->columnCount(QModelIndex())-1; i++)
	  	resizeColumnToContents(i);
		
	/*connect(header(),SIGNAL(sectionMoved(int,int,int)),
                this, SLOT(slotMessageTreeColumnMoved(int,int,int)));*/
	
	setProperty("mvStyle","DetailedFolderView");
	
	//We set up the shortcuts here!!
	setupShortCut(); 
	
	//We attach the model because by default the view is enabled. At this point the model is empty so
	//it is a cheap operation!!
	attachModel();	
}

MvQDetailedFolderView::~MvQDetailedFolderView()
{
  	views.removeOne(this);
}

//Connect the models signal to the view
void MvQDetailedFolderView::attachModel()
{
	//Standard signals from the model	
	setModel(filterModel_);	
	
	connect(folderModel_,SIGNAL(iconSizeChanged()),
		this,SLOT(slotIconSizeChanged()));	
		
	connect(folderModel_,SIGNAL(objectRenamed(const QModelIndex&,QString)),	
		this,SLOT(slotObjectRenamed(const QModelIndex& ,QString)));	
}	
		
//Disconnect the model from the view
void MvQDetailedFolderView::detachModel()
{ 
	setModel(0);
	disconnect(folderModel_,0,this,0); 
}  

void MvQDetailedFolderView::changeEvent(QEvent* event) 
{
  	if(event->type() == QEvent::EnabledChange)
	{
	 	if(isEnabled())
		{
		  	attachModel();
			blockSignals(false);
		}
		//When the view is diabled we do not want to receive any signals from the model
		else
		{
		  	detachModel();
			blockSignals(true);
		}
	}	
}

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

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

MvQContextItemSet* MvQDetailedFolderView::cmSet()
{
	static MvQContextItemSet cmItems("DetailedFolderView");  
  	return &cmItems;
}

QModelIndexList MvQDetailedFolderView::selectedList()
{
  	QModelIndexList lst;
  	foreach(QModelIndex idx,selectedIndexes())	
	  	if(idx.column() == 0)
		  	lst << idx;	
	return lst;
}		
		
void MvQDetailedFolderView::slotSelectItem(const QModelIndex& /*index*/)
{	
	/*if(isFolder(index,FilterModelIndex))
	{   
		changeFolder(index,FilterModelIndex);
		emit currentFolderChanged(currentFolder());

	}*/
}

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

void MvQDetailedFolderView::setupShortCut()
{
	if(shortCutInit_)
	  	return;
	else
	  	shortCutInit_=true;
	
	if(MvQContextItemSet *cms=cmSet())
	{
		foreach(MvQContextItem *cm,cms->icon())
		{	  		
		  	if(!appIconActions_ || !appIconActions_->shortCutDefined(cm->shortCut()))
			{  
			  	if(QShortcut *sc=cm->makeShortCut(this))		
				{
					connect(sc,SIGNAL(activated()),
						this,SLOT(slotIconShortCut()));
				}
			}	
		}
		
		foreach(MvQContextItem *cm,cms->desktop())
		{
	  		
	  		if(!appDesktopActions_ || !appDesktopActions_->shortCutDefined(cm->shortCut()))
			{  
	  			if(QShortcut *sc=cm->makeShortCut(this))
				{
		  			connect(sc,SIGNAL(activated()),
						this,SLOT(slotDesktopShortCut()));
				}
			}	
		}
		
		if(QShortcut* sc=cms->makeDefaultIconShortCut(this))
		{
			defaultShortCut_=sc;
			connect(defaultShortCut_,SIGNAL(activated()),
				this,SLOT(slotDefaultShortCut()));
		}
		
	}
}

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

void MvQDetailedFolderView::slotDefaultShortCut()
{
  	if(defaultShortCut_)
	{	QModelIndexList lst=selectedList();
		qDebug() << "default shortcut" << lst;
		if(lst.count() == 1)
		  	handleDoubleClick(lst.at(0));
	}	
}

void MvQDetailedFolderView::slotDesktopShortCut()
{
	QShortcut *sc=static_cast<QShortcut*>(QObject::sender());
  	if(sc) 
	{
	  	//QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value()); 
		//handleContextMenu(index,lst,mapToGlobal(position),position+scrollOffset,this);	
	
		handleDesktopShortCut(sc,QPoint(0,0),this);
	}	
}

void MvQDetailedFolderView::slotContextMenu(const QPoint &position)
{
	QModelIndexList lst=selectedList();
	//QModelIndex index=indexAt(position);	
	QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value());
	
	handleContextMenu(indexAt(position),lst,mapToGlobal(position),position+scrollOffset,this);
}

void  MvQDetailedFolderView::iconCommandFromMain(QString name)
{	 	
	handleIconCommand(selectedList(),name);
}

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

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

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

void MvQDetailedFolderView::slotObjectRenamed(const QModelIndex& sourceIndex,QString /*oriName*/)
{
	QModelIndex index=filterModel_->mapFromSource(sourceIndex);
	dataChanged(index,index);	
}  

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

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

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


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

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

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


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


QRect MvQDetailedFolderView::pixmapRect(IconObject *obj)
{
  	QRect r=visualRect(filterModel_->mapFromSource(folderModel_->indexFromObject(obj)));
	r.setSize(QSize(getIconSize(),getIconSize()));
	return r;
}	

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

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

void MvQDetailedFolderView::mousePressEvent(QMouseEvent *event)
{
  	if( event->button() == Qt::LeftButton || event->button() == Qt::MidButton)
	{
		startPos_=event->pos();
	}
		
	QTreeView::mousePressEvent(event);
}

void MvQDetailedFolderView::mouseMoveEvent(QMouseEvent *event)
{
	if(!itemInfo_.isEmpty() && !indexAt(event->pos()).isValid())
	{
	  	itemInfo_=QString();
	  	emit itemEntered(itemInfo_);
	}
  
  	if(event->buttons() & (Qt::LeftButton | Qt::MidButton))
	{
		int distance = (event->pos() - startPos_).manhattanLength();
		if(distance >= QApplication::startDragDistance())
		{
		  	if(moveActionEnabled_)
			{  		  
		  		if(event->buttons() & Qt::LeftButton)		  
		  			performDrag(Qt::MoveAction,event->pos());
				else if(event->buttons() & Qt::MidButton)
			  		performDrag(Qt::CopyAction,event->pos());
			}
			else
			{
			  	performDrag(Qt::CopyAction,event->pos());
			}
		}	
	}
	
	QTreeView::mouseMoveEvent(event);
}

void MvQDetailedFolderView::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
	//We need only one index (the first column) for each row
	QList<IconObject*> objLst;
	QSet<int> rows;
	foreach(QModelIndex index,selectedIndexes())
	{
	 	if(!rows.contains(index.row())) 	
		{
		  	rows << index.row();
		  	QModelIndex idx=filterModel_->index(index.row(),0);
			objLst << folderModel_->objectFromIndex(filterModel_->mapToSource(idx));
		}
	}
				
	//It cannot be a helper!
	bool fromHelper=false;
	
	QDrag *drag=buildDrag(dragObj,objLst,fromHelper,this);	
	if(drag)
	{
		drag->exec(Qt::CopyAction | Qt::MoveAction,dropAction);	
	}	
}	

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

void MvQDetailedFolderView::checkDropTarget(QDropEvent *event)
{  
	IconObject* dragObj=0;
	if(event->mimeData()->hasFormat("metview/icon"))
	{	
		const MvQIconMimeData* mimeData=qobject_cast<const MvQIconMimeData*>(event->mimeData());
				
		if(mimeData)
			dragObj=mimeData->dragObject();
	}
	
	
	if(!dragObj)
	{  	
	  	removeDropTarget();
		return;
	}	

	Qt::DropAction dropAction=event->proposedAction();
	
	QModelIndex index=indexAt(event->pos());
	IconObject* obj=folderModel_->objectFromIndex(filterModel_->mapToSource(index));
	
	if(obj != dragObj && obj && obj->isFolder() && !obj->locked())
	{	  	
	  	MvQDropTarget::Instance()->reset(QString::fromStdString(obj->name()),(dropAction == Qt::MoveAction));							
		if(window())
		{  
			MvQDropTarget::Instance()->move(mapToGlobal(event->pos())+QPoint(20,20));
		}
		return;
	}
	else
	{
		removeDropTarget();
	}	
}	  

void MvQDetailedFolderView::removeDropTarget()
{	
  	MvQDropTarget::Instance()->hide();
}

void MvQDetailedFolderView::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->accept();
	}
	else  
		event->ignore();
}

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

void MvQDetailedFolderView::dragLeaveEvent(QDragLeaveEvent *event)
{
  	removeDropTarget();
	event->accept();
}	

void MvQDetailedFolderView::dropEvent(QDropEvent *event)
{
	qDebug() << "dropEvent" << folderModel_->fullName();
	
	removeDropTarget();
	
	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;
	}	

	//--------------------------------------
	// 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();
		MvQFolderModel *model=mimeData->model();
		if(dragObj && model)
		{  
			bool fromSameView=(model == folderModel_ || model->folder() == folderModel_->folder());
			performDrop(event->proposedAction(),mimeData,event->pos(),fromSameView);
	
			event->accept();
			return;
		}	
	}
	
	//--------------------------------------
	// Drag and drop from create icon panel
	//--------------------------------------
	
	else if(event->mimeData()->hasFormat("metview/new_icon"))
	{	
		const MvQNewIconMimeData* mimeData=qobject_cast<const MvQNewIconMimeData*>(event->mimeData());
				
		if(!mimeData)
		{
			event->ignore();
			return;
		}	
	
		if(mimeData->iconDefType() == MvQNewIconMimeData::UserDef)
		{
			const IconClass &kind=IconClass::find(mimeData->className().toStdString());
			kind.createOne(folderModel_->folder());						
	
			event->accept();
			return;
		}
	}
	
	event->ignore();	
}

void MvQDetailedFolderView::performDrop(Qt::DropAction dropAction,const MvQIconMimeData* data,
				       QPoint pos,bool fromSameView)
{  
	bool toSameFolder=true;
	Folder *folder=folderModel_->folder();
	IconObject *objAt=folderModel_->objectFromIndex(filterModel_->mapToSource(indexAt(pos)));
			
	//See if we drop the icons onto a folder icons. We do not allow dropping a folder into itself!!
	if(objAt && objAt->isFolder() && !data->objects().contains(objAt))
	{
		 folder=static_cast<Folder*>(objAt);
		 toSameFolder=false;
	}

	foreach(IconObject* obj, data->objects())
	{
		if(!isAccepted(obj))
			continue;
		
		//We set the object position to zero. This will instuct the icon folder view
		//(when the folder is next open in it) to find meaningful positions to the 
		//icons.
		
		if(dropAction == Qt::CopyAction || moveActionEnabled_== false)
		{	
			obj->clone(folder,false);  	
		}
		else if((!fromSameView || !toSameFolder) && 
		        dropAction == Qt::MoveAction && folder)
		{  			
			obj->position(0,0);
		  	folder->adopt(obj);
		}
	}	  
}


void MvQDetailedFolderView::keyPressEvent(QKeyEvent* event)
{
	QTreeView::keyPressEvent(event);
}

//=========================================
// Icons
//=========================================

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


void MvQDetailedFolderView::slotIconSizeChanged()
{
  	reset();
	for(int i=0; i < folderModel_->columnCount(QModelIndex())-1; i++)
	  	resizeColumnToContents(i);
}	

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


void MvQDetailedFolderView::showIcon(const QModelIndex& index)
{
  	scrollTo(index);
	setCurrentIndex(index);
}  

//=========================================
// Header
//=========================================

void MvQDetailedFolderView::slotHeaderContextMenu(const QPoint &position)
{
	int section=header()->logicalIndexAt(position); 
	
	if(section< 0 || section >= header()->count())
		return;

	QList<QAction*> lst;
	QMenu *menu=new QMenu(this);	
	QAction *ac;
	
	for(int i=0; i <header()->count(); i++)
	{
	  	QString name=header()->model()->headerData(i,Qt::Horizontal).toString();
		ac=new QAction(menu);
		ac->setText(name);
		ac->setCheckable(true);
		ac->setData(i);
		
		if(i==0)
		{
		  	ac->setChecked(true);
			ac->setEnabled(false);
		}
		else
		{  
			ac->setChecked(!(header()->isSectionHidden(i)));
		}
		
		menu->addAction(ac);
	}	
		
	ac=menu->exec(header()->mapToGlobal(position));
	if(ac && ac->isEnabled() && ac->isCheckable())
	{
	  	int i=ac->data().toInt();
	  	header()->setSectionHidden(i,!ac->isChecked());
		MvQDesktopSettings::headerVisible_[i]=ac->isChecked();		
		broadcastHeaderChange();	
	}	
	delete menu;
}

void MvQDetailedFolderView::updateHeader()
{
	if(header()->count() != MvQDesktopSettings::headerVisible_.count())
	{
	  	MvQDesktopSettings::headerVisible_.clear();
		
 		for(int i=0; i < header()->count(); i++)
				MvQDesktopSettings::headerVisible_ << !header()->isSectionHidden(i);
		
		qDebug() << MvQDesktopSettings::headerVisible_;
	}
	else
	{  		  
  		for(int i=0; i < MvQDesktopSettings::headerVisible_.count(); i++)
		{
			if(i!=0) 
				header()->setSectionHidden(i,!MvQDesktopSettings::headerVisible_[i]);
		}
	}	
}

void MvQDetailedFolderView::broadcastHeaderChange()
{
  	foreach(MvQDetailedFolderView *v,views)
		  	v->updateHeader();
}
