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

#include "MvQKeyProfileModel.h"

#include "MvQKeyMimeData.h"
#include "MvKeyProfile.h"

MvQKeyProfileSortFilterModel::MvQKeyProfileSortFilterModel(QObject* parent) :
    QSortFilterProxyModel(parent),
    filterMode_(StringFilter)
{
}

#if 0
bool MvQKeyProfileSortFilterModel::lessThan ( const QModelIndex &left, const QModelIndex &right) const
{
 	QString leftData = sourceModel()->data(left).toString();
    QString rightData = sourceModel()->data(right).toString();

	//Sort as int
	if(QString::number(leftData.toInt()) == leftData)
	{
		return leftData.toInt() < rightData.toInt();
	}
	else if(QString::number(leftData.toFloat()) == leftData)
	{
		return leftData.toFloat() < rightData.toFloat();
	}
	else
	{
		return QString::localeAwareCompare(leftData, rightData) < 0;	
	}
}

#endif

bool MvQKeyProfileSortFilterModel::filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const
{
    if (filterMode_ == StringFilter) {
        if (filterStr_.isEmpty() || filterStr_[0].simplified().isEmpty())
            return true;

        if (source_parent.isValid())
            return true;

        if (source_column == 0)
            return true;

        QString h = sourceModel()->headerData(source_column, Qt::Horizontal).toString();
        foreach (QString s, filterStr_) {
            if (h.contains(s, Qt::CaseInsensitive))
                return true;
        }
    }
    else {
        if (filterPos_.isEmpty())
            return false;

        if (source_parent.isValid())
            return true;

        return filterPos_.contains(source_column);
    }

    return false;
}

void MvQKeyProfileSortFilterModel::setColumnFilter(QStringList lst)
{
    filterMode_ = StringFilter;
    filterStr_  = lst;
    filterPos_.clear();
    invalidate();
}

void MvQKeyProfileSortFilterModel::setColumnFilter(QList<int> lst)
{
    filterMode_ = PositionFilter;
    filterStr_.clear();
    filterPos_ = lst;
    invalidate();
}


MvQKeyProfileModel::MvQKeyProfileModel(QObject* parent) :
    QAbstractItemModel(parent),
    profile_(nullptr),
    shadeFirstColumn_(false)
{
    possibleStringKeys_ << "shortName"
                        << "name"
                        << "nameECMWF"
                        << "levelType"
                        << "typeOfLevel"
                        << "units"
                        << "centre"
                        << "dataType"
                        << "gridType"
                        << "packingType"
                        << "class"
                        << "domain"
                        << "levtype"
                        << "stream"
                        << "type"
                        << "stepType"
                        << "stepUnits"
                        << "marsClass"
                        << "marsType"
                        << "marsStream"
                        << "pressureUnits"
                        << "parameterName"
                        << "parameterUnits"
                        << "nameECMWF"
                        << "cfName"
                        << "cfVarName"
                        << "ident";
}

MvQKeyProfileModel::~MvQKeyProfileModel()
{
    if (profile_)
        delete profile_;
}

bool MvQKeyProfileModel::isDataSet() const
{
    return (profile_ == nullptr) ? false : true;
}

void MvQKeyProfileModel::keyProfileIsAboutToChange()
{
    beginResetModel();
}

void MvQKeyProfileModel::keyProfileChangeFinished()
{
    endResetModel();
}

void MvQKeyProfileModel::clear()
{
    beginResetModel();
    if (profile_)
        delete profile_;

    profile_ = nullptr;
    endResetModel();
}

void MvQKeyProfileModel::reloadKeyProfile()
{
    MvKeyProfile* ori = profile_;
    beginResetModel();
    profile_ = nullptr;
    endResetModel();

    beginResetModel();
    profile_ = ori;
    endResetModel();
}

void MvQKeyProfileModel::setKeyProfile(MvKeyProfile* prof)
{
    beginResetModel();

    if (profile_ != prof) {
        if (profile_)
            delete profile_;
    }

    profile_ = prof;

    messageFilterStatus_.clear();
    if (profile_) {
        int n = profile_->valueNum();
        for (int i = 0; i < n; i++) {
            messageFilterStatus_.push_back(true);
        }
    }

    endResetModel();
}

