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

#include <QColor>
#include <QDebug>
#include <QPainter>
#include <QPixmap>

#include "MvQScmModel.h"

#include "MvScm.h"
#include "MvQTheme.h"


MvQScmTableViewDelegate::MvQScmTableViewDelegate(QObject* parent) :
    QStyledItemDelegate(parent)
{
}

void MvQScmTableViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
                                    const QModelIndex& index) const
{
    //Paint the rest
    QStyledItemDelegate::paint(painter, option, index);

    painter->save();
    QVariant va = index.data(Qt::UserRole);
    if (va.isValid()) {
        painter->setPen(QPen(va.value<QColor>(), 2));
        painter->drawRect(option.rect.adjusted(2, 2, -2, -2));
    }
    painter->restore();
}

//=========================================
//
// MvQScmDataModel
//
//=========================================

MvQScmDataModel::MvQScmDataModel(QObject* parent) :
    QAbstractItemModel(parent)
{
    step_    = -1;
    editCol_ = MvQTheme::colour("scmview", "edited_cell");
}

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

void MvQScmDataModel::setStep(int step)
{
    beginResetModel();

    if (step >= 0 && data_.size() > 0 && step < data_.at(0)->stepNum())
        step_ = step;
    else
        step_ = -1;

    endResetModel();
}


QModelIndex MvQScmDataModel::index(int row, int column, const QModelIndex& /*parent*/) const
{
    if (data_.size() == 0 || column < 0) {
        return QModelIndex();
    }

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

QModelIndex MvQScmDataModel::indexForChange(const MvScmProfileChange& item) const
{
    return indexForVar(item.var(), item.step(), item.level());
}

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

void MvQScmDataModel::reload()
{
    //reset();
    beginResetModel();
    endResetModel();
}


//=========================================
//
// MvQScmSurfaceModel
//
//=========================================

MvQScmSurfaceModel::MvQScmSurfaceModel(QObject* parent) :
    MvQScmDataModel(parent)
{
}

int MvQScmSurfaceModel::columnCount(const QModelIndex& /* parent */) const
{
    return 2;
}

int MvQScmSurfaceModel::rowCount(const QModelIndex& parent) const
{
    //Non-root
    if (parent.isValid()) {
        return 0;
    }
    //Root
    return static_cast<int>(data_.size());
}

QVariant MvQScmSurfaceModel::data(const QModelIndex& index, int role) const
{
    if (role != Qt::DisplayRole && role != Qt::BackgroundRole && role != Qt::EditRole &&
        role != Qt::UserRole) {
        return QVariant();
    }

    if (index.column() != 0 && index.column() != 1)
        return QVariant();

    if (step_ == -1 || !index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(data_.size())) {
        return QVariant();
    }

    if (role == Qt::DisplayRole || role == Qt::EditRole) {
        if (index.column() == 0)
            return data_.at(index.row())->data(step_).at(0);
        else
            return QString::fromStdString(data_.at(index.row())->units());
    }
    else if (role == Qt::UserRole) {
        bool editable = (std::find(editableVars_.begin(), editableVars_.end(), data_.at(index.column())) != editableVars_.end()) ? true : false;

        if (editable) {
            if (data_.at(index.row())->changed(step_, 0)) {
                return editCol_;
            }
        }
        return QVariant();
    }
    return QVariant();
}

bool MvQScmSurfaceModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (!index.isValid() || role != Qt::EditRole || index.column() != 0)
        return false;

    float f = value.toFloat();
    data_.at(index.row())->fitToRange(f);

    MvScmProfileChange changeItem;

    data_.at(index.row())->setValue(step_, index.column(), f, changeItem);
    emit dataChanged(index, index);
    emit dataEdited(changeItem);
    return true;
}

void MvQScmSurfaceModel::setData(const std::vector<MvScmVar*>& data, int step)
{
    beginResetModel();

    data_ = data;

    if (step >= 0 && data_.size() > 0 && step < data_.at(0)->stepNum())
        step_ = step;
    else
        step_ = -1;

    endResetModel();
}

QVariant MvQScmSurfaceModel::headerData(const int section, const Qt::Orientation orient, const int role) const
{
    if (role != Qt::DisplayRole && role != Qt::ToolTipRole)
        return QAbstractItemModel::headerData(section, orient, role);

    if (orient == Qt::Vertical) {
        if (section < 0 || section >= static_cast<int>(data_.size()))
            return QVariant();

        QString str = QString::fromStdString(data_.at(section)->name());
        //+
        //          "  [" + QString::fromStdString(data_.at(section)->units()) + "]" ;

        if (role == Qt::DisplayRole)
            return str;
        else if (role == Qt::ToolTipRole)
            return QString::fromStdString(data_.at(section)->longName());
    }
    else if (orient == Qt::Horizontal) {
        if (section == 0)
            return tr("Value");
        else if (section == 1)
            return tr("Units");
    }

    return QString();
}

QModelIndex MvQScmSurfaceModel::indexForVar(MvScmVar* var, int step, int /*level*/) const
{
    if (step == step_) {
        for (unsigned int i = 0; i < data_.size(); i++) {
            if (data_.at(i) == var) {
                return createIndex(i, 0, (void*)nullptr);
            }
        }
    }

    return QModelIndex();
}


