/* This file is part of the Chakra project

   Copyright (C) 2010 Dario Freddi <drf@chakra-project.org>

   This program 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.
*/

#ifndef AKABEIOPERATIONRUNNER_H
#define AKABEIOPERATIONRUNNER_H

#include <QObject>

#include <akabeioperation.h>
#include <akabeicore_global.h>

namespace Akabei {

class OperationRunnerPrivate;
/**
 * \class OperationRunner  akabeioperationrunner.h "akabeioperationrunner.h"
 *
 * \brief This class is used to process operations.
 *
 * Use Backend::instance()->operationRunner() to access the global OperationRunner object.
 * Then add all \link Akabei::Operation Operations \endlink you constructed, set processing options and validate it.
 * If validated() returns sucessfully, use run() to process the queue of operations.
 *
 * This class is thread-safe.
 */
class OperationRunner : public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY(OperationRunner)
    Q_DECLARE_PRIVATE(OperationRunner)
public:
    virtual ~OperationRunner();

    /**
     * @returns the number of operations currently in queue
     */
    qint64 count() const;

    /**
     * @returns all operations currently in queue
     */
    QList<Operation*> operations() const;
    /**
     * @returns all operations which are in a certain phase
     * @param phase the phase the operations must be in
     */
    QList<Operation*> operationsForPhase(Operation::Phase phase) const;

    /**
     * Appends an operation to the queue
     * @param operation the operation which gets added
     */
    void append(Operation *operation);
    /**
     * Removes an operation from the queue
     * @param operation the operation which gets removed
     */
    void remove(Operation *operation);

    /**
     * Removes all operations from the queue
     */
    void clear();

    /**
     * @returns a list of errors which occurred during processing or validating
     */
    Error::List errors() const;

    /**
     * Sets the processing options to be used by the operations
     * @param options the processing options to be used, by default none
     */
    void setProcessingOptions(ProcessingOptions options = NoProcessingOption);
    /**
     * @returns the processing options used by the operation runner
     */
    ProcessingOptions processingOptions() const;

public Q_SLOTS:
    /**
     * Validates the queue.
     * This does not only mean calling Operation::validate() on all operations,
     * but also checking if there are conflicts on disk, dependency conflicts etc.
     * When the validation finished \c validated gets emitted.
     *
     * @see phaseValidationStarted
     * @see phaseValidationFinished
     * @see validated
     */
    void validate();
    /**
     * Runs Operation::run() on every operation in the queue, when possible concurrent.
     * When the processing finished \c finished gets emitted.
     *
     * @see phaseStarted
     * @see phaseFinished
     * @see operationStarted
     * @see operationProgress
     * @see operationFinished
     * @see errorsOccurred
     * @see finished
     */
    void run();

    /**
     * Aborts all current operations.
     */
    void cancel();

Q_SIGNALS:
    /**
     * The operation runner started to validate the operations of a certain phase.
     * @param phase the phase currently validated
     */
    void phaseValidationStarted(Akabei::Operation::Phase phase);
    /**
     * The operation runner finished to validate the operations of a certain phase.
     * @param phase the phase which finished to validate
     */
    void phaseValidationFinished(Akabei::Operation::Phase phase);
    /**
     * The operation runner started to process the operations of a certain phase.
     * @param phase the phase currently processed
     */
    void phaseStarted(Akabei::Operation::Phase phase);
    /**
     * The operation runner finished to process the operations of a certain phase.
     * @param phase the phase which just finished to run
     */
    void phaseFinished(Akabei::Operation::Phase phase);

    /**
     * An operation started to run.
     * @param operation the operation which just started
     */
    void operationStarted(Akabei::Operation *operation);
    /**
     * The status of an operation changed.
     * @param operation the operation of which the status changed
     * @param current the current status of the operation
     */
    void operationStatusChanged(Akabei::Operation *operation, Akabei::Operation::Status current);
    /**
     * The progress of an operation changed.
     * @param operation the operation of which the progress changed
     * @param percentage the current progress in percent
     */
    void operationProgress(Akabei::Operation *operation, int percentage);
    /**
     * The estimated time left for an operation changed
     * @param operation the operation which has now roughly
     * @param eta seconds to get finished
     */
    void operationEtaChanged(Akabei::Operation* operation, int eta);
    /**
     * An operation wants to show a message to the user.
     * @param operation the operation which wants to show the message
     * @param message the message to show
     */
    void operationMessage(Akabei::Operation *operation, const QString &message);
    /**
     * An operation finished to execute.
     * @param operation the operation which finished
     */
    void operationFinished(Akabei::Operation *operation);

    /**
     * There were errors during the execution.
     * @param errors the errors which occurred
     */
    void errorsOccurred(const Akabei::Error::List& errors);

    /**
     * The validation finished
     * @param result true if successful, false if the queue could not be validated
     */
    void validated(bool result);
    /**
     * The processing finished
     * @param result true if successful, false if errors occurred
     */
    void finished(bool result);

private:
    explicit OperationRunner(QObject *parent = nullptr);

    OperationRunnerPrivate * const d_ptr;

    Q_PRIVATE_SLOT(d_func(), void __k__errorsOccurred(Akabei::Error::List))
    Q_PRIVATE_SLOT(d_func(), void __k__validationFinished(bool,OpsHash))
    Q_PRIVATE_SLOT(d_func(), void __k__finished(bool))
    Q_PRIVATE_SLOT(d_func(), void __k__connectToValidationStatus())
    Q_PRIVATE_SLOT(d_func(), void __k__connectToRunnerStatus())
    Q_PRIVATE_SLOT(d_func(), void __k__doValidate())

    friend class BackendPrivate;
    friend class Operation;
    friend class OperationPrivate;
};

}

#endif // AKABEIOPERATIONRUNNER_H
