#ifndef __SAFEC_THREADS_H
#define __SAFEC_THREADS_H
#include "types.h"
#include <time.h>

#ifdef __STDC_NO_THREADS__

#include <pthread.h>

typedef pthread_t thrd_t;
typedef pthread_mutex_t mtx_t;
typedef pthread_cond_t cnd_t;
typedef void* thrd_ret_type;
typedef thrd_ret_type (*thrd_start_t)(void*);

#define thrd_success 0
#define thrd_timedout ETIMEDOUT
#define mtx_plain NULL

#define mtx_lock(m) pthread_mutex_lock(m)
#define mtx_timedlock(m, t) pthread_mutex_timedlock(m, t)
#define mtx_trylock(m) pthread_mutex_trylock(m)
#define mtx_unlock(m) pthread_mutex_unlock(m)
#define mtx_init(m, t) pthread_mutex_init(m, t)
#define mtx_destroy(m) pthread_mutex_destroy(m)

#define cnd_init(c) pthread_cond_init(c, NULL)
#define cnd_signal(c) pthread_cond_signal(c)
#define cnd_broadcast(c) pthread_cond_broadcast(c)
#define cnd_wait(c, m) pthread_cond_wait(c, m)
#define cnd_timedwait(c, m, t) pthread_cond_timedwait(c, m, t)
#define cnd_destroy(c) pthread_cond_destroy(c)

#define thrd_create(t, w, a) pthread_create(t, NULL, w, a)
#define thrd_equal(t1, t2) pthread_equal(t1, t2)
#define thrd_current() pthread_self()
#define thrd_yield() pthread_yield()
#define thrd_exit(rv) pthread_exit(rv)
#define thrd_detach(t) pthread_detach(t)
#define thrd_join(t, rv) pthread_join(t, rv)

#else

#include <threads.h>
typedef int thrd_ret_type;

#endif

typedef struct {
    mtx_t __lock;
    cnd_t __cond;
    ssize_t __val;
} sema_t;

/* these return thrd_success on success and something else on failure */
int sema_init(sema_t* sema, ssize_t value);
int sema_get_value(sema_t* sema, ssize_t* p_value);
int sema_up(sema_t* sema);
int sema_up_many(sema_t* sema, ssize_t n);
int sema_down(sema_t* sema);
int sema_try_down(sema_t* sema);
int sema_down_timed(sema_t* sema, const struct timespec* time);
int sema_down_many(sema_t* sema, ssize_t n);
int sema_try_down_many(sema_t* sema, ssize_t n);
int sema_down_many_timed(sema_t* sema, const struct timespec* time, ssize_t n);
void sema_destroy(sema_t* sema);

#endif
