/***************************** 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 <QDebug>

#include "MvQGribDumpModel.h"

#include "MvQKeyMimeData.h"

#include "GribMetaData.h"
#include "MvKeyProfile.h"

//=====================================
//
//MvQGribMvDumpModel
//
//=====================================


MvQGribMvDumpModel::MvQGribMvDumpModel()
{
	data_=0;
}

void MvQGribMvDumpModel::dataIsAboutToChange()
{
	beginResetModel();
}

void MvQGribMvDumpModel::setDumpData(GribMvDump *dump)
{	
	data_=dump;
	
	//Reset the model (views will be notified)
	endResetModel();
}

void MvQGribMvDumpModel::setGribNameSpace(QString ns)
{	
	gribNameSpace_=ns;
	
	//Reset the model (views will be notified)
	endResetModel();
}

int MvQGribMvDumpModel::columnCount( const QModelIndex& /* parent */ ) const
{
   	 return 3;
}

int MvQGribMvDumpModel::rowCount( const QModelIndex& parent) const
{
	if(!data_)
		return 0;

	//Non-root
	if(!parent.isValid() )
	{
		return data_->itemNum();
	}
	else
	{
		return 0;
	}
}


QVariant MvQGribMvDumpModel::data( const QModelIndex& index, int role ) const
{
	if( !index.isValid() || 
	    (role != Qt::DisplayRole && role != Qt::UserRole))
        {			
		return QVariant();
	}

	if(role == Qt::UserRole) 
	{
		if(index.column() != 0)
			return QVariant();

		GribItem *item = data_->item().at(index.row());
		if(gribNameSpace_ == "Default")
		{
			if(QString::fromStdString(item->name()).contains(".") == false)
			{
				return QString::number(1);
			}
			else
			{
				return QString::number(0);
			}
		}
		else
		{
			if(QString::fromStdString(item->name()).startsWith(gribNameSpace_ + "."))
			{
				return QString::number(1);
			}
			else
			{
				return QString::number(0);
			}
		}
		
	}
	else
	{

		GribItem *item = data_->item().at(index.row());
		return label(item,index.column());
	}
}


QVariant MvQGribMvDumpModel::headerData( const int section, const Qt::Orientation orient , const int role ) const
{
	if ( orient != Qt::Horizontal || role != Qt::DisplayRole )
      		  return QAbstractItemModel::headerData( section, orient, role );

   	switch ( section ) 
	{
   	case 0: return "Key name (GRIB API)";
	case 1: return "Key type";
	case 2: return "Value";
   	}

    	return QVariant();
}


QString MvQGribMvDumpModel::label(GribItem *item,const int column ) const
{
	switch ( column ) 
	{
    	case 0: 
	{
		return QString(item->name().c_str());
	}
	case 1: 
	{
		return QString(item->type().c_str());
	}
	case 2: 
	{
		return QString(item->value().c_str());
	}
	default:
        	return QString();
    	}
}


QModelIndex MvQGribMvDumpModel::index( int row, int column, const QModelIndex & /*parent*/ ) const
{
#ifdef METVIEW_QT5
    return createIndex(row,column,(void*)0);
#else
    return createIndex(row,column,0);
#endif
}


QModelIndex MvQGribMvDumpModel::parent( const QModelIndex & /*index*/ ) const
{		
	 return QModelIndex();

}

Qt::ItemFlags MvQGribMvDumpModel::flags ( const QModelIndex & /*index*/) const
{
	Qt::ItemFlags defaultFlags;

	defaultFlags=Qt::ItemIsEnabled |
		   Qt::ItemIsSelectable;

	return Qt::ItemIsDragEnabled | defaultFlags;
}

Qt::DropActions MvQGribMvDumpModel::supportedDropActions() const
{
	return Qt::CopyAction;
}

QStringList MvQGribMvDumpModel::mimeTypes() const
 {
	QStringList types;
    	types << "metview/mvkey";
    	return types;
 }