void MvQKeyProfileModel::setKeyFilter(MvQKeyFilter /*filter*/)
{
#if 0

#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
	beginResetModel();
#endif
	filter_=filter;

	loadKeyFilter();

	//Reset the model (views will be notified)
#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
	endResetModel();
#endif

#endif
}


int MvQKeyProfileModel::columnCount(const QModelIndex& /* parent */) const
{
    if (!isDataSet())
        return 0;

    return profile_->size();
}

int MvQKeyProfileModel::rowCount(const QModelIndex& index) const
{
    if (!isDataSet())
        return 0;

    if (!index.isValid()) {
        return profile_->valueNum();
    }
    else {
        return 0;
    }
}

QVariant MvQKeyProfileModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid()) {
        return QVariant();
    }
    else if (role == Qt::UserRole) {
        if (messageFilterStatus_[index.row()] == true)
            return QString("1");
        else
            return QString("0");
    }
    else if (role == Qt::TextAlignmentRole) {
        if (profile_->at(index.column())->name() == "MV_Value")
            return Qt::AlignRight;

        else
            return QVariant();
    }
    else if (role == Qt::DisplayRole) {
        int row    = index.row();
        int column = index.column();

        if (!isDataSet() || row < 0 || row >= profile_->valueNum() ||
            column < 0 || column >= static_cast<int>(profile_->size())) {
            return QVariant();
        }

        MvKey* key = profile_->at(column);

        if (key->valueType() == MvKey::StringType) {
            if (key->isConstant())
                return QString::fromStdString(key->stringConstantValue());
            else
                return QString::fromStdString(key->stringValue()[row]);
        }
        else if (key->valueType() == MvKey::IntType) {
            if (key->role() == MvKey::RankRole) {
                int kVal = (key->isConstant()) ? key->intConstantValue() : key->intValue()[row];
                if (kVal <= 0)
                    return "N/A";
                else
                    return kVal;
            }
            else {
                int v;
                if (key->isConstant())
                    v = key->intConstantValue();
                else
                    v = key->intValue()[row];

                if (key->isMissingValueDefined()) {
                    if (v == key->intMissingValue())
                        return "missing";
                    else
                        return v;
                }
                else
                    return v;
            }
        }
        else if (key->valueType() == MvKey::LongType) {
            long long v;
            if (key->isConstant())
                v = static_cast<long long>(key->longConstantValue());
            else
                v = static_cast<long long>(key->longValue()[row]);

            if (key->isMissingValueDefined()) {
                if (v == key->longMissingValue())
                    return "missing";
                else
                    return v;
            }
            else
                return v;
        }
        else if (key->valueType() == MvKey::DoubleType) {
            double val = 0.;
            if (key->isConstant())
                val = key->doubleConstantValue();
            else
                val = key->doubleValue()[row];

            if (key->isMissingValueDefined()) {
                if (val == key->doubleMissingValue())
                    return "missing";
            }

            if (key->valuePrecision() > 0 &&
                qAbs(val) < 100000 && qAbs(val) > 0.0001) {
                return QString::number(val, 'f', key->valuePrecision());
            }
            return val;
        }
    }

    else if (role == Qt::BackgroundRole) {
        return QVariant();
    }

    else if (role == SubTextRole) {
        return (shadeFirstColumn_ && index.column() == 0);
    }

    else if (role == SortRole) {
        int row    = index.row();
        int column = index.column();

        if (!isDataSet() || row < 0 || row >= profile_->valueNum() ||
            column < 0 || column >= static_cast<int>(profile_->size())) {
            return QVariant();
        }

        MvKey* key = profile_->at(column);

        if (key->valueType() == MvKey::StringType) {
            // The majority of the key profile data is stored as string but only
            // a small fraction of this is actually string, most of them are a number!
            // Unfortunately here we do not know the real type we can just try to
            // guess it from the name!!!
            if (key->name() == "MV_Index" || key->name() == "MV_Frame") {
                if (key->isConstant())
                    return QString::fromStdString(key->stringConstantValue()).toInt();
                else
                    return QString::fromStdString(key->stringValue()[row]).toInt();
            }
            else if (!isPossibleStringKey(key->name())) {
                if (key->isConstant())
                    return QString::fromStdString(key->stringConstantValue()).toDouble();
                else
                    return QString::fromStdString(key->stringValue()[row]).toDouble();
            }

            if (key->isConstant())
                return QString::fromStdString(key->stringConstantValue());
            else
                return QString::fromStdString(key->stringValue()[row]);
        }
        else if (key->valueType() == MvKey::IntType) {
            if (key->isConstant())
                return key->intConstantValue();
            else
                return key->intValue()[row];
        }
        else if (key->valueType() == MvKey::LongType) {
            if (key->isConstant())
                return static_cast<long long>(key->longConstantValue());
            else
                return static_cast<long long>(key->longValue()[row]);
        }
        else if (key->valueType() == MvKey::DoubleType) {
            if (key->isConstant())
                return key->doubleConstantValue();
            else
                return key->doubleValue()[row];
        }

        return QVariant();
    }

    else if (role == ErrorRole) {
        return profile_->isErrorRow(index.row());
    }

    return QVariant();
}


