#include "thread.hpp"

#include <cstdarg>										// va_start, va_end
#include <cstdio>
#include <cstring>										// strlen
extern "C" {
	#include <unistd.h>										// _exit, getpid
	#include <signal.h>
	#include <dlfcn.h>										// dlopen, dlsym
	#include <execinfo.h>									// backtrace, messages
}

#include <iostream>
#include <string>

using thrdlib::thread_t;

thread_t (*thrdlib::create)( void (*main)( thread_t ) ) = nullptr;
void (*thrdlib::join)( thread_t handle ) = nullptr;
void (*thrdlib::park)( thread_t handle ) = nullptr;
void (*thrdlib::unpark)( thread_t handle ) = nullptr;
void (*thrdlib::yield)( void ) = nullptr;
void (*lib_clean)(void) = nullptr;

typedef void (*fptr_t)();
static fptr_t open_symbol( void * library, const char * symbol, bool required ) {
	void * ptr = dlsym( library, symbol );

	const char * error = dlerror();
	if ( required && error ) {
		std::cerr << "Fetching symbol '" << symbol << "' failed with error '" << error << "'\n";
		std::abort();
	}

	return (fptr_t)ptr;
}

//--------------------
// Basic kernel features
void thrdlib::init( const char * name, int procs ) {
	std::string file = __FILE__;
	std::size_t found = file.find_last_of("/");
  	std::string libname = file.substr(0,found+1) + name + ".so";

	std::cout << "Use framework " << name << "(" << libname << ")\n";

	void * library = dlopen( libname.c_str(), RTLD_NOW );
	if ( const char * error = dlerror() ) {
		std::cerr << "Could not open library '" << libname << "' from name '" << name <<"'\n";
		std::cerr << "Error was : '" << error << "'\n";
		std::abort();
	}

	void (*lib_init)( int ) = (void (*)( int ))open_symbol( library, "thrdlib_init", false );
	lib_clean = open_symbol( library, "thrdlib_clean" , false );

	thrdlib::create = (typeof(thrdlib::create))open_symbol( library, "thrdlib_create", true  );
	thrdlib::join   = (typeof(thrdlib::join  ))open_symbol( library, "thrdlib_join"  , true  );
	thrdlib::park   = (typeof(thrdlib::park  ))open_symbol( library, "thrdlib_park"  , true  );
	thrdlib::unpark = (typeof(thrdlib::unpark))open_symbol( library, "thrdlib_unpark", true  );
	thrdlib::yield  = (typeof(thrdlib::yield ))open_symbol( library, "thrdlib_yield" , true  );

	lib_init( procs );
}

void thrdlib::clean( void ) {
	if(lib_clean) lib_clean();
}