#ifndef ASYNC_HPP_
#define ASYNC_HPP_

#include <functional>

/**
 * @brief Enumeration of the possible async task priority classes.
 */
enum class PriorityClass
{
    /**
     * @brief Miscellaneous tasks.
     * 
     * This priority class tends to give way to the other classes, but after a
     * certain amount of time it's allowed to run. Thus, it makes steady progress
     * at all times, albeit slower when there are other tasks.
     */
    Misc,

    /**
     * @brief The task is related to rendering chunks.
     * 
     * Chunk rendering happens before world generation unconditionally.
     */
    ChunkRender,

    /**
     * @brief The task is related to world generation.
     * 
     * Tasks with this class give way to `ChunkRender`.
     */
    WorldGen,
};

/**
 * @brief Enumeration of the possible async barrier types.
 */
enum class AsyncBarrier
{
    /**
     * @brief The callback is called at some point after all already-running tasks end.
     */
    CurrentlyRunning,

    /**
     * @brief The callback is called at some point after all already-dispatched tasks end.
     */
    CurrentlyDispatched,

    // Make sure this is always last; this is the number of barrier types.
    Num,
};

/**
 * @brief Initialize the asynchronous worker system.
 */
void initAsync();

/**
 * @brief Call the callback function asynchronously.
 * 
 * @param callback This will be called when the task is scheduled.
 * @param relativeDeadline The deadline for this task relative to other tasks (`0.0f` means closer, `1.0f` furthest).
 *                         By default, the relative deadline is `1.0f`, meaning it has the furthest deadline and thus
 *                         lowest priority.
 * @param priorityClass The priority class this task should fall into (`Misc` by default).
 */
void dispatchTask(std::function<void()> callback, float relativeDeadline = 1.0f, PriorityClass priorityClass = PriorityClass::Misc);

/**
 * @brief Call a callback after something specific happens to async tasks.
 * 
 * @param barrier The barrier (decides what we will wait for).
 * @param callback The callback to call once the barrier is passed.
 */
void afterAsyncBarrier(AsyncBarrier barrier, std::function<void()> callback);

/**
 * @brief Block until a specific async barrier is passed.
 */
void awaitAsyncBarrier(AsyncBarrier barrier);

/**
 * @brief Return the number of pending async tasks.
 * 
 * This can be called from any thread. The returned value only makes sense as a statistic.
 */
int getPendingTasks();

#endif