/******************************************************************************
*
* 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 "Job.h"

#include "Debug.h"
#include "File.h"

#include <QFile>
#include <unistd.h>

//____________________________________________________________
Job::Job( long pid, long ppid ):
    Counter( "Job" ),
    mask_( 0 ),
    id_( 0 ),
    parentId_( 0 ),
    treeParentId_( 0 ),
    userId_( 0 ),
    cpu_( 0 ),
    memory_( 0 ),
    priority_( 0 ),
    nice_( 0 ),
    virtualMemory_(0),
    residentMemory_(0),
    sharedMemory_(0),
    userTime_(0),
    systemTime_(0),
    elapsedTime_(0),
    threads_(0),
    iconInitialized_(false)
{
    if( pid ) setId( pid );
    if( ppid ) setParentId( ppid );
}

//____________________________________________________________
Job::Job( void ):
    Counter( "Job" ),
    mask_( 0 ),
    id_( 0 ),
    parentId_( 0 ),
    cpu_( 0 ),
    memory_( 0 ),
    priority_( 0 ),
    nice_( 0 ),
    virtualMemory_(0),
    residentMemory_(0),
    sharedMemory_(0),
    userTime_(0),
    systemTime_(0),
    elapsedTime_(0),
    threads_(0),
    iconInitialized_(false)
{}

//____________________________________________________________
void Job::updateFrom( const Job& other )
{

    // check id
    Q_ASSERT( id_ == other.id_ );

    // update based on mask
    if( other.mask_ & ParentId ) setParentId( other.parentId_ );
    if( other.mask_ & TreeParentId ) setParentId( other.treeParentId_ );
    if( other.mask_ & Name ) setName( other.name_ );
    if( other.mask_ & LongName ) setLongName( other.longName_ );
    if( other.mask_ & Command ) setCommand( other.command_ );
    if( other.mask_ & User ) setUser( other.user_ );
    if( other.mask_ & UserId ) setUserId( other.userId_ );
    if( other.mask_ & State ) setState( other.state_ );
    if( other.mask_ & Cpu ) setCpu( other.cpu_ );
    if( other.mask_ & Memory ) setMemory( other.memory_ );
    if( other.mask_ & Priority ) setPriority( other.priority_ );
    if( other.mask_ & Nice ) setNice( other.nice_ );

    if( other.mask_ & VirtualMemory ) setVirtualMemory( other.virtualMemory_ );
    if( other.mask_ & ResidentMemory ) setResidentMemory( other.residentMemory_ );
    if( other.mask_ & SharedMemory ) setSharedMemory( other.sharedMemory_ );

    // update cpu
    if( (other.mask_ & UserTime) && (other.mask_ & SystemTime) && (other.mask_ & ElapsedTime) && other.elapsedTime_ > elapsedTime_ )
    {

        setCpu( 100*(other.cpuTime() - cpuTime() )/(other.elapsedTime_ - elapsedTime_ ) );

    } else if( other.mask_ & Cpu ) setCpu( other.cpu_ );

    if( other.mask_ & UserTime ) setUserTime( other.userTime_ );
    if( other.mask_ & SystemTime ) setSystemTime( other.systemTime_ );
    if( other.mask_ & ElapsedTime ) setElapsedTime( other.elapsedTime_ );

    if( other.mask_ & StartTime ) setStartTime( other.startTime_ );
    if( other.mask_ & Threads ) setThreads( other.threads_ );
    if( other.mask_ & Icon ) setIcon( other.icon_ );

}

//____________________________________________________________
void Job::readFromCommandLine( void )
{

    // check if not already set
    if( !longName_.isEmpty() ) return;

    QFile file( QString( "/proc/%1/cmdline" ).arg( id_ ) );
    if( !file.open(QIODevice::ReadOnly) )
    {
        setLongName( name_ );
        return;
    }

    QTextStream in(&file);
    QString commandLine( in.readAll() );

    //cmdline separates parameters with the NULL character
    if( !commandLine.isEmpty() )
    {

        //extract non-truncated name from cmdline
        const int zeroIndex = commandLine.indexOf(QChar('\0'));
        int processNameStart = commandLine.lastIndexOf(QChar('/'), zeroIndex);
        if(processNameStart == -1) processNameStart = 0;
        else processNameStart++;

        const QString nameFromCmdLine = commandLine.mid(processNameStart, zeroIndex - processNameStart);
        if( nameFromCmdLine.startsWith( name_ ) ) setLongName( nameFromCmdLine );
        else setLongName( name_ );

        // assign command line
        commandLine.replace('\0', ' ');
        setCommand( commandLine );

    } else setLongName( name_ );

    file.close();
    return;

}

//____________________________________________________________
bool Job::readFromStatus( void )
{

    QFile file( QString( "/proc/%1/status" ).arg( id_ ) );
    if( !file.open(QIODevice::ReadOnly) ) return false;

    // QTextStream in(&file);
    QString buffer( file.readAll() );
    QTextStream in( &buffer );
    while( !in.atEnd() )
    {
        // read one line and split
        const QString line( in.readLine() );
        if( line.startsWith( "Uid:" ) )
        {

            // parent id
            const QStringList values( line.split( QRegExp( "\\s+" ), QString::SkipEmptyParts ) );
            if( values.size() >= 2 ) setUserId( values[1].toLongLong() );

            // stop here
            break;

        }

    }

    return true;

}

//____________________________________________________________
bool Job::readFromStat( void )
{

    QFile file( QString( "/proc/%1/stat" ).arg( id_ ) );
    if( !file.open(QIODevice::ReadOnly) ) return false;

    QTextStream in(&file);
    const QString data( in.readAll() );
    const QStringList dataList( data.split( " ", QString::SkipEmptyParts ) );
    if( dataList.size() < 24 ) return false;

    // check pid
    if( dataList[0].toLong() != id_ ) return false;

    // name
    setName( dataList[1].mid(1, dataList[1].size()-2) );
    setState( dataList[2].at(0) );
    setParentId( dataList[3].toLong() );
    setUserTime( 10*dataList[13].toLongLong() );
    setSystemTime( 10*dataList[14].toLongLong() );
    setPriority( dataList[17].toInt() );
    setNice( dataList[18].toInt() );
    setThreads( dataList[19].toInt() );
    return true;

}

//____________________________________________________________
bool Job::readFromStatM( void )
{

    QFile file( QString( "/proc/%1/statm" ).arg( id_ ) );
    if( !file.open(QIODevice::ReadOnly) ) return false;

    QTextStream in(&file);
    const QString data( in.readAll() );
    const QStringList dataList( data.split( " ", QString::SkipEmptyParts ) );
    if( dataList.size() < 3 ) return false;

    setVirtualMemory( dataList[0].toLongLong() * sysconf(_SC_PAGESIZE) / 1024 );
    setResidentMemory( dataList[1].toLongLong() * sysconf(_SC_PAGESIZE) / 1024 );
    setSharedMemory( dataList[2].toLongLong() * sysconf(_SC_PAGESIZE) / 1024 );
    return true;

}

//____________________________________________________________
bool Job::readStartTime( void )
{
    const File file( QString( "/proc/%1" ).arg( id_ ) );
    if( !file.exists() ) return false;
    setStartTime( TimeStamp( file.created() ) );
    return true;
}

//____________________________________________________________
QString Job::_timeString( int time )
{
    const int hours( time/(3600*1000) );
    const int minutes( time/(60*1000) - hours*60 );
    const int seconds( (time/1000) % 60 );
    int msec( time % 1000 );

    QString timeString;
    if( minutes > 0 || hours > 0 )
    {

        if( hours > 0 ) timeString = QString( QObject::tr( "%1h " ) ).arg( hours );
        if( minutes > 0 ) timeString += QString( QObject::tr( "%1m " ) ).arg( minutes );
        if( seconds > 0 ) timeString += QString( QObject::tr( "%1s" ) ).arg( seconds );

    } else if( msec > 0 ) {

        msec /= 10;
        if( msec > 9 ) timeString = QString( QObject::tr( "%1.%2s" ) ).arg( seconds ).arg( msec );
        else timeString = QString( QObject::tr( "%1.0%2s" ) ).arg( seconds ).arg( msec );

    } else timeString = QString( QObject::tr( "%1s" ) ).arg( seconds );
    return timeString;

}