QMimeData* MvQGribMvDumpModel::mimeData(const QModelIndexList &indexes) const
 {
     	MvQKeyMimeData *mimeData = new MvQKeyMimeData(this);
     	
	QList<int> procRows;

    	foreach (QModelIndex index, indexes) 
	{
	       	if (index.isValid() && 
		    procRows.indexOf(index.row()) == -1) 
		{
			GribItem *item = data_->item().at(index.row());
			MvKey *key=new MvKey(item->name(),item->name());
			mimeData->addKey(key,index.row());
			procRows << index.row();
         	}
     	}

     	return mimeData;
}

bool MvQGribMvDumpModel::dropMimeData(const QMimeData*/*data*/,
     Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex& /*parent*/)
{
        return false;
}

//=====================================
//
// MvQGribWmoDumpModel
//
//=====================================


MvQGribWmoDumpModel::MvQGribWmoDumpModel()
{
	data_=0;
}

void MvQGribWmoDumpModel::dataIsAboutToChange()
{
	beginResetModel();
}

void MvQGribWmoDumpModel::setDumpData(GribWmoDump *dump)
{	
	data_=dump;
	
	//Reset the model (views will be notified)
	endResetModel();
}


int MvQGribWmoDumpModel::columnCount( const QModelIndex& /* parent */ ) const
{
   	 return 3;
}

int MvQGribWmoDumpModel::rowCount( const QModelIndex& parent) const
{
	if(!data_)
		return 0;

	//Non-root
	if(parent.isValid() )
	{
		int id=parent.internalId();
		if(idToLevel(id) == 0)
		{
			return data_->section().at(id)->itemNum();			
		}
		if(idToLevel(id) == 1)
		{	
			int parentRow=idToParentRow(id);
			int row=parent.row();
			int num=data_->section().at(parentRow)->item().at(row)->arrayData().size();
			return num;			
		}

		else
		{
			return 0;
		}
	}
	//Root
	else
	{
		return data_->sectionNum();
	}
}


QVariant MvQGribWmoDumpModel::data( const QModelIndex& index, int role ) const
{
	if( !index.isValid() || (role != Qt::DisplayRole && role != Qt::ToolTipRole))
        {			
		return QVariant();
	}
	
	int id=index.internalId();

	int level=idToLevel(id);

	if(level == 0)
	{
		GribSection *section = data_->section().at(id);
		return label(section,index.column());
	}
	else if(level == 1)
	{
		int parentRow=idToParentRow(id);
		GribSection *section = data_->section().at(parentRow);	
		GribItem *item= section->item().at(index.row());
		return label(item,index.column());
	}
	else if(level == 2)
	{
		//int parentId=index.parent().internalId();
		//int grandParentRow=idToParentRow(parentId);

		int grandParentRow=idToGrandParentRow(id);
		GribSection *section = data_->section().at(grandParentRow);	

		int parentRow=idToParentRow(id);
		GribItem *item= section->item().at(parentRow);

		QString value(item->arrayData().at(index.row()).c_str());

		return label(value,index.column());
	}
	
	return  QVariant();
}


QVariant MvQGribWmoDumpModel::headerData( const int section, const Qt::Orientation orient , const int role ) const
{
	if ( orient != Qt::Horizontal || role != Qt::DisplayRole )
      		  return QAbstractItemModel::headerData( section, orient, role );

   	switch ( section ) 
	{
   	case 0: return "Position";
  	case 1: return "Key name (GRIB API)";
	case 2: return "Value";
   	}

    	return QVariant();
}


QString MvQGribWmoDumpModel::label(GribSection *section,const int column ) const
{
	switch ( column ) 
	{
    	case 0: 
	{
		return QString(section->name().c_str());
	}
	default:
        	return QString();
    	}
}

QString MvQGribWmoDumpModel::label(GribItem* item, const int column ) const
{
	switch ( column ) 
	{
    	case 0: 
	{
		return QString(item->pos().c_str());
	}
     	case 1: 
	{
		return QString(item->name().c_str());
	}
    	case 2: 
	{
		return QString(item->value().c_str());
	}
	default:
        	return QString();
    	}
}

QString MvQGribWmoDumpModel::label(QString value,const int column ) const
{
	switch ( column ) 
	{
    	case 2: 
	{
		return value;
	}
	default:
        	return QString();
    	}
}

