/*****************************************************************************
 * Copyright (C) 2017 VLC authors and VideoLAN
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifndef BACKGROUND_WORKER_H__
#define BACKGROUND_WORKER_H__

struct background_worker_config {
    /**
     * Default timeout for completing a task
     *
     * If less-than 0 a task can run indefinitely without being killed, whereas
     * a positive value denotes the maximum number of milliseconds a task can
     * run before \ref pf_stop is called to kill it.
     **/
    vlc_tick_t default_timeout;

    /**
     * Release an entity
     *
     * This callback will be called in order to decrement the ref-count of a
     * entity within the background-worker. It will happen either when \ref
     * pf_stop has finished executing, or if the entity is removed from the
     * queue (through \ref background_worker_Cancel)
     *
     * \param entity the entity to release
     **/
    void( *pf_release )( void* entity );

    /**
     * Hold a queued item
     *
     * This callback will be called in order to increment the ref-count of an
     * entity. It will happen when the entity is pushed into the queue of
     * pending tasks as part of \ref background_worker_Push.
     *
     * \param entity the entity to hold
     **/
    void( *pf_hold )( void* entity );

    /**
     * Start a new task
     *
     * This callback is called in order to construct a new background task. In
     * order for the background-worker to be able to continue processing
     * incoming requests, \ref pf_start is meant to start a task (such as a
     * thread), and then store the associated handle in `*out`.
     *
     * The value of `*out` will then be the value of the argument named `handle`
     * in terms of \ref pf_probe and \ref pf_stop.
     *
     * \param owner the owner of the background-worker
     * \param entity the entity for which a task is to be created
     * \param out [out] `*out` shall, on success, refer to the handle associated
     *                   with the running task.
     * \return VLC_SUCCESS if a task was created, an error-code on failure.
     **/
    int( *pf_start )( void* owner, void* entity, void** out );

    /**
     * Probe a running task
     *
     * This callback is called in order to see whether or not a running task has
     * finished or not. It can be called anytime between a successful call to
     * \ref pf_start, and the corresponding call to \ref pf_stop.
     *
     * \param owner the owner of the background-worker
     * \param handle the handle associated with the running task
     * \return 0 if the task is still running, any other value if finished.
     **/
    int( *pf_probe )( void* owner, void* handle );

    /**
     * Stop a running task
     *
     * This callback is called in order to stop a running task. If \ref pf_start
     * has created a non-detached thread, \ref pf_stop is where you would
     * interrupt and then join it.
     *
     * \warning This function is called either after \ref pf_probe has stated
     *          that the task has finished, or if the timeout (if any) for the
     *          task has been reached.
     *
     * \param owner the owner of the background-worker
     * \parma handle the handle associated with the task to be stopped
     **/
    void( *pf_stop )( void* owner, void* handle );
};

/**
 * Create a background-worker
 *
 * This function creates a new background-worker using the passed configuration.
 *
 * \warning all members of `config` shall have been set by the caller.
 * \warning the returned resource must be destroyed using \ref
 *          background_worker_Delete on success.
 *
 * \param owner the owner of the background-worker
 * \param config the background-worker's configuration
 * \return a pointer-to the created background-worker on success,
 *         `NULL` on failure.
 **/
struct background_worker* background_worker_New( void* owner,
    struct background_worker_config* config );

/**
 * Request the background-worker to probe the current task
 *
 * This function is used to signal the background-worker that it should do
 * another probe to see whether the current task is still alive.
 *
 * \warning Note that the function will not wait for the probing to finish, it
 *          will simply ask the background worker to recheck it as soon as
 *          possible.
 *
 * \param worker the background-worker
 **/
void background_worker_RequestProbe( struct background_worker* worker );

/**
 * Push an entity into the background-worker
 *
 * This function is used to push an entity into the queue of pending work. The
 * entities will be processed in the order in which they are received (in terms
 * of the order of invocations in a single-threaded environment).
 *
 * \param worker the background-worker
 * \param entity the entity which is to be queued
 * \param id a value suitable for identifying the entity, or `NULL`
 * \param timeout the timeout of the entity in milliseconds, `0` denotes no
 *                timeout, a negative value will use the default timeout
 *                associated with the background-worker.
 * \return VLC_SUCCESS if the entity was successfully queued, an error-code on
 *         failure.
 **/
int background_worker_Push( struct background_worker* worker, void* entity,
    void* id, int timeout );

/**
 * Remove entities from the background-worker
 *
 * This function is used to remove processing of a certain entity given its
 * associated id, or to remove all queued (including currently running)
 * entities.
 *
 * \warning if the `id` passed refers to an entity that is currently being
 *          processed, the call will block until the task has been terminated.
 *
 * \param worker the background-worker
 * \param id NULL if every entity shall be removed, and the currently running
 *        task (if any) shall be cancelled.
 **/
void background_worker_Cancel( struct background_worker* worker, void* id );

/**
 * Delete a background-worker
 *
 * This function will destroy a background-worker created through \ref
 * background_worker_New. It will effectively stop the currently running task,
 * if any, and empty the queue of pending entities.
 *
 * \warning If there is a currently running task, the function will block until
 *          it has been stopped.
 *
 * \param worker the background-worker
 **/
void background_worker_Delete( struct background_worker* worker );
#endif
