#ifndef Job_h
#define Job_h

/******************************************************************************
*
* 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 "Counter.h"
#include "TimeStamp.h"

#include <QIcon>
#include <QList>
#include <QSet>
#include <QVector>

//* single process read from top command line output
class Job final: private Base::Counter<Job>
{
    public:

    //* list of jobs
    using List = QList<Job>;

    //* set of jobs
    using Set = QSet<Job>;

    //* constructor
    explicit Job();

    //* constructor
    explicit Job( long, long = 0 );

    //* changed flags
    enum Flag
    {
        Id = 1<<0,
        ParentId = 1<<1,
        TreeParentId = 1<<2,
        Name = 1<<3,
        LongName = 1<<4,
        Command = 1<<5,
        User = 1<<6,
        UserId = 1<<7,
        State = 1<<8,
        Cpu = 1<<9,
        Memory = 1<<10,
        Priority = 1<<11,
        Nice = 1<<12,
        VirtualMemory = 1<<13,
        ResidentMemory = 1<<14,
        SharedMemory = 1<<15,
        UserTime = 1<<16,
        SystemTime = 1<<16,
        ElapsedTime = 1<<17,
        Threads = 1<<18,
        StartTime = 1<<19,
        Icon = 1<<20
    };

    Q_DECLARE_FLAGS( Flags, Flag );

    //*@name accessors
    //@{

    //* valid
    bool isValid() const
    { return mask_&Id && mask_&Name;  }

    //* mask
    Flags mask() const
    { return mask_; }

    //* id
    long id() const
    { return id_; }

    //* parent id
    long parentId() const
    { return parentId_; }

    //* returns true if this job is a child of argument
    bool isChild( const Job& other ) const
    { return treeParentId_ == other.id_; }

    //* name
    QString name() const
    { return name_; }

    //* long name
    QString longName() const
    { return longName_; }

    //* command
    QString command() const
    { return command_; }

    //* user
    QString user() const
    { return user_; }

    //* user id
    qint64 userId() const
    { return userId_; }

    //* state
    QChar state() const
    { return state_; }

    //* returns true if job is running
    bool isRunning() const
    { return state_ == 'R'; }

    //* returns true if job is paused
    bool isPaused() const
    { return state_ == 'T'; }

    //* cpu percentage
    qreal cpu() const
    { return cpu_; }

    //* memory percentage
    qreal memory() const
    { return memory_; }

    //* priority
    int priority() const
    { return priority_; }

    //* nice
    int nice() const
    { return nice_; }

    //* virtual memory
    int virtualMemory() const
    { return virtualMemory_; }

    //* resident memory
    int residentMemory() const
    { return residentMemory_; }

    //* shared memory
    int sharedMemory() const
    { return sharedMemory_; }

    //* time
    int userTime() const
    { return userTime_; }

    //* time
    int systemTime() const
    { return systemTime_; }

    //* time
    int cpuTime() const
    { return userTime_ + systemTime_; }

    //* time
    QString userTimeString() const
    { return _timeString( userTime_ ); }

    //* time
    QString systemTimeString() const
    { return _timeString( systemTime_ ); }

    // time
    QString cpuTimeString() const
    { return _timeString( userTime_ + systemTime_ ); }

    //* threads
    int threads() const
    { return threads_; }

    //* start time
    const TimeStamp startTime() const
    { return startTime_; }

    //*icon initialized
    bool iconInitialized() const
    { return iconInitialized_; }

    //* icon
    QIcon icon() const
    { return icon_; }

    //@}

    //*@name modifiers
    //@{

    //* clear mask
    void clearMask()
    { mask_ = 0; }

    //* update from other job
    void updateFrom( const Job& );

    //* read name from /proc/pid/cmdline
    void readFromCommandLine();

    //* read information from /proc/pid/status
    bool readFromStatus();

    //* read information from /proc/pid/stat
    bool readFromStat();

    //* read information from /proc/pid/statM
    bool readFromStatM();

    //* read start time
    bool readStartTime();

    //* id
    void setId( long value )
    {
        id_ = value;
        mask_ |= Id;
    }

    //* parent id
    void setParentId( long value )
    {
        parentId_ = value;
        mask_ |= ParentId;
    }

    //* parent id
    void setTreeParentId( long value )
    {
        treeParentId_ = value;
        mask_ |= TreeParentId;
    }

    //* name
    void setName( const QString& value )
    {
        name_ = value;
        mask_ |= Name;
    }

    //* long name
    void setLongName( const QString& value )
    {
        longName_ = value;
        mask_ |= LongName;
    }

    //* command
    void setCommand( const QString& value )
    {
        command_ = value;
        mask_ |= Command;
    }

    //* user
    void setUser( const QString& value )
    {
        user_ = value;
        mask_ |= User;
    }

    //* user
    void setUserId( qint64 value )
    {
        userId_ = value;
        mask_ |= UserId;
    }

    //* state
    void setState( QChar value )
    {
        state_ = value;
        mask_ |= State;
    }

    //* cpu
    void setCpu( qreal value )
    {
        cpu_ = value;
        mask_ |= Cpu;
    }

    void setMemory( qreal value )
    {
        memory_ = value;
        mask_ |= Memory;
    }

    //* priority
    void setPriority( int value )
    {
        priority_ = value;
        mask_ |= Priority;
    }

    //* nice
    void setNice( int value )
    {
        nice_ = value;
        mask_ |= Nice;
    }

    //* virtual memory
    void setVirtualMemory( qint64 value )
    {
        virtualMemory_ = value;
        mask_ |= VirtualMemory;
    }

    //* resident memory
    void setResidentMemory( qint64 value )
    {
        residentMemory_ = value;
        mask_ |= ResidentMemory;
    }

    //* shared memory
    void setSharedMemory( qint64 value )
    {
        sharedMemory_ = value;
        mask_ |= SharedMemory;
    }

    //* user time
    void setUserTime( qint64 value )
    {
        userTime_ = value;
        mask_ |= UserTime;
    }

    //* system time
    void setSystemTime( qint64 value )
    {
        systemTime_ = value;
        mask_ |= SystemTime;
    }

    //* up time
    void setElapsedTime( qint64 value )
    {
        elapsedTime_ = value;
        mask_ |= ElapsedTime;
    }

    //* threads
    void setThreads( int value )
    {
        threads_ = value;
        mask_ |= Threads;
    }

    //* start time
    void setStartTime( const TimeStamp& value )
    {
        startTime_ = value;
        mask_ |= StartTime;
    }

    //* icon initialized
    void setIconInitialized( bool value )
    { iconInitialized_ = value; }

    //* icon
    void setIcon( QIcon icon )
    {
        icon_ = icon;
        mask_ |= Icon;
    }

    //@}

    //* used to cound running jobs
    class IsRunningFTor
    {
        public:

        bool operator() (const Job& job ) const
        { return job.isRunning(); }

    };

    //* used to select with Id matching an integer
    class SameIdFTor
    {
        public:

        //* constructor
        explicit SameIdFTor( long id ):
            id_( id )
        {}

        //* predicate
        bool operator() (const Job& job ) const
        {  return job.id() == id_; }

        private:

        //* field index
        long id_;

    };

    protected:

    //* convert time integer to string
    static QString _timeString( int );

    private:

    //* mask
    Flags mask_ = 0;

    //* id
    long id_ = 0;

    //* parent id
    long parentId_ = 0;

    //* parent id (for tree)
    long treeParentId_ = 0;

    //* name
    QString name_;

    //* long command
    QString longName_;

    //* full command
    QString command_;

    //* user
    QString user_;

    //* user id
    qint64 userId_ = 0;

    //* state
    QChar state_;

    //* cpu percentage
    qreal cpu_ = 0;

    //* memory percentage
    qreal memory_ = 0;

    //* priority
    int priority_ = 0;

    //* nice
    int nice_ = 0;

    //* virtual memory
    qint64 virtualMemory_ = 0;

    //* resident memory
    qint64 residentMemory_ = 0;

    //* shared memory
    qint64 sharedMemory_ = 0;

    //* user time (milliseconds)
    qint64 userTime_ = 0;

    //* system time (milliseconds)
    qint64 systemTime_ = 0;

    //* up time (milliseconds)
    qint64 elapsedTime_ = 0;

    //* start time
    TimeStamp startTime_;

    //* number of threads
    int threads_ = 0;

    //* true if icon is set (or absent)
    bool iconInitialized_ = false;

    //* icon
    QIcon icon_;

};

//* lower than operator
inline bool operator < ( const Job& first, const Job& second )
{
    return
        (first.parentId() < second.parentId()) ||
        (first.parentId() == second.parentId() && first.id() < second.id() );
}

//* equal to operator
inline bool operator == ( const Job& first, const Job& second )
{
    return
        first.parentId() == second.parentId() &&
        first.id() == second.id();
}

// hash (this is utterly wrong
inline uint qHash( const Job& job )
{ return job.parentId() << 8 | job.id(); }

Q_DECLARE_OPERATORS_FOR_FLAGS( Job::Flags );

#endif