QModelIndex MvQGribWmoDumpModel::index( int row, int column, const QModelIndex & parent ) const
{
	if(!data_ || row < 0 || column < 0 || parent.column() > 3)
	{
		return QModelIndex();
	}
	
	//Parent is non-root -> level-1 items: id is the parent row (number+1)*1000
	if(parent.isValid() )
	{
		int id;
		int parentId=parent.internalId();
		int parentLevel=idToLevel(parentId);

		if(parentLevel == 0)
		{
			id=(parent.row()+1)*1000;
		}
		else if(parentLevel == 1)
		{	
			id=parentId+(parent.row()+1);
		}
		else
		{
			return QModelIndex();
		}

		return createIndex(row, column, id); 
	}
	//Parent is root -> level-0 items: id is the row number
	else
	{
		return createIndex(row, column, row); 
	}
}

QModelIndex MvQGribWmoDumpModel::parent( const QModelIndex & index ) const
{		
	if(!index.isValid() )
	{
		return QModelIndex();
	}
	
	int id=index.internalId();
	if(idToLevel(id) == 0)
	{
		return QModelIndex();
	}
	else if(idToLevel(id) == 1)
	{
		int parentRow=idToParentRow(id);
		return createIndex(parentRow,0,parentRow);
	}
	else if(idToLevel(id) == 2)
	{
		int parentRow=idToParentRow(id);
		int grandParentRow=idToGrandParentRow(id);		
		return createIndex(parentRow,0,(grandParentRow+1)*1000);
	}

	return QModelIndex();
}

int MvQGribWmoDumpModel::idToLevel(int id) const
{
	if(id >=0 && id < 1000)
		return 0;
	else if(id % 1000 ==0)		
		return 1;
	else
		return 2;
}

int MvQGribWmoDumpModel::idToParentRow(int id) const
{
	int level =idToLevel(id);

	if(level == 0)
		return -1;
	else if(level == 1)
		return id/1000-1;
	else if(level == 2)
		return id-(id/1000)*1000-1;
	
	return -1;
}

int MvQGribWmoDumpModel::idToGrandParentRow(int id) const
{
	int level =idToLevel(id);

	if(level == 0)
		return -1;
	else if(level == 1)
		return -1;
	else if(level == 2)
		return id/1000-1;
	
	return -1;
}

Qt::ItemFlags MvQGribWmoDumpModel::flags (const QModelIndex& /*index*/) const
{
	Qt::ItemFlags defaultFlags;

	defaultFlags=Qt::ItemIsEnabled |
		   Qt::ItemIsSelectable;

	return Qt::ItemIsDragEnabled | defaultFlags;
}

Qt::DropActions MvQGribWmoDumpModel::supportedDropActions() const
{
	return Qt::CopyAction;
}

QStringList MvQGribWmoDumpModel::mimeTypes() const
 {
	QStringList types;
    	types << "metview/mvkey";
    	return types;
 }

QMimeData* MvQGribWmoDumpModel::mimeData(const QModelIndexList &indexes) const
 {
     	MvQKeyMimeData *mimeData = new MvQKeyMimeData(this);
     	
	QList<int> procRows;

    	foreach (QModelIndex index, indexes) 
	{
	       	int id=index.internalId();
		if (index.isValid() && idToLevel(id) == 1 &&
		    procRows.indexOf(index.row()) == -1) 
		{
			//Index to the second column!!!
			QModelIndex col=createIndex(index.row(),1,id);

			string keyName=data(col).toString().toStdString();
			MvKey *key=new MvKey(keyName,keyName);
			mimeData->addKey(key,index.row());
			procRows << index.row();
         	}
     	}

     	return mimeData;
}

bool MvQGribWmoDumpModel::dropMimeData(const QMimeData* /*data*/,
     Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex& /*parent*/)
{
        return false;
}

//=====================================
//
// MvQGribStdDumpModel
//
//=====================================

MvQGribStdDumpModel::MvQGribStdDumpModel()
{
	data_=0;
}

void MvQGribStdDumpModel::dataIsAboutToChange()
{
	beginResetModel();
}

void MvQGribStdDumpModel::setDumpData(GribStdDump *dump)
{	
	data_=dump;
	
	//Reset the model (views will be notified)
	endResetModel();
}

