#include "cor_platform.h"
#include <assert.h>
#include "timespec_utils.h"

void _Cor_Mutex_ctor(_Cor_Mutex *mut){
    int r;
    r = pthread_mutex_init(&mut->mut, NULL);
    assert(r == 0);
}


void _Cor_Mutex_dtor(_Cor_Mutex *mut){
    int r;
    r = pthread_mutex_destroy(&mut->mut);
    assert(r == 0);
}


void _Cor_Mutex_Lock(_Cor_Mutex *mut){
    int r;
    r = pthread_mutex_lock(&mut->mut);
    assert(r == 0);
}


void _Cor_Mutex_Unlock(_Cor_Mutex *mut){
    int r;
    r = pthread_mutex_unlock(&mut->mut);
    assert(r == 0);
}


int64_t _Cor_Realtime_Now(){
    int r;
    struct timespec now;
    r = clock_gettime(CLOCK_REALTIME, &now);
    assert(r == 0);
    return int64_ns_from_timespec(now);
}


void _Cor_Semaphore_ctor(_Cor_Semaphore *sem){
    int r;
    r = pthread_mutex_init(&sem->mut, NULL);
    assert(r == 0);
    r = pthread_cond_init(&sem->cond, NULL);
    assert(r == 0);
    sem->count = 0;
}


void _Cor_Sempahore_dtor(_Cor_Semaphore *sem){
    int r;
    r = pthread_cond_destroy(&sem->cond);
    assert(r == 0);
    r = pthread_mutex_destroy(&sem->mut);
    assert(r == 0);
}


// timeout_when < 0 means 'wait forever'
// Returns true for success, false for timeout
bool _Cor_Semaphore_Wait(_Cor_Semaphore *sem, int64_t timeout_when){
    int r;
    r = pthread_mutex_lock(&sem->mut);
    assert(r == 0);
    if (sem->count == 0) {
        if (timeout_when >= 0) {
            struct timespec ts = timespec_from_int64_ns(timeout_when);
            r = pthread_cond_timedwait(&sem->cond, &sem->mut, &ts);
        }
        else {
            r = pthread_cond_wait(&sem->cond, &sem->mut);
        }
    }
    assert(r == 0 || r == ETIMEDOUT);
    bool res;
    if (sem->count > 0) {
        sem->count--;
        res = true;
    } else {
        res = false;
    }
    r = pthread_mutex_unlock(&sem->mut);
    assert(r == 0);

    return res;
}


void _Cor_Semaphore_Signal(_Cor_Semaphore *sem){
    int r;
    r = pthread_mutex_lock(&sem->mut);
    assert(r == 0);
    sem->count++;
    r = pthread_cond_signal(&sem->cond);
    assert(r == 0);
    r = pthread_mutex_unlock(&sem->mut);
    assert(r == 0);
}
