#include "thread.h"
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

FOHANDLE FoCreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes , DWORD dwStackSize , LPTHREAD_START_ROUTINE lpStartAddress , LPVOID lpParameters , DWORD dwCreationFlags , LPDWORD lpThreadId ) {
	pthread_attr_t attr;
	FOHANDLE h;
	int err,hadtoretry;

	if (lpThreadAttributes) {
		//fprintf( stderr , " CreateThread() lpThreadAttributes parameter not supported on Linux\n" );
		return NULL;
	}
	if ( dwCreationFlags ) {
		// fprintf( stderr , " CreateThread() dwCreationFlags parameter not supported on Linux\n" );
		return NULL;
	}
	if ( dwStackSize ) {
		// fprintf( stderr , " CreateThread() dwStacksize!=0 parameter not supported on Linux\n" );
		return NULL;
	}
	h = NULL;
	pthread_attr_init( &attr );
	/* rx sx sminek semmi ertelme sincsen, mert ugy is unimplementalt a pthread libraruban */
	/* pthread_attr_setdetachstate( &attr , PTHREAD_CREATE_JOINABLE );  */
	pthread_attr_setdetachstate( &attr , PTHREAD_CREATE_DETACHED );
	/* pthread_attr_setscope( &attr , PTHREAD_SCOPE_PROCESS ); */

	hadtoretry=0;
	err = pthread_create( (pthread_t *) lpThreadId, &attr , lpStartAddress, lpParameters);
	while (err!=0) {
		hadtoretry++;
		/*
		fprintf( stderr, " CreateThread() error: thread creation failed(%d),retrying...\n" ,err );
		fprintf( stderr, " detachstate:   <%08x>\n" , attr.__detachstate );
		fprintf( stderr, " schedpolicy:   <%08x>\n" , attr.__schedpolicy );
		fprintf( stderr, " schedprior:    <%08x>\n" , (int)attr.__schedparam.__sched_priority );
		fprintf( stderr, " inheritsched:  <%08x>\n" , attr.__inheritsched );
		fprintf( stderr, " scope:         <%08x>\n" , attr.__scope );
		fprintf( stderr ," guardsize:     <%08x>\n" , attr.__guardsize );
		fprintf( stderr ," stackaddr_set: <%08x>\n" , attr.__stackaddr_set );
		fprintf( stderr ," stackaddr:     <%08x>\n" , (int) attr.__stackaddr);
		fprintf( stderr ," stacksize:     <%08x>\n" , attr.__stacksize);
		Sleep( 10 );
		exit(2);
		*/
		return NULL;
	}
	if (hadtoretry) {
		/*
		fprintf( stderr, " CreateThread() successfully created after %d times\n" , hadtoretry );
		fprintf( stderr, " detachstate:   <%08x>\n" , attr.__detachstate );
		fprintf( stderr, " schedpolicy:   <%08x>\n" , attr.__schedpolicy );
		fprintf( stderr, " schedprior:    <%08x>\n" , (int)attr.__schedparam.__sched_priority );
		fprintf( stderr, " inheritsched:  <%08x>\n" , attr.__inheritsched );
		fprintf( stderr, " scope:         <%08x>\n" , attr.__scope );
		fprintf( stderr ," guardsize:     <%08x>\n" , attr.__guardsize );
		fprintf( stderr ," stackaddr_set: <%08x>\n" , attr.__stackaddr_set );
		fprintf( stderr ," stackaddr:     <%08x>\n" , (int) attr.__stackaddr);
		fprintf( stderr ," stacksize:     <%08x>\n" , attr.__stacksize);
		*/
	}
	pthread_attr_destroy( &attr );
	h = (FOHANDLE)malloc( sizeof( handle_t ) );
	h->handle_type = HANDLE_TYPE_THREAD;
	h->handle_value = (DWORD) lpThreadId;
	return h;
}

BOOL FoCloseHandle( FOHANDLE hObject ) {
	if (!hObject) {
		return FALSE;
	}
	switch( hObject->handle_type ) {
	case HANDLE_TYPE_MUTEX:
		pthread_mutex_destroy( (pthread_mutex_t *) hObject->handle_value );
		free( (void *) hObject->handle_value );
		free( (void *) hObject );
		break;
	case HANDLE_TYPE_SEMAPHORE:
		sem_destroy( (sem_t *)hObject->handle_value );
		free( (void *)hObject->handle_value );
		free( (void *)hObject );
		break;
	case HANDLE_TYPE_EVENT:
		pthread_mutex_destroy( (pthread_mutex_t *) hObject->aux_value );
		free( (void *)hObject->aux_value );

		pthread_cond_destroy( (pthread_cond_t *) hObject->handle_value );
		free( (void *)hObject->handle_value );
		free( (void *)hObject );
		break;
	case HANDLE_TYPE_THREAD:
		free( (void *)hObject );
		break;
	default:
		return FALSE;
	}
	return TRUE;
}

FOHANDLE FoCreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName ) {
	FOHANDLE h;
	pthread_cond_t *cvp;
	pthread_mutex_t *mp;

	if (lpEventAttributes) {
		return NULL;
	}
	if (lpName) {
		return NULL;
	}

	h = NULL;
	cvp = NULL;
	mp = NULL;

	h = (FOHANDLE)malloc( sizeof( handle_t ) );
	memset( h , 0 , sizeof( handle_t ) );

	cvp = (pthread_cond_t *) malloc( sizeof( pthread_cond_t ) );
	if (cvp)
		memset( cvp , 0 , sizeof( pthread_cond_t ));

	pthread_cond_init( cvp , NULL );

	mp = (pthread_mutex_t *) malloc( sizeof( pthread_mutex_t ) );
	if (mp)
		memset( mp , 0 , sizeof( pthread_mutex_t ) );
	pthread_mutex_init( mp , NULL  );

	h->handle_type = HANDLE_TYPE_EVENT;
	h->handle_value = (DWORD)cvp;
	h->aux_value = (DWORD)mp;
	h->flags = bManualReset;
	h->reserved = bInitialState;
	return h;
}