QVariant MvQKeyProfileModel::headerData(const int section, const Qt::Orientation orient, const int role) const
{
    if (!isDataSet() || orient != Qt::Horizontal ||
        (role != Qt::DisplayRole && role != Qt::ToolTipRole && role != Qt::DecorationRole) ||
        section < 0 || section >= static_cast<int>(profile_->size())) {
        return QAbstractItemModel::headerData(section, orient, role);
    }

    if (section < 0 || section >= static_cast<int>(profile_->size())) {
        return QVariant();
    }

    if (role == Qt::DisplayRole) {
        if (profile_->at(section)->name() == "MV_Value") {
        }
        if (!profile_->at(section)->shortName().empty())
            return QString(profile_->at(section)->shortName().c_str());
        else
            return QString(profile_->at(section)->name().c_str());
    }
    else if (role == Qt::ToolTipRole) {
        QString desc = QString::fromStdString(profile_->at(section)->description());

        if (profile_->at(section)->name() == "MV_Value") {
            std::string xv = profile_->at(section)->metaData("x_coord");
            std::string yv = profile_->at(section)->metaData("y_coord");

            QString s("key: ");
            s += QString(profile_->at(section)->name().c_str()) + "\n";
            s += desc + "\n";
            s += "x: " + QString::fromStdString(xv) + "\n" +
                 "y: " + QString::fromStdString(yv);

            return s;
        }

        if (profile_->at(section)->keyType() == MvKey::NormalKey) {
            return desc;
        }
        else if (profile_->at(section)->keyType() == MvKey::EccodesKey) {
            if (desc.isEmpty()) {
                return "ecCodes key: \n" + QString::fromStdString(profile_->at(section)->name());
            }

            //BUFR
            QString sec(profile_->at(section)->metaData("section").c_str());
            if (!sec.isEmpty()) {
                QString centre(profile_->at(section)->metaData("centre").c_str());
                QString s = desc + "\n(Section: " + sec;
                if (!centre.isEmpty()) {
                    s += " Centre: " + centre;
                }
                s += ")";
                return s;
            }
            else {
                return desc;
            }
        }

        return desc;
#if 0
		if(profile_->at(section)->name().find("MV_") !=std::string::npos)
		{	
			//QString str="MV key: "  + QString(profile_->at(section)->name().c_str()) +  "\n";
			//str += desc;
			return desc;
		}

		//GRIB
		if(desc.isEmpty())
		{
            QString str("ecCodes key: \n");
			str.append(profile_->at(section)->name().c_str());
			return str;
		}
		
		//BUFR
		QString sec(profile_->at(section)->metaData("section").c_str());
		if(!sec.isEmpty())
		{
			QString centre(profile_->at(section)->metaData("centre").c_str());
			QString s=desc +  "\n(Section: " + sec;
			if(!centre.isEmpty())
			{
				s+=" Centre: " + centre;  
			}
			s+=")";
			return s;
		}
		else
		{
			return desc;
		}
#endif
    }

#if 0
    else if(role == Qt::DecorationRole)
	{
		QString str(profile_->at(section)->name().c_str());
		if(filter_.contains(str))
		{
			return QIcon(QString::fromUtf8(":/examiner/keyFilter"));
		}
		else 
			return QVariant();
	}
#endif

    return QVariant();
}

