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

 Copyright 2012 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 <QDebug>
#include <QFontMetrics>
#include <QHeaderView>
#include <QHBoxLayout> 
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QSortFilterProxyModel>
#include <QToolButton>
#include <QTreeView>
#include <QVBoxLayout>

#include <stdlib.h>

#include "MvOdb.h"


#include "MvQOdbDataWidget.h"
#include "MvQOdbModel.h"


MvQOdbDataWidget::MvQOdbDataWidget(QWidget *parent) : QWidget(parent)
{
	data_=0;

	maxChunkSizeInMb_=60;
	chunkSize_=0;
	chunkNum_=0;
	currentChunk_=-1;
	dataInitialised_=false;

	QVBoxLayout *dataLayout = new QVBoxLayout;
	dataLayout->setContentsMargins(0,0,0,0);
	setLayout(dataLayout);

	//Data tree
	dataModel_= new MvQOdbDataModel();

	dataSortModel_= new QSortFilterProxyModel;
        dataSortModel_->setSourceModel(dataModel_);
	dataSortModel_->setDynamicSortFilter(true);
		
	dataTree_=new QTreeView;
	dataTree_->setObjectName("dataTree");
	dataTree_->setProperty("mvStyle",0);
	dataTree_->setAlternatingRowColors(true);
        dataTree_->setAllColumnsShowFocus(true);
	dataTree_->setModel(dataSortModel_);	
	dataTree_->setUniformRowHeights(true);
	dataTree_->setRootIsDecorated(false);  
	dataTree_->setSortingEnabled(false); //!
	
	//Set header ContextMenuPolicy
	dataTree_->header()->setContextMenuPolicy(Qt::CustomContextMenu);
	
	connect(dataTree_->header(),SIGNAL(customContextMenuRequested(const QPoint &)),
                this, SLOT(slotHeaderContextMenu(const QPoint &)));
		
	connect(dataTree_,SIGNAL(clicked(const QModelIndex&)),
		this,SLOT(slotRowSelected(const QModelIndex&)));
	
	
	dataLayout->addWidget(dataTree_);

	//Control at the bottom
	QHBoxLayout *hb=new QHBoxLayout; 
	dataLayout->addLayout(hb);

	hb->addSpacing(5);

	rowNumLabel_=new QLabel;
	hb->addWidget(rowNumLabel_);

	hb->addStretch(1);

	chunkSizeLabel_ = new QLabel;
	hb->addWidget(chunkSizeLabel_);
	hb->addSpacing(12);

	dataBlockLabel_=new QLabel(tr("<b>Data block:</b>"));
	hb->addWidget(dataBlockLabel_);

	chunkPrevTb_ = new QToolButton(this);
	chunkPrevTb_->setToolTip(tr("Previous data block"));
	chunkPrevTb_->setIcon(QPixmap(QString::fromUtf8(":/examiner/triangle_left.svg")));
	chunkPrevTb_->setAutoRaise(true);
	hb->addWidget(chunkPrevTb_);

	chunkEdit_ = new QLineEdit(this);
	chunkEdit_->setToolTip(tr("Current data block"));
	QSizePolicy sp=chunkEdit_->sizePolicy();
	sp.setHorizontalPolicy(QSizePolicy::Fixed);
	chunkEdit_->setSizePolicy(sp);
	QSize size=chunkEdit_->fontMetrics().size(Qt::TextSingleLine,"333");
	chunkEdit_->setFixedWidth(size.width()+10);
	chunkEditValidator_=new QIntValidator(this);

	connect(chunkEdit_,SIGNAL(editingFinished()),
		this, SLOT(slotChunkEdited()));  

	chunkNumLabel_=new QLabel(this);

	hb->addWidget(chunkEdit_);
	hb->addWidget(chunkNumLabel_);
		
	chunkNextTb_ = new QToolButton(this);
	chunkNextTb_->setToolTip(tr("Next data block"));
	chunkNextTb_->setIcon(QPixmap(QString::fromUtf8(":/examiner/triangle_right.svg")));
	chunkNextTb_->setAutoRaise(true);
	hb->addWidget(chunkNextTb_);

	hb->addSpacing(5);

	connect(chunkPrevTb_,SIGNAL(clicked()),
		this,SLOT(slotToPrevChunk()));

	connect(chunkNextTb_,SIGNAL(clicked()),
		this,SLOT(slotToNextChunk()));

	chunkPrevTb_->setEnabled(false);
	chunkNextTb_->setEnabled(false);	

}

