/******************************************************************************
*
* Copyright (C) 2002 Hugo PEREIRA <mailto: hugo.pereira@free.fr>
*
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT
* Any WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program.  If not, see <http://www.gnu.org/licenses/>.
*
*******************************************************************************/

#include "JobModel.h"

#include "Color.h"
#include "CppUtil.h"
#include "CustomPixmap.h"
#include "Singleton.h"
#include "XmlOptions.h"

#include <QApplication>
#include <QFont>
#include <QPalette>

//_______________________________________________________________
JobModel::JobModel( QObject* parent ):
    TreeModel<Job>( parent ),
    Counter( "JobModel" ),
    today_( TimeStamp::now() )
{
    Debug::Throw( "JobModel::JobModel.\n" );
    connect( Base::Singleton::get().application(), SIGNAL(configurationChanged()), SLOT(_updateConfiguration()) );
    _updateConfiguration();
}

//__________________________________________________________________
QVariant JobModel::data( const QModelIndex& index, int role ) const
{

    if( !index.isValid() ) return QVariant();

    switch( role )
    {
        case Qt::DisplayRole:
        {

            const Job& job( _find( index.internalId() ).get() );
            switch( index.column() )
            {

                case Name:
                {
                    const QString name( job.longName().isEmpty() ? job.name():job.longName() );
                    return name.left( name.indexOf( ' ' ) );
                }

                case User: return job.user();
                case Id: return QString::number(abs(job.id()));

                case ParentId:
                {
                    if( job.parentId() > 0 ) return QString::number(job.parentId());
                    else return QVariant();
                }

                case Cpu:
                {
                    if( job.isPaused() ) return "stopped";
                    else {
                        const int value = job.cpu();
                        if( value > 0 ) return QString( "%1 %" ).arg(value);
                        else return QVariant();
                    }
                }

                case Memory:
                {
                    const qreal value = job.memory();
                    if( value > 0 ) return QString( "%1 %").arg( QString::number( value, 'f', 1 ) );
                    else return QVariant();
                }

                case CpuTime:
                return ( job.cpuTime()>0 ) ? job.cpuTimeString():QString();

                case Priority:
                return job.priority();

                case Nice:
                return job.nice();

                case VirtualMemory:
                {
                    const qint64 value( job.virtualMemory() );
                    return value > 0 ? File::rawSizeString( value ) + " K" : QString();
                }

                case ResidentMemory:
                {
                    const qint64 value( job.residentMemory() );
                    return value > 0 ? File::rawSizeString( value ) + " K" : QString();
                }

                case SharedMemory:
                {
                    const qint64 value( job.sharedMemory() );
                    return value > 0 ? File::rawSizeString( value ) + " K" : QString();
                }

                case Threads: return job.threads();
                case StartTime:
                if( job.startTime().isSameDay( today_ ) ) return job.startTime().toString( TimeStamp::Format::Time );
                else if( job.startTime().isSameYear( today_ ) ) return job.startTime().toString( "MMM d hh:mm" );
                else return job.startTime().toString( "MMM d yyy hh:mm" );

                case Command: return job.command();

                default: return QVariant();

            }

            break;

        }

        case Qt::FontRole:
        {
            if( index.column() == Cpu )
            {

                const Job& job( _find( index.internalId() ).get() );
                if( job.isPaused() )
                {
                    QFont font( QApplication::font() );
                    font.setItalic( true );
                    return font;
                }
            }

            break;

        }

        case Qt::ForegroundRole:
        {
            const Job& job( _find( index.internalId() ).get() );
            if( job.id() < 0 ) return QPalette().color( QPalette::Disabled, QPalette::Text );
            else return QVariant();

        }

        case Qt::TextAlignmentRole:
        {

            switch( index.column() )
            {
                case User:
                case Memory:
                case Cpu:
                case Threads:
                case StartTime:
                return static_cast<int>(Qt::AlignCenter);

                case Id:
                case ParentId:
                case CpuTime:
                case VirtualMemory:
                case ResidentMemory:
                case SharedMemory:
                return static_cast<int>(Qt::AlignRight|Qt::AlignVCenter);

                default: break;
            }

            break;

        }

        case Qt::DecorationRole:
        {
            if( showIcons_ && index.column() == Name )
            {

                const Job& job( _find( index.internalId() ).get() );
                if( !job.icon().isNull() ) return job.icon();
                else return emptyIcon_;

            }
            break;

        }

        default: break;

    }

    return QVariant();

}

