#ifndef RT_THREADING_H
#define RT_THREADING_H

/* platform independent threading */

#include <stdbool.h>

#include "runtime.h"

#ifdef __cplusplus
extern "C" {
#endif

/* Mutexes */

typedef struct rt_mutex_s rt_mutex;

RT_DLLEXPORT rt_mutex *rtCreateMutex(void);

RT_DLLEXPORT void rtDestroyMutex(rt_mutex *mutex);

RT_DLLEXPORT bool rtLockMutex(rt_mutex *mutex);

RT_DLLEXPORT bool rtUnlockMutex(rt_mutex *mutex);

/* Condition variables */

typedef struct rt_condition_var_s rt_condition_var;

RT_DLLEXPORT rt_condition_var *rtCreateConditionVar(void);

RT_DLLEXPORT void rtDestroyConditionVar(rt_condition_var *var);

RT_DLLEXPORT void rtLockConditionVar(rt_condition_var *var);

RT_DLLEXPORT void rtUnlockConditionVar(rt_condition_var *var, bool signal);

/* The condition variable must be locked by the thread! */
RT_DLLEXPORT void rtWaitOnConditionVar(rt_condition_var *var);

/* Read-Write Lock */
typedef struct {
    volatile int reader_count;
    rt_condition_var *cond;
} rt_rwlock;

typedef struct {
    bool ok;
    rt_rwlock lock;
} rt_create_rwlock_result;

RT_DLLEXPORT rt_create_rwlock_result rtCreateRWLock(void);

RT_DLLEXPORT void rtDestroyRWLock(rt_rwlock *lock);

RT_DLLEXPORT void rtLockRead(rt_rwlock *lock);

RT_DLLEXPORT void rtUnlockRead(rt_rwlock *lock);

RT_DLLEXPORT void rtLockWrite(rt_rwlock *lock);

RT_DLLEXPORT void rtUnlockWrite(rt_rwlock *lock);

/* Semaphore */
typedef struct {
    volatile int counter;
    int max;
    rt_condition_var *cond;
} rt_semaphore;

typedef struct {
    bool ok;
    rt_semaphore semaphore;
} rt_create_semaphore_result;

RT_DLLEXPORT rt_create_semaphore_result rtCreateSemaphore(int initial_value, int max_value);

RT_DLLEXPORT void rtDestroySemaphore(rt_semaphore *sem);

RT_DLLEXPORT void rtSignalSemaphore(rt_semaphore *sem);

RT_DLLEXPORT void rtWaitOnSemaphore(rt_semaphore *sem);

/* Spinlock */
typedef struct {
    volatile int lock;
} rt_spinlock;

static RT_INLINE rt_spinlock rtCreateSpinlock(void) {
    rt_spinlock l = {0};
    return l;
}

RT_DLLEXPORT void rtLockSpinlock(rt_spinlock *lock);

RT_DLLEXPORT void rtUnlockSpinlock(rt_spinlock *lock);

/* Threads */

typedef struct rt_thread_s rt_thread;

typedef void rt_thread_entry_fn(void *param);

RT_DLLEXPORT rt_thread *rtSpawnThread(rt_thread_entry_fn *entry, void *param, const char *name);

RT_DLLEXPORT void rtJoinThread(rt_thread *thread);

RT_DLLEXPORT unsigned int rtGetCPUCoreCount(void);

typedef uint32_t rt_thread_id;

RT_DLLEXPORT rt_thread_id rtGetCurrentThreadId(void);

RT_DLLEXPORT bool rtIsMainThread(void);

RT_DLLEXPORT void rtSleep(unsigned int milliseconds);

#ifdef __cplusplus
}
#endif

#endif