MvQOdbDataWidget::~MvQOdbDataWidget()
{
	if(data_)
		delete data_;
}

//Should be called only once 
bool MvQOdbDataWidget::init(QString path)
{
	vector<int> v;
	return init(path,v);
}	

bool MvQOdbDataWidget::init(QString path,const vector<int>& filter)
{
	if(dataInitialised_)
	{
		if(data_->path() == path.toStdString())	  
		{
			dataModel_->setFilter(filter);
			setTotalNumDisplay();
			return true;
		}
		else
		{
			delete data_;
			data_=0;
		}	
	}
			
	dataInitialised_=true;

	//Create a data object!!
	data_= MvOdbFactory::make(path.toStdString());
	if(data_ && data_->version() != MvAbstractOdb::VersionNew)
	{
		delete data_;
		data_=0;
		return false;
	}

	if(!data_)
	{
		//metaTab_->setTabEnabled(4,false);
		return false;
	}		
	else
	{				
		dataModel_->setFilter(filter);
		loadData();
		return true;
	}	

	return true;
}

void MvQOdbDataWidget::loadData()
{
	if(!data_)
		return;  
				
	loadStarted();
	//statusMessageLabel_->setText(tr("Counting number of rows in ODB data ..."));
	
	int rowNum=data_->rowNum(); 
	int colNum=data_->columnNum();
	
	int totSizeInMb=-1;
	if( maxChunkSizeInMb_ >0)
	{
		totSizeInMb=data_->totalSizeInMb();
	}
	
	//statusMessageLabel_->clear();	

	qDebug() << "Data" << rowNum << colNum << maxChunkSizeInMb_;

	chunkSize_=0;
	chunkNum_=0;

	if(totSizeInMb >  maxChunkSizeInMb_ && maxChunkSizeInMb_ >0)
	{	  
	        int r=static_cast<float>(rowNum)*(static_cast<float>(maxChunkSizeInMb_)/static_cast<float>(totSizeInMb));
		int chunk;
		
		if(r < 1000)
			chunk=500;
		else if(r < 5000)
			chunk=1000;
		else if(r <10000)
			chunk=5000;
		else if(r < 25000)
			chunk=10000;
		else if(r < 50000)
			chunk=25000;
		else if(r < 100000)
			chunk=50000;
		else if(r < 300000)
			chunk=100000;
		else
		  	chunk=200000;
	
		chunkSize_=chunk;
		chunkNum_=rowNum/chunkSize_ + ((rowNum % chunkSize_ == 0)?0:1);

		//Disable sorting in tree
		dataTree_->setSortingEnabled(false);
				
		dataModel_->dataIsAboutToChange();		
		initCurrentChunk(); //set currentChunk_ to 0
		data_->setChunkSize(chunkSize_);
		data_->setCurrentChunk(currentChunk_);
		dataModel_->setData(data_);
		
		chunkEditValidator_->setRange(1,chunkNum_);
		chunkEdit_->setValidator(chunkEditValidator_);			
		chunkNumLabel_->setText("/ "+QString::number(chunkNum_));			
		chunkSizeLabel_->setText(tr("<b>Data block size:</b> ") + QString::number(chunkSize_) + tr(" rows"));

		chunkSizeLabel_->show();
		dataBlockLabel_->show();
		chunkEdit_->show();
		chunkNextTb_->show();
		chunkPrevTb_->show();
		chunkNumLabel_->show();		
	}	
	else
	{				
		chunkSizeLabel_->hide();
		dataBlockLabel_->hide();
		chunkEdit_->hide();
		chunkNextTb_->hide();
		chunkPrevTb_->hide();
		chunkNumLabel_->hide();
			
		dataModel_->dataIsAboutToChange();
		data_->setChunkSize(0);
		dataModel_->setData(data_);
			
		dataTree_->setSortingEnabled(true);
		dataTree_->sortByColumn (0,Qt::AscendingOrder); 			
	}	
	
	
	for(int i=0;i < dataModel_->columnCount()-1; i++)
	{ 
		dataTree_->resizeColumnToContents(i);
	}

	setTotalNumDisplay();
	
	loadFinished();
}