DWORD FoWaitForSingleObject( FOHANDLE hHandle , DWORD dwMilliseconds ) {
	DWORD retval;

	retval = WAIT_OBJECT_0;

	if ( !hHandle ) {
		return WAIT_ABANDONED;
	}
	if (	hHandle->handle_type != HANDLE_TYPE_MUTEX &&
			hHandle->handle_type != HANDLE_TYPE_EVENT &&
			hHandle->handle_type != HANDLE_TYPE_SEMAPHORE ) {
		return WAIT_ABANDONED;
	}
	switch (hHandle->handle_type) {
		case HANDLE_TYPE_MUTEX:
			if (!dwMilliseconds)
				retval = ( pthread_mutex_trylock( (pthread_mutex_t *)hHandle->handle_value) == 0 ? WAIT_OBJECT_0 : WAIT_TIMEOUT );
			else
				pthread_mutex_lock( (pthread_mutex_t *)hHandle->handle_value );
			break;
		case HANDLE_TYPE_SEMAPHORE:
			if (!dwMilliseconds)
				retval = ( sem_trywait( (sem_t *)hHandle->handle_value ) == 0 ? WAIT_OBJECT_0 : WAIT_TIMEOUT );
			else
				sem_wait( (sem_t *)hHandle->handle_value );
			break;
		case HANDLE_TYPE_EVENT:
			pthread_mutex_lock( (pthread_mutex_t *)hHandle->aux_value );
			if (dwMilliseconds != INFINITE ) {
				struct timespec t;
				t.tv_sec = time( NULL ) + dwMilliseconds/1000;
				t.tv_nsec = 10000000;
				if ( !hHandle->flags || !hHandle->reserved ) {
					if ( pthread_cond_timedwait(
								(pthread_cond_t *)hHandle->handle_value,
								(pthread_mutex_t *)hHandle->aux_value ,
								((const struct timespec *)&t) ) != 0 )
						retval = WAIT_TIMEOUT;
				}
				if ( hHandle->flags ) {
					if ( retval == WAIT_OBJECT_0 ) {
						hHandle->reserved=1;
						pthread_cond_broadcast( (pthread_cond_t *)hHandle->handle_value );
					}
				} else
					hHandle->reserved=0;
			} else {
				if ( !hHandle->flags || !hHandle->reserved )
					pthread_cond_wait( (pthread_cond_t *)hHandle->handle_value, (pthread_mutex_t *)hHandle->aux_value );
				if ( hHandle->flags ) {
					pthread_cond_broadcast( (pthread_cond_t *)hHandle->handle_value );
				} else
					hHandle->reserved = 0;
			}
			pthread_mutex_unlock( (pthread_mutex_t *)hHandle->aux_value );
			break;
	}
	return retval;
}

BOOL FoSetEvent( FOHANDLE hEvent ) {
	if ( !hEvent ) {
		return FALSE;
	}
	if ( hEvent->handle_type != HANDLE_TYPE_EVENT ) {
		return FALSE;
	}
	pthread_mutex_lock( (pthread_mutex_t *) hEvent->aux_value );
	hEvent->reserved = 1;
	pthread_cond_broadcast( (pthread_cond_t *) hEvent->handle_value );
	pthread_mutex_unlock( (pthread_mutex_t *) hEvent->aux_value );
	return TRUE;
}

BOOL FoResetEvent( FOHANDLE hEvent ) {
	if ( !hEvent ) {
		return FALSE;
	}
	if ( hEvent->handle_type != HANDLE_TYPE_EVENT ) {
		return FALSE;
	}
	pthread_mutex_lock( (pthread_mutex_t *)hEvent->aux_value );
	hEvent->reserved = 0;
	pthread_cond_broadcast( (pthread_cond_t *)hEvent->handle_value );
	pthread_mutex_unlock( (pthread_mutex_t *)hEvent->aux_value );
	return TRUE;
}

FOHANDLE FoCreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR lpName ) {
	FOHANDLE h;
	pthread_mutex_t *mp;

	if (lpMutexAttributes) {
		return NULL;
	}
	if (lpName) {
		return NULL;
	}
	h = NULL;
	mp = NULL;
	h = (FOHANDLE)malloc( sizeof( handle_t ) );
	memset( h , 0 , sizeof( handle_t ) );
	mp = (pthread_mutex_t *)malloc( sizeof( pthread_mutex_t ) );
	if (mp)
		memset( mp , 0 , sizeof( pthread_mutex_t ));

	pthread_mutex_init( mp , NULL );
	h->handle_type = HANDLE_TYPE_MUTEX;
	h->handle_value = (DWORD)mp;

	if (bInitialOwner)
		FoWaitForSingleObject( h, INFINITE );
	return h;
}

BOOL FoReleaseMutex( FOHANDLE hMutex ) {
	if (!hMutex) {
		return FALSE;
	}
	if (hMutex->handle_type != HANDLE_TYPE_MUTEX ) {
		return FALSE;
	}
	pthread_mutex_unlock( (pthread_mutex_t *)hMutex->handle_value );
	return TRUE;
}
