#include <pthread.h>
#include <sys/time.h>

#include "events.h"

pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

event create_event(bool initval)
{
	bool *p = new bool;
	*p = initval;
	return p;
}

void delete_event(event ev)
{
	delete ev;
}

void set_event(event ev)
{
	pthread_mutex_lock(&mut);
	*ev = true;
	pthread_cond_signal(&cond);
	pthread_mutex_unlock(&mut);
}

void reset_event(event ev)
{
	pthread_mutex_lock(&mut);
	*ev = false;
	pthread_cond_signal(&cond);
	pthread_mutex_unlock(&mut);
}

int wait_for_multiple_events(int count, event events[], bool all, long msec)
{
	pthread_mutex_lock(&mut);
	for (int i=0; i<count; i++)
		if (*events[i]) {
			pthread_mutex_unlock(&mut);
			return i;
		}
	if (msec==0) {
		pthread_mutex_unlock(&mut);
		return -1;
	}

	if (msec==-1)
		pthread_cond_wait(&cond, &mut);
	else {
		struct timespec ts;
		struct timeval tv;

		gettimeofday(&tv,0);
		long usec = tv.tv_usec + msec*1000;
		ts.tv_sec = tv.tv_sec + usec/1000000;
		ts.tv_nsec = (usec%1000000)*1000;
		pthread_cond_timedwait(&cond, &mut, &ts);
	}

	for (int i=0; i<count; i++)
		if (*events[i]) {
			pthread_mutex_unlock(&mut);
			return i;
		}
	pthread_mutex_unlock(&mut);
	return -1;
}

critical_section::critical_section()
{
	handle = new pthread_mutex_t;
	pthread_mutex_init((pthread_mutex_t*)handle, 0);
}

critical_section::~critical_section()
{
	pthread_mutex_destroy((pthread_mutex_t*)handle);
	delete (pthread_mutex_t*)handle;
}


void critical_section::enter()
{
	if (pthread_mutex_lock((pthread_mutex_t*)handle))
		throw std::runtime_error("Cannot lock mutex");
}

void critical_section::leave()
{
	if (pthread_mutex_unlock((pthread_mutex_t*)handle))
		throw std::runtime_error("Cannot unlock mutex");
}
