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

 Copyright 2014 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 "MvQProcModel.h"
#include "MvQPixmapCache.h"

#include "MvProcNode.h"


MvQProcModel::MvQProcModel(QObject* parent) :
    QAbstractItemModel(parent),
    rootItem_(nullptr)
{
    pm_ = new MvQPixmapCache(QPixmap());

    cpuYellow_ = QColor(255, 255, 0);
    cpuOrange_ = QColor(255, 188, 46);
    cpuRed_ = QColor(255, 102, 102);
    textGrey_ = QColor(100, 100, 100);
}

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


void MvQProcModel::changeData(MvProcNode* data)
{
    beginResetModel();
    rootItem_ = data;
    rootItem_->setObserver(this);
    endResetModel();
}


void MvQProcModel::updateData()
{
    // beginResetModel();
    endResetModel();
}

int MvQProcModel::columnCount(const QModelIndex& /* parent */) const
{
    return 5;
}

int MvQProcModel::rowCount(const QModelIndex& parent) const
{
    if (!rootItem_)
        return 0;

    MvProcNode* parentItem = itemForIndex(parent);
    return parentItem ? parentItem->childrenCount() : 0;
}


QVariant MvQProcModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid() ||
        (role != Qt::DisplayRole && role != Qt::BackgroundRole &&
         role != Qt::DecorationRole && role != Qt::TextAlignmentRole && role != Qt::ForegroundRole)) {
        return {};
    }

    MvProcNode* item = itemForIndex(index);
    if (!item)
        return {};

    MvProcStat* st = item->stats();
    if (!st && index.column() > 0)
        return {};

    if (role == Qt::DisplayRole) {
        switch (index.column()) {
            case 0:
                return QString::fromStdString(item->niceName());
            case 1:
                return (st->cpu() > 1) ? (QString::number(st->cpu()) + " %") : "< 1%";
            case 2:
                return QString::fromStdString(st->vmRss());
            case 3:
                return QString::fromStdString(st->elapsedTime());
            case 4:
                return "   " + QString::number(item->pid());
            default:
                return QString();
        }
    }
    else if (role == Qt::DecorationRole && index.column() == 0) {
        if (MvProcRequest* req = item->procRequest()) {
            return pm_->pixmap(QString::fromStdString(req->className()));
        }
        else
            return {};
    }
    else if (role == Qt::BackgroundRole && index.column() == 1) {
        int cpu = st->cpu();
        if (cpu > 75)
            return cpuRed_;
        else if (cpu > 40)
            return cpuOrange_;
        else if (cpu > 25)
            return cpuYellow_;
        else
            return {};
    }
    else if (role == Qt::TextAlignmentRole) {
        if (index.column() == 0 || index.column() == 4)
            return {};
        else
            return Qt::AlignRight;
    }
    else if (role == Qt::ForegroundRole) {
        if ((index.column() == 1 && st->cpu() < 1) || index.column() == 4)
            return textGrey_;
    }
    return {};
}


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

    if (role == Qt::DisplayRole) {
        switch (section) {
            case 0:
                return QObject::tr("Name");
            case 1:
                return QObject::tr("CPU %");
            case 2:
                return QObject::tr("Memory");
            case 3:
                return QObject::tr("Time");
            case 4:
                return QObject::tr("PID");
            default:
                return {};
        }
    }
    else if (role == Qt::ToolTipRole) {
        switch (section) {
            case 0:
                return QObject::tr("The process name");
            case 1:
                return QObject::tr("The current <b>CPU usage</b> of the process <br> per processor core core");
            case 2:
                return QObject::tr("The <b>resident set size</b> of memory <br> used by the process");
            case 3:
                return QObject::tr("The running time of the process");
            case 4:
                return QObject::tr("The unique process ID");
            default:
                return {};
        }
    }


    return {};
}

MvProcNode* MvQProcModel::itemForIndex(const QModelIndex& index) const
{
    if (index.isValid()) {
        if (auto* item = static_cast<MvProcNode*>(index.internalPointer())) {
            return item;
        }
    }

    return rootItem_;
}

QModelIndex MvQProcModel::indexForItem(MvProcNode* proc, int column)
{
    if (!proc->parent())
        return {};

    else {
        int row = proc->parent()->indexOfChild(proc);
        return createIndex(row, column, proc);
    }
}

QModelIndex MvQProcModel::index(int row, int column, const QModelIndex& parent) const
{
    if (!rootItem_ || row < 0 || column < 0)
        return {};

    MvProcNode* parentItem = itemForIndex(parent);
    if (MvProcNode* item = parentItem->childAt(row)) {
        QModelIndex index = createIndex(row, column, item);
        return index;
    }

    return {};
}

QModelIndex MvQProcModel::parent(const QModelIndex& index) const
{
    if (!index.isValid())
        return {};

    if (MvProcNode* childItem = itemForIndex(index)) {
        if (MvProcNode* parentItem = childItem->parent()) {
            if (parentItem == rootItem_) {
                return {};
            }
            else if (MvProcNode* grandParentItem = parentItem->parent()) {
                int row = grandParentItem->indexOfChild(parentItem);
                return createIndex(row, 0, parentItem);
            }
        }
    }

    return {};
}

void MvQProcModel::infoChanged(MvProcNode* proc)
{
    if (!proc)
        return;

    emit dataChanged(indexForItem(proc, 1),
                     indexForItem(proc, 3));
}

//=======================================
//
// MvQProcFilterModel:
//
//=======================================

MvQProcFilterModel::MvQProcFilterModel(QObject* parent) :
    QSortFilterProxyModel(parent)
{
}

bool MvQProcFilterModel::lessThan(const QModelIndex& left,
                                  const QModelIndex& right) const
{
    QVariant leftData, rightData;
    if (left.column() == 2) {
        leftData = sourceModel()->data(left, Qt::UserRole);
        rightData = sourceModel()->data(right, Qt::UserRole);
        return leftData.toLongLong() < rightData.toLongLong();
    }
    else
        return QSortFilterProxyModel::lessThan(left, right);
}