int MvQGribStdDumpModel::columnCount( const QModelIndex& /* parent */ ) const
{
   	 return 3;
}

int MvQGribStdDumpModel::rowCount( const QModelIndex& parent) const
{
	if(!data_)
		return 0;

	//Non-root
	if(parent.isValid() )
	{
		int id=parent.internalId();
		if(idToLevel(id) == 0)
		{
			return data_->item().at(id)->arrayData().size();			
		}
		else
		{
			return 0;
		}
	}
	//Root
	else
	{
		return data_->itemNum();
	}
}


QVariant MvQGribStdDumpModel::data( const QModelIndex& index, int role ) const
{
	if( !index.isValid() || role != Qt::DisplayRole)
        {			
		return QVariant();
	}
	
	int id=index.internalId();
	if(idToLevel(id) == 0)
	{
		GribItem *item = data_->item().at(id);
		return label(item,index.column());
	}
	else if(idToLevel(id) == 1)
	{
		int parentRow=idToParentRow(id);
		GribItem *item = data_->item().at(parentRow);	
		string value = item->arrayData().at(index.row());
		return label(value,index.column());
	}
	
	return  QVariant();
}


QVariant MvQGribStdDumpModel::headerData( const int section, const Qt::Orientation orient , const int role ) const
{
	if ( orient != Qt::Horizontal || role != Qt::DisplayRole )
      		  return QAbstractItemModel::headerData( section, orient, role );

   	switch ( section ) 
	{
   	case 0: return "Key name (GRIB API)";
  	case 1: return "Value";
	case 2: return "Description";
   	}

    	return QVariant();
}


QString MvQGribStdDumpModel::label(GribItem *item,const int column ) const
{
	switch ( column ) 
	{
    	case 0: 
	{
		return QString(item->name().c_str());
	}
	case 1: 
	{
		return QString(item->value().c_str());
	}
	case 2: 
	{
		return QString(item->description().c_str());
	}

	default:
        	return QString();
    	}
}

QString MvQGribStdDumpModel::label(string value, const int column ) const
{
	switch ( column ) 
	{
    	case 0: 
	{
		return QString(value.c_str());
	}
    
	default:
        	return QString();
    	}
}

QModelIndex MvQGribStdDumpModel::index( int row, int column, const QModelIndex & parent ) const
{
	if(!data_ || row < 0 || column < 0 || parent.column() > 3)
	{
		return QModelIndex();
	}
	
	//Parent is non-root -> level-1 items: id is the (parent row number +1)*1000
	if(parent.isValid() )
	{
		int id=(parent.row()+1)*1000;
		return createIndex(row, column, id); 
	}
	//Parent is root -> level-0 items: id is the row number
	else
	{
		return createIndex(row, column, row); 
	}
}


QModelIndex MvQGribStdDumpModel::parent( const QModelIndex & index ) const
{		
	if(!index.isValid() )
	{
		return QModelIndex();
	}
	
	int id=index.internalId();
	if(idToLevel(id) == 0)
	{
		return QModelIndex();
	}
	else 
	{
		int parentRow=idToParentRow(id);
		return createIndex(parentRow,0,parentRow);
	}

	return QModelIndex();
}

int MvQGribStdDumpModel::idToLevel(int id) const
{
	if(id >=0 && id < 1000)
		return 0;
	else
		return 1;
}

int MvQGribStdDumpModel::idToParentRow(int id) const
{
	if(idToLevel(id) == 0)
		return -1;
	else
		return id/1000-1;
}


Qt::ItemFlags MvQGribStdDumpModel::flags (const QModelIndex& /*index*/) const
{
	Qt::ItemFlags defaultFlags;

	defaultFlags=Qt::ItemIsEnabled |
		   Qt::ItemIsSelectable;

	return Qt::ItemIsDragEnabled | defaultFlags;
}

Qt::DropActions MvQGribStdDumpModel::supportedDropActions() const
{
	return Qt::CopyAction;
}

QStringList MvQGribStdDumpModel::mimeTypes() const
 {
	QStringList types;
    	types << "metview/mvkey";
    	return types;
 }

