/***************************** 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 {};
    }

    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 {};
}

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 {};
    }

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

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

    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 {};
    }
    return {};
}

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 {};

        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 {};
}


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 {};
    }

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

    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 {};
    }
    return {};
}

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 {};

        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 {};

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

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

    return {};
}

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 {};
}

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;
}