void MvQOdbDataWidget::slotToPrevChunk()
{
	setCurrentChunk(currentChunk_-1);
}

void MvQOdbDataWidget::slotToNextChunk()
{
	setCurrentChunk(currentChunk_+1);
}

void MvQOdbDataWidget::slotChunkEdited()
{
	setCurrentChunk(chunkEdit_->text().toInt()-1);
}

void MvQOdbDataWidget::setCurrentChunk(int chunk)
{
	if(chunkSize_== 0)
		return;

	int oriVal=currentChunk_;
	if(chunk<0)
		currentChunk_=0;
	else if (chunk >= chunkNum_)
		currentChunk_=chunkNum_-1;
	else
		currentChunk_=chunk;

	if(currentChunk_<=0)
	{
		chunkPrevTb_->setEnabled(false);
		chunkNextTb_->setEnabled(true);
	}
	else if(currentChunk_ >= chunkNum_-1)
	{
		chunkPrevTb_->setEnabled(true);
		chunkNextTb_->setEnabled(false);
	}
	else
	{
		chunkPrevTb_->setEnabled(true);
		chunkNextTb_->setEnabled(true);
	}

	if(oriVal != currentChunk_)
	{
		loadStarted();
		//statusMessageLabel_->setText(tr("Reading ODB data ..."));
		
		dataModel_->dataIsAboutToChange();
		data_->setCurrentChunk(currentChunk_);
		dataModel_->setData(data_);

		if(chunkEdit_->text().toInt() != currentChunk_+1)
		{
			chunkEdit_->setText(QString::number(currentChunk_+1));
		}

		loadFinished();
		//statusMessageLabel_->setText(tr("Reading ODB data ..."));
		//statusMessageLabel_->clear();		
	}


}	

void MvQOdbDataWidget::initCurrentChunk()
{
	currentChunk_=0;
  
	chunkPrevTb_->setEnabled(false);
	chunkNextTb_->setEnabled(true);
			
	if(chunkEdit_->text().toInt() != currentChunk_+1)
	{
		chunkEdit_->setText(QString::number(currentChunk_+1));
	}
}	


void MvQOdbDataWidget::setMaxChunkSizeInMb(int maxNum)
{ 
  	if(maxNum < 0 || maxNum > 2000)
		return;  
	  
	maxChunkSizeInMb_=maxNum;
	
	if(dataInitialised_)
	{
	  	loadData();
	}	
}	

void MvQOdbDataWidget::loadStarted()
{
	QApplication::setOverrideCursor(QCursor(Qt::BusyCursor));
}

void MvQOdbDataWidget::loadFinished()
{
	QApplication::restoreOverrideCursor();
}

void MvQOdbDataWidget::setFilter(const vector<int>& filter)
{
	dataModel_->setFilter(filter);
}

void MvQOdbDataWidget::highlightRow(int row)
{
  	QModelIndex index=dataSortModel_->mapFromSource(dataModel_->index(row,0));
	dataTree_->setCurrentIndex(index);
	//dataTree_->scrollTo()
}

void MvQOdbDataWidget::slotRowSelected(const QModelIndex& index)
{
	QModelIndex dataIndex=dataSortModel_->mapToSource(index); 
	emit dataRowSelected(dataIndex.row());
}  


void MvQOdbDataWidget::setTotalNumDisplay()
{
	int displayedRowNum=data_->rowNum();
	if(dataModel_->filtered())
	{
		displayedRowNum=dataModel_->rowCount();
	}
	
	rowNumLabel_->setText(tr("<b>Total number of rows:</b> ") + QString::number(displayedRowNum));
}
	