QMimeData* MvQGribStdDumpModel::mimeData(const QModelIndexList &indexes) const
 {
     	MvQKeyMimeData *mimeData = new MvQKeyMimeData(this);
     	
	QList<int> procRows;

    	foreach (QModelIndex index, indexes) 
	{
	       	int id=index.internalId();
		if (index.isValid() && idToLevel(id) == 0 &&
		    procRows.indexOf(index.row()) == -1) 
		{
			//Index to the first column!!!
			QModelIndex col=createIndex(index.row(),0,id);

			string keyName=data(index).toString().toStdString();
			MvKey *key=new MvKey(keyName,keyName);
			mimeData->addKey(key,index.row());
			procRows << index.row();
         	}
     	}

     	return mimeData;
}

bool MvQGribStdDumpModel::dropMimeData(const QMimeData* /*data*/,
     Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex& /*parent*/)
{
        return false;
}


//=====================================
//
//MvQGribValueDumpModel
//
//=====================================

MvQGribValueDumpModel::MvQGribValueDumpModel()
{
	data_=0;
	minValAsDecimal_=0.001;
}

void MvQGribValueDumpModel::dataIsAboutToChange()
{
	beginResetModel();
}

void MvQGribValueDumpModel::setDumpData(GribValueDump *dump)
{	
	data_=dump;
	
	if(data_)
	{	
		if(data_->decimalPlaces() > 0)
		{
			minValAsDecimal_=0.0001;
		}
		else
		{
	  		minValAsDecimal_=pow(10.,data_->decimalPlaces()-4);
		}	
	}
	
	//Reset the model (views will be notified)
	endResetModel();
}

int MvQGribValueDumpModel::columnCount( const QModelIndex& /* parent */ ) const
{
   	 return 4;
}

int MvQGribValueDumpModel::rowCount( const QModelIndex& parent) const
{
	if(!data_)
		return 0;

	//Non-root
	if(!parent.isValid() )
	{
		return data_->num();
	}
	else
	{
		return 0;
	}
}


QVariant MvQGribValueDumpModel::data( const QModelIndex& index, int role ) const
{
	if( !index.isValid() || (role != Qt::DisplayRole && role != Qt::UserRole))
        {			
		return QVariant();
	}

	if(role == Qt::DisplayRole) 
	{
		switch (index.column())
		{
			case 0: 
				return index.row()+1;
			case 1: 
				return QString::number(data_->latitude()[index.row()],'f',3);
			case 2: 
				return QString::number(data_->longitude()[index.row()],'f',3);	
			case 3: 
				if(fabs(data_->value()[index.row()]) > minValAsDecimal_)
				{  
			  		return QString::number(data_->value()[index.row()],'f',data_->decimalPlaces()+4);
				}
				else
				{
				  	return QString::number(data_->value()[index.row()],'e',4);
				}
				break;
			default:
				return QVariant();
		}				
	}
	
	//Will be used for sorting
	else if(role == Qt::UserRole) 
	{
		switch (index.column())
		{
			case 0: 
				return index.row()+1;
			case 1: 
				return data_->latitude()[index.row()];
			case 2: 
				return data_->longitude()[index.row()];	
			case 3: 
				return data_->value()[index.row()];	
			default:
				return QVariant();
		}				
	}
	
	
	
	return QVariant();
}


QVariant MvQGribValueDumpModel::headerData( const int section, const Qt::Orientation orient , const int role ) const
{
	if ( orient != Qt::Horizontal || role != Qt::DisplayRole )
      		  return QAbstractItemModel::headerData( section, orient, role );

   	switch ( section ) 
	{
   	case 0: return tr("Index");
	case 1: return tr("Latitude");
	case 2: return tr("Longitude");
	case 3: return tr("Value");
   	}

    	return QVariant();
}



QModelIndex MvQGribValueDumpModel::index( int row, int column, const QModelIndex & /*parent*/ ) const
{
#ifdef METVIEW_QT5
    return createIndex(row,column,(void*)0);
#else
    return createIndex(row,column,0);
#endif
}


QModelIndex MvQGribValueDumpModel::parent( const QModelIndex & /*index*/ ) const
{		
	 return QModelIndex();

}
