
#pragma once

#include "defs.h"
#include "utils.h"
#include "Func.h"

S_BEGIN_DECLS

#include "config.h"

//#if  !__STDC_NO_ATOMICS__
//#include <stdatomic.h>
//#else
#include "external/stdatomic.h"
//#endif

//#if !__STDC_NO_THREADS__
//#include <threads.h>
//#else
#include "external/tinycthread.h"
//#endif


/**
 * @defgroup Threading Threading
 * @addtogroup Threading
 * @{
 */


/**
 * Sleep for a set amount of micro seconds.
 */
S_EXPORTED
void
s_usleep (slong us);

/** @brief
 * The status of the thread.
 *
 * These values are equivalent to the value defined my the threads.h. They are
 * offten used as return values for different fuctions.
 */
typedef enum {
  S_THREAD_STATUS_SUCCESS    = thrd_success,  /**< Succes*/
  S_THREAD_STATUS_TIMEOUT    = thrd_timedout, /**< Timedout*/
  S_THREAD_STATUS_ERROR      = thrd_error,    /**< Error */
  S_THREAD_STATUS_BUSY       = thrd_busy,     /**< Busy */
  S_THREAD_STATUS_NO_MEM     = thrd_nomem,    /**< No memory*/
  S_THREAD_STATUS_LAST                        /**< Not used */
} SThreadStatus;

/**
 * @see SThreadStatus
 */
S_UNUSED static
schar * SThreadStatusName[] = {
  "Succes",
  "Timedout",
  "Error",
  "Busy",
  "No Momory",
  "INVALID",
  0x0,
  0x0
};

/** @brief
 * Get the status name.
 *
 * For use in bindings.
 *
 * @see SThreadStatus
 * @see SThreadStatusName
 */
S_EXPORTED
char *
s_thread_status_get_name (SThreadStatus status);

/**
 * The flags used when creating threads.
 * These map 1:1 to the C11 flags.
 */
typedef enum {
 S_MUTEX_FLAG_PLAIN     = mtx_plain,     /**< Plain mutex */
 S_MUTEX_FLAG_TIMED     = mtx_timed,     /**< Timed mutex */
 S_MUTEX_FLAG_RECURSIVE = mtx_recursive, /**< Recursive mutex */
 S_MUTEX_FLAG_LAST
} SMutexFlag;

/**
 * @see SMutexFlag
 */
S_UNUSED static
schar * SMutexFlagName[] = {
  "Plain",
  "Timed",
  "Recursive",
  "INVALID",
  0x0,
  0x0
};

/** @brief
 * Get the name of the mutex flag.
 *
 * For use in bindings.
 */
schar *
s_mutex_flag_get_name (SMutexFlag flag);

/* ****************************************************************************
 ********************************** SMutex ************************************
 **************************************************************************** */

/**
 * @defgroup SMutex SMutex
 * @addtogroup SMutex
 * @{
 */


/**
 * An SMutex is an opaque data type that handles the platform specifics of the
 * Mutex, if it exists. If not we roll our own.
 */
typedef struct SMutex SMutex;

/**
 * Create a new SMutex;
 */
S_EXPORTED
SMutex *
s_mutex_new ();

/**
 * Free the mutex.
 */
S_EXPORTED
void
s_mutex_free (SMutex * self);

/**
 * Lock the mutex.
 *
 * Returns a key used to unlock the mutex.
 */
S_EXPORTED
SThreadStatus
s_mutex_lock (SMutex * self);

/**
 * unlock the mutex.
 *
 * @param self The mutex to unlock;
 * @param key The key used to unlock the mutex.
 */
S_EXPORTED
SThreadStatus
s_mutex_unlock (SMutex * self);

/**
 * Check if a mutex is locked.
 *
 * Note that this operation is non-atomic and may be wrong.
 */
S_EXPORTED
SThreadStatus
s_mutex_check_lock (SMutex * self);

/** @} */

/* ****************************************************************************
 ********************************** SThread ***********************************
 **************************************************************************** */

/**
 * @defgroup SThread SThread
 * @addtogroup SThread
 * @{
 */

/**
 * An opaque data type representing a Thread.
 */
typedef struct SThread SThread;

/**
 * Creat a now Thread.
 */
S_EXPORTED
SThread *
s_thread_new (RunFunc func);

/**
 * Free the thread object.
 */
S_EXPORTED
void
s_thread_free (SThread * self);

/**
 * Run the thread.
 *
 * @note You must pass the self object as part of the user_data object, or have
 *       the self object globally avalible. Otherwise you will not be able to
 *       stop the thread using s_thread_stop.
 */
S_EXPORTED
SThreadStatus
s_thread_run (SThread * self, spointer user_data);


S_EXPORTED
void
s_thread_stop (SThread * self, sint res);


/** @} */

/** @} */

S_END_DECLS