void MvQOdbDataWidget::slotHeaderContextMenu(const QPoint &position)
{
	int section=dataTree_->header()->logicalIndexAt(position); 
	
	if(section< 0 || section >= dataTree_->header()->count())
		return;

	QMenu menu;
	QMenu subMenu(tr("Show other columns"));
	
	
	int numOfShown=0;
	for(int i=0; i < dataModel_->columnCount(); i++)
	{
		 if(!dataTree_->isColumnHidden(i))
		 {  		   
			numOfShown++;
		 }
	}
	
	QAction *actionHide=0;
	
	//Thic column can be hidden only if there are other visible columns as well
	if(!dataTree_->isColumnHidden(section) && numOfShown > 1)
	{
		actionHide = new QAction(this);
		actionHide->setText(tr("Hide ") + "\'" + dataModel_->headerData(section,Qt::Horizontal,Qt::DisplayRole).toString() + "\'");
		menu.addAction(actionHide);	

		menu.addSeparator();
	}
	
	//Show columns
	QMap<QAction*,int> actionShow;
	for(int i=0; i < dataModel_->columnCount(); i++)
	{
		 if(dataTree_->isColumnHidden(i))
		 {  		   
		 	//SubMenu if there are too amny items
	  	 	if(actionShow.count() == 6)
		 	{
		 		menu.addMenu(&subMenu);
		 	}
		 	
		 	QAction *ac = new QAction(this);
			QString acText=dataModel_->headerData(i,Qt::Horizontal,Qt::DisplayRole).toString();		
			if(actionShow.count() >= 6)
		 	{
		   		ac->setText(acText);
		   		subMenu.addAction(ac);
			}
			else
			{
			  	ac->setText(tr("Show ") + "\'" + acText + "\'");
				menu.addAction(ac);
			}			
			actionShow[ac]=i;
		 }		 
	}	 
	
	menu.addSeparator();
	
	QAction *actionShowAll = new QAction(this);
	actionShowAll->setText(tr("Show all columns"));    
	menu.addAction(actionShowAll);
	
	QAction *actionHideAll = new QAction(this);
	actionHideAll->setText(tr("Hide all other columns"));
	menu.addAction(actionHideAll);
		
	QAction *action=menu.exec(dataTree_->header()->mapToGlobal(position));
	  
	bool actionSelected=false;
	
	if(action == actionHide)
	{			
         	dataTree_->hideColumn(section);	
		actionSelected=true;			
	}
	else if(action == actionShowAll)
	{			
         	for(int i=0; i < dataModel_->columnCount(); i++)
		{
			dataTree_->showColumn(i);
		}
		actionSelected=true;
	}
	else if(action == actionHideAll)
	{			
         	for(int i=0; i < dataModel_->columnCount(); i++)
		{
			if(!dataTree_->isColumnHidden(i) && i != section)
				dataTree_->hideColumn(i);
		}
		actionSelected=true;	
	}	
	else if(action)
	{			
         	QMap<QAction*,int>::iterator it=actionShow.find(action);
		if(it != actionShow.end())
		{         		
         		dataTree_->showColumn(it.value());
		}
		actionSelected=true;	
	}
	
	if(actionSelected)
	{  
		for(int i=0; i < dataModel_->columnCount()-1; i++)
		{
			dataTree_->resizeColumnToContents(i);
		}
	}	
	
	subMenu.clear();
	menu.clear();
}

QStringList MvQOdbDataWidget::visibleColumns()
{	
  	QStringList lst;
  	for(int i=0; i < dataModel_->columnCount(); i++)
	{
		 if(!dataTree_->isColumnHidden(i))
		 {  		   
		 	lst << dataModel_->headerData(i,Qt::Horizontal,Qt::DisplayRole).toString();
		 }	
	}
	
	return lst;
}

void MvQOdbDataWidget::setVisibleColumns(QStringList lst)
{	
  	for(int i=0; i < dataModel_->columnCount(); i++)
	{
		if(lst.indexOf(dataModel_->headerData(i,Qt::Horizontal,Qt::DisplayRole).toString()) != -1)
		{
			dataTree_->showColumn(i);
		}	
		else
		{
			dataTree_->hideColumn(i);
		}	
	}
}	
	
