#pragma once

#include <pthread.h>
#include <errno.h>
#include <cstring>
#include <cstdio>
#include <iostream>

#define CHECKED(x) { int err = x; if( err != 0 ) { std::cerr << "KERNEL ERROR: Operation \"" #x "\" return error " << err << " - " << strerror(err) << std::endl; std::abort(); } }

struct __bin_sem_t {
	pthread_mutex_t 	lock;
	pthread_cond_t  	cond;
	int     		val;

	__bin_sem_t() {
		// Create the mutex with error checking
		pthread_mutexattr_t mattr;
		pthread_mutexattr_init( &mattr );
		pthread_mutexattr_settype( &mattr, PTHREAD_MUTEX_ERRORCHECK_NP);
		pthread_mutex_init(&lock, &mattr);

		pthread_cond_init (&cond, nullptr);
		val = 0;
	}

	~__bin_sem_t() {
		CHECKED( pthread_mutex_destroy(&lock) );
		CHECKED( pthread_cond_destroy (&cond) );
	}

	void wait() {
		CHECKED( pthread_mutex_lock(&lock) );
			while(val < 1) {
				pthread_cond_wait(&cond, &lock);
			}
			val -= 1;
		CHECKED( pthread_mutex_unlock(&lock) );
	}

	bool post() {
		bool needs_signal = false;

		CHECKED( pthread_mutex_lock(&lock) );
			if(val < 1) {
				val += 1;
				pthread_cond_signal(&cond);
				needs_signal = true;
			}
		CHECKED( pthread_mutex_unlock(&lock) );

		return needs_signal;
	}
};

#undef CHECKED

//--------------------
// Basic types
struct pthread_runner_t {
	pthread_t handle;
	__bin_sem_t sem;
};
typedef pthread_runner_t * thread_t;

static_assert(sizeof(thread_t) == sizeof(void*), "thread_t musst be of same size as void*");

extern "C" {
	//--------------------
	// Basic thread support
	thread_t thrdlib_create( void (*main)( thread_t ) ) {
		thread_t thrd = new pthread_runner_t();
		int r = pthread_create( &thrd->handle, nullptr, (void *(*)(void *))main, thrd );
		if( r != 0 ) std::abort();
		return thrd;
	}

	void thrdlib_join( thread_t handle ) {
		void * ret;
		int r = pthread_join( handle->handle, &ret );
		if( r != 0 ) std::abort();
		delete handle;
	}

	void thrdlib_park( thread_t handle ) {
		handle->sem.wait();
	}

	void thrdlib_unpark( thread_t handle ) {
		handle->sem.post();
	}

	void thrdlib_yield( void ) {
		int r = pthread_yield();
		if( r != 0 ) std::abort();
	}

	//--------------------
	// Basic kernel features
	void thrdlib_init( int ) {}
	void thrdlib_clean( void ) {}
}