Qt::ItemFlags MvQScmSurfaceModel::flags(const QModelIndex& index) const
{
    Qt::ItemFlags defaultFlags;

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

    if (index.column() == 0 && index.row() >= 0 && index.row() < static_cast<int>(data_.size()) &&
        std::find(editableVars_.begin(), editableVars_.end(), data_.at(index.row())) != editableVars_.end()) {
        return defaultFlags | Qt::ItemIsEditable;
    }

    return defaultFlags;
}

//=========================================
//
// MvQScmProfileModel
//
//=========================================

MvQScmProfileModel::MvQScmProfileModel(QObject* parent) :
    MvQScmDataModel(parent)
{
}

int MvQScmProfileModel::columnCount(const QModelIndex& /* parent */) const
{
    return data_.size();
}

int MvQScmProfileModel::rowCount(const QModelIndex& parent) const
{
    //Non-root
    if (parent.isValid()) {
        return 0;
    }
    //Root
    if (data_.size() > 0) {
        return static_cast<int>(data_.at(0)->levelNum());
    }
    else
        return 0;
}

QVariant MvQScmProfileModel::data(const QModelIndex& index, int role) const
{
    if (role != Qt::DisplayRole && role != Qt::BackgroundRole && role != Qt::EditRole &&
        role != Qt::UserRole) {
        return QVariant();
    }

    if (step_ == -1 || !index.isValid() || index.column() < 0 || index.column() >= static_cast<int>(data_.size())) {
        return QVariant();
    }

    if (role == Qt::DisplayRole || role == Qt::EditRole) {
        return data_.at(index.column())->data(step_).at(index.row());
    }

    else if (role == Qt::UserRole) {
        bool editable = (std::find(editableVars_.begin(), editableVars_.end(), data_.at(index.column())) != editableVars_.end()) ? true : false;

        if (editable) {
            if (data_.at(index.column())->changed(step_, index.row())) {
                return editCol_;
            }
        }
        return QVariant();
    }
    return QVariant();
}

bool MvQScmProfileModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (!index.isValid() || role != Qt::EditRole)
        return false;

    float f = value.toFloat();
    data_.at(index.column())->fitToRange(f);

    MvScmProfileChange changeItem;

    data_.at(index.column())->setValue(step_, index.row(), f, changeItem);
    emit dataChanged(index, index);
    emit dataEdited(changeItem);
    return true;
}

void MvQScmProfileModel::setData(const std::vector<MvScmVar*>& data, int step, const std::vector<float>& levels, const std::string& levelName)
{
    beginResetModel();

    data_      = data;
    levels_    = levels;
    levelName_ = QString::fromStdString(levelName);

    if (step >= 0 && data_.size() > 0 && step < data_.at(0)->stepNum())
        step_ = step;
    else
        step_ = -1;

    endResetModel();
}

QVariant MvQScmProfileModel::headerData(const int section, const Qt::Orientation orient, const int role) const
{
    if (role != Qt::DisplayRole && role != Qt::ToolTipRole)
        return QAbstractItemModel::headerData(section, orient, role);

    if (orient == Qt::Horizontal) {
        if (section < 0 || section >= static_cast<int>(data_.size()))
            return QVariant();

        QString str = QString::fromStdString(data_.at(section)->name()) + "\n" +
                      "[" + QString::fromStdString(data_.at(section)->units()) + "]";

        //bool editable=(std::find(editableVars_.begin(),editableVars_.end(),data_.at(section)) != editableVars_.end())?true:false;

        if (role == Qt::DisplayRole)
            return str;
        else if (role == Qt::ToolTipRole)
            return QString::fromStdString(data_.at(section)->longName());
        //else if(role == Qt::BackgroundRole && editable)
        //	return QColor(Qt::red);
        //else if(role == Qt::DecorationRole && editable)
        //	return QPixmap(QString::fromUtf8(":/scmEditor/edit.svg"));
    }
    else if (orient == Qt::Vertical) {
        if (section < 0 || section >= static_cast<int>(levels_.size()))
            return QVariant();

        QString str = QString::number(levels_.at(section));

        if (role == Qt::DisplayRole)
            return str;
        else if (role == Qt::ToolTipRole)
            return levelName_ + " = " + str;
    }

    return QVariant();
}

QModelIndex MvQScmProfileModel::indexForVar(MvScmVar* var, int step, int level) const
{
    if (step == step_) {
        for (unsigned int i = 0; i < data_.size(); i++) {
            if (data_.at(i) == var) {
                return createIndex(level, i, (void*)nullptr);
            }
        }
    }

    return QModelIndex();
}

Qt::ItemFlags MvQScmProfileModel::flags(const QModelIndex& index) const
{
    Qt::ItemFlags defaultFlags;

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

    if (index.column() >= 0 && index.column() < static_cast<int>(data_.size()) &&
        std::find(editableVars_.begin(), editableVars_.end(), data_.at(index.column())) != editableVars_.end()) {
        return defaultFlags | Qt::ItemIsEditable;
    }

    return defaultFlags;
}


MvQScmProfileFilterModel::MvQScmProfileFilterModel(QObject* parent) :
    QSortFilterProxyModel(parent),
    showEditableOnly_(false)
{
}

void MvQScmProfileFilterModel::setShowEditableOnly(bool b)
{
    showEditableOnly_ = b;
    //reset();
    beginResetModel();
    endResetModel();
}

bool MvQScmProfileFilterModel::filterAcceptsColumn(int sourceColumn,
                                                   const QModelIndex&) const
{
    if (showEditableOnly_)
        return sourceModel()->flags(sourceModel()->index(0, sourceColumn)) & Qt::ItemIsEditable;
    else
        return true;
}