QString MvQKeyProfileModel::label(const int row, const int column) const
{
    if (!isDataSet() || row < 0 || row >= profile_->valueNum() ||
        column < 0 || column >= static_cast<int>(profile_->size())) {
        return QString();
    }

    MvKey* key = profile_->at(column);
    Q_ASSERT(key);

    if (key->valueType() == MvKey::StringType) {
        if (key->isConstant())
            return QString::fromStdString(key->stringConstantValue());
        else
            return QString::fromStdString(key->stringValue()[row]);
    }
    else if (key->valueType() == MvKey::IntType) {
        if (key->isConstant())
            return QString::number(key->intConstantValue());
        else
            return QString::number(key->intValue()[row]);
    }
    else if (key->valueType() == MvKey::DoubleType) {
        if (key->isConstant())
            return QString::number(key->doubleConstantValue());
        else
            return QString::number(key->doubleValue()[row]);
    }

    return QString();
}

QModelIndex MvQKeyProfileModel::index(int row, int column, const QModelIndex& /*parent*/) const
{
    if (!isDataSet()) {
        return QModelIndex();
    }

    return createIndex(row, column, (void*)0);
}


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


bool MvQKeyProfileModel::isPossibleStringKey(const std::string& name) const
{
    if (name.find(".") == std::string::npos) {
        return possibleStringKeys_.contains(QString::fromStdString(name));
    }

    QStringList lst = QString::fromStdString(name).split(".");
    if (lst.count() == 2) {
        return possibleStringKeys_.contains(lst[1]);
    }

    return false;
}


void MvQKeyProfileModel::loadKeyFilter()
{
#if 0
    int num=profile_->valueNum(0);

	messageFilterStatus_.clear();

	for(unsigned int i=0; i <num; i++)
	{
		messageFilterStatus_.push_back(true);

		QMapIterator<QString, QStringList> it(filter_);
 		while (it.hasNext()) 
		{
            it.next();
			MvKey *key=profile_->key(it.key().toStdString());
			if(key)
			{
				if(it.value().contains(QString(key->value().at(i).c_str())) == false)
				{
					messageFilterStatus_[i]=false;
					break;
				}
			}
		}
	}
#endif
}

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

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

    return Qt::ItemIsDropEnabled | defaultFlags;
}

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

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

QMimeData* MvQKeyProfileModel::mimeData(const QModelIndexList& indexes) const
{
    return QAbstractItemModel::mimeData(indexes);
}

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

    if (action == Qt::IgnoreAction)
        return true;

    if (!data->hasFormat("metview/mvkey"))
        return false;

    const auto* keyData = qobject_cast<const MvQKeyMimeData*>(data);

    if (!keyData || keyData->keys().isEmpty())
        return false;

    emit keysInserted(keyData->keys().values(), parent.column());


    //Dnd from a key model --> insert new column
    if (keyData->model() != this) {
#if 0
        QMapIterator<int,MvKey*> it(keyData->keys());
        while(it.hasNext())
        {
            it.next();
            MvKey *key=it.value()->clone();
        }

        //qDebug() << "other";
		beginResetModel();
		QMapIterator<int,MvKey*> it(keyData->keys());
		while(it.hasNext()) 
		{
            it.next();
			MvKey *key=it.value()->clone();	
			profile_->addKey(key);
			if(parent.isValid() && parent.column() >= 0 && parent.column() < profile_->size()-1)
			{
			  	profile_->reposition(profile_->size()-1,parent.column()+1);				
			}	
		}

		emit keyInserted();

        endResetModel();
#endif
    }

    return true;
}
