/*******************************************************************************
 *
 * SPDX-FileCopyrightText: 2023-2024 Open Mobile Platform LLC <community@omp.ru>
 * SPDX-License-Identifier: MIT
 *
 ******************************************************************************/

#ifndef RM_LIB_TASK_H
#define RM_LIB_TASK_H

#include "error.h"
#include "global.h"
#include "schedule.h"

#include <QObject>
#include <QScopedPointer>
#include <functional>

namespace RuntimeManager {

class TaskPrivate;
/**
 * @brief Manage a background task
 * @class Task task.h <RuntimeManager/Task>
 *
 * The Task class allows applications to start background tasks, track
 * their execution, and stop them.
 */
class RM_EXPORT Task: public QObject
{
    Q_OBJECT

public:
    enum class StopFlag {
        /** Terminate the process with SIGTERM + SIGKILL */
        None = 0,
        /** Prevent further starting of recurrent or scheduled processes */
        Disable = 1 << 0,
    };
    Q_DECLARE_FLAGS(StopFlags, StopFlag)

    enum State {
        /** The state has not yet been retrieved */
        Unknown,
        /** The task is running */
        Running,
        /** The task is not running and was not started */
        NotRunning,
        /**
         * The task is not running but will automatically be started when its
         * conditions are met or its scheduled time comes
         */
        Waiting,
    };

    /**
     * Constructor.
     */
    Task(const QString &taskId, QObject *parent = Q_NULLPTR);
    ~Task();

    /**
     * Get the task ID that was passed to this object's constructor.
     *
     * @return The ID of the task.
     */
    QString taskId() const;

    /**
     * Set the arguments that will be passed to the main function of the
     * executed process.
     */
    Task &withArguments(const QStringList &arguments);
    /**
     * Get the arguments passed to the new process.
     *
     * @return the list of arguments.
     */
    QStringList arguments() const;

    /**
     * Get the current state of the task.
     *
     * @return the state of the task.
     */
    State state() const;

    /**
     * Set the maximum running time (in seconds) for the task.
     */
    Task &withMaximumRunningTime(int seconds);
    /**
     * Get the maximum running time of this task, in seconds. Default is -1 (no
     * limit).
     *
     * @return the maximum running time.
     */
    int maximumRunningTime() const;

    /**
     * Set the time interval (in seconds) for periodic tasks. If this is not
     * specified, the periodic task will not be started.
     */
    Task &withInterval(int seconds);
    /**
     * Get the time interval for periodic tasks, in seconds.
     *
     * @return the time interval.
     */
    int interval() const;

    /**
     * Set whether the periodic task should be autostarted on the next sessions
     * too. By default, period tasks need to be explicitly started every time.
     */
    Task &withAutostart(bool autostart);
    /**
     * Get whether the periodic task should be autostarted on the next
     * sessions.
     *
     * @return true if the autostart is enabled.
     */
    bool autostart() const;

    /**
     * Set a delay (in seconds) for starting the task. This only affects tasks
     * of type "worker".
     */
    Task &withStartDelay(int seconds);
    /**
     * Get the start delay of this task, in seconds. Default is 0.
     *
     * @return the start delay in seconds.
     */
    int startDelay() const;

    /**
     * Set the start time for a background task. This only affects tasks
     * of type "scheduled".
     *
     * Note that this create a task that will start only once. If you need to
     * setup recurrent tasks, use setSchedule().
     */
    Task &withStartTime(const QDateTime &time);
    /**
     * Get the start time of this task.
     *
     * @return the start time of this task.
     */
    QDateTime startTime() const;

    /**
     * Set the schedule for a background task. This only affects tasks
     * of type "scheduled".
     */
    Task &withSchedule(const Schedule &schedule);
    /**
     * Get the schedule of this task.
     *
     * @return the schedule of this task.
     */
    Schedule schedule() const;

    /**
     * Set the priority the task. The value is interpreted in the same way as
     * the parameter of the "nice" UNIX command.
     * Note that specifying negative values (higher priority) is currently not
     * allowed.
     */
    Task &withPriority(int seconds);
    /**
     * Get the priority of the task. Default is 0.
     *
     * @return the task priority, as "nice" level.
     */
    int priority() const;

    /**
     * Set whether the runtime manager should create an IPC socket to let the
     * application communicate with the background task.
     *
     * @sa socketPath()
     */
    Task &withIpcSocket(bool enabled = true);
    /**
     * Get whether the task needs an IPC socket.
     *
     * @return true if an IPC socket is needed.
     */
    bool hasIpcSocket() const;

    /**
     * Callback for the start() and stop() methods.
     */
    using ErrorCallback = std::function<void(const Error &error)>;

    /**
     * Starts the background task identified by the task ID given at
     * construction time. The task will be executed in a separate process.
     *
     * Command line arguments can be specified using the setArguments() method.
     *
     * The actual state of the process can be monitored by listening to the
     * stateChanged() signal.
     *
     * @param callback A function to be invoked as the operation completes.
     */
    void start(const ErrorCallback &callback = {});

    /**
     * Request the task to be stopped. Calling this method when the task is not
     * running is not an error.
     *
     * @param callback A function to be invoked as the operation completes.
     */
    void stop(StopFlags stopFlags = StopFlag::None,
              const ErrorCallback &callback = {});

    /**
     * If the task was created with an IPC socket, this method will return the
     * path to the socket file.
     *
     * @return the path to the socket file.
     *
     * @sa withIpcSocket()
     */
    QString socketPath() const;

Q_SIGNALS:
    /**
     * Emitted when the process state changes.
     *
     * @param state Tell the current state of the process.
     *
     * @sa state()
     */
    void stateChanged(State state);

private:
    Q_DECLARE_PRIVATE(Task)
    QScopedPointer<TaskPrivate> d_ptr;
};

} // namespace RuntimeManager

Q_DECLARE_OPERATORS_FOR_FLAGS(RuntimeManager::Task::StopFlags)

#endif // RM_LIB_TASK_H