//__________________________________________________________________
QVariant JobModel::headerData(int section, Qt::Orientation orientation, int role) const
{

    switch( role )
    {
        case Qt::DisplayRole:
        {
            switch( section )
            {
                case Name: return tr( "Name" );
                case User: return tr( "User" );
                case Id: return tr( "Process Id " );
                case ParentId: return tr( "Parent's Id " );
                case Cpu: return tr( "CPU %" );
                case Memory: return tr( "Memory %" );
                case CpuTime: return tr( "CPU Time " );
                case Priority: return tr( "Priority" );
                case Nice: return tr( "Nice" );
                case VirtualMemory: return tr( "Virtual Memory " );
                case ResidentMemory: return tr( "Resident Memory " );
                case SharedMemory: return tr( "Shared Memory " );
                case Threads: return tr( "Threads" );
                case StartTime: return tr( "Started" );
                case Command: return tr( "Command" );
                default: return QVariant();

            }
            break;
        }

        case Qt::TextAlignmentRole:
        {

            switch( section )
            {
                case User:
                case Memory:
                case Cpu:
                case Threads:
                case StartTime:
                return static_cast<int>(Qt::AlignCenter);

                case Id:
                case ParentId:
                case CpuTime:
                case VirtualMemory:
                case ResidentMemory:
                case SharedMemory:
                return static_cast<int>(Qt::AlignRight|Qt::AlignVCenter);

                default: return QVariant();
            }
            break;
        }

        default: return QVariant();
    }

    // return empty
    return QVariant();

}

//__________________________________________________________
void JobModel::_updateConfiguration()
{

    Debug::Throw( "JobModel::_updateConfiguration.\n" );
    showIcons_ = XmlOptions::get().get<bool>( "SHOW_ICONS" );

    // empty icon
    const int iconSize( XmlOptions::get().get<int>( "LIST_ICON_SIZE" ) );
    iconSize_ = QSize( iconSize, iconSize );

    // create empty pixmap
    emptyIcon_ = CustomPixmap( iconSize_, CustomPixmap::Flag::Transparent );

}

//___________________________________________________________
bool JobModel::SortFTor::operator() ( Job first, Job second ) const
{

    if( order_ == Qt::DescendingOrder ) std::swap( first, second );

    switch( type_ )
    {

        case Name:
        {
            const QString firstName( first.longName().isEmpty() ? first.name():first.longName() );
            const QString secondName( second.longName().isEmpty() ? second.name():second.longName() );
            if( firstName != secondName ) return firstName < secondName;
            break;
        }

        case User:
        if( first.user() != second.user() ) return first.user() < second.user();
        break;

        case Id:
        return first.id() < second.id();
        break;

        case ParentId:
        if( first.parentId() != second.parentId() ) return first.parentId() < second.parentId();
        break;

        case Cpu:
        if( first.cpu() != second.cpu() ) return first.cpu() < second.cpu();
        break;

        case Memory:
        if( first.memory() != second.memory() ) return first.memory() < second.memory();
        break;

        case CpuTime:
        if( first.cpuTime() != second.cpuTime() ) return first.cpuTime() < second.cpuTime();
        break;

        case Priority:
        if( first.priority() != second.priority() ) return first.priority() < second.priority();
        break;

        case Nice:
        if( first.nice() != second.nice() ) return first.nice() < second.nice();
        break;

        case VirtualMemory:
        if( first.virtualMemory() != second.virtualMemory() ) return first.virtualMemory() < second.virtualMemory();
        break;

        case ResidentMemory:
        if( first.residentMemory() != second.residentMemory() ) return first.residentMemory() < second.residentMemory();
        break;

        case SharedMemory:
        if( first.sharedMemory() != second.sharedMemory() ) return first.sharedMemory() < second.sharedMemory();
        break;

        case Threads:
        if( first.threads() != second.threads() ) return first.threads() < second.threads();
        break;

        case StartTime:
        if( first.startTime() != second.startTime() ) return first.startTime() < second.startTime();
        break;

        case Command:
        if( first.command() != second.command() ) return first.command() < second.command();
        break;

        default:
        break;

    }

    return first.id() < second.id();

}
