//============================================================
//
//  sdlsync.c - SDL core synchronization functions
//
//  Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
//  Visit http://mamedev.org for licensing and usage restrictions.
//
//  SDLMAME by Olivier Galibert and R. Belmont
//
//============================================================

#include "SDL/SDL.h"

// standard C headers
#include <unistd.h>

// MAME headers
#include "osdcore.h"
#include "osinline.h"
#include "sdlsync.h"

#include "eminline.h"

#define	VERBOSE		(0)

#if VERBOSE
#define LOG( x ) do { printf x; printf("\n"); } while (0)
#else
#define LOG( x ) 
#endif
typedef struct _hidden_mutex_t hidden_mutex_t;
struct _hidden_mutex_t {
	SDL_mutex *			id;
	volatile INT32		locked;
	volatile INT32		threadid;
};
 
struct _osd_event {
	SDL_mutex *		 	mutex;
	SDL_cond *	 		cond;
	volatile INT32		autoreset;
	volatile INT32		signalled;
};

//============================================================
//  TYPE DEFINITIONS
//============================================================

struct _osd_thread {
	SDL_Thread *		thread;
	osd_thread_callback callback;
	void *param;
};

struct _osd_scalable_lock
{
	SDL_mutex *		 	mutex;
};

//============================================================
//  Scalable Locks
//============================================================

osd_scalable_lock *osd_scalable_lock_alloc(void)
{
	osd_scalable_lock *lock;
	
	lock = (osd_scalable_lock *)calloc(1, sizeof(*lock));

	lock->mutex = SDL_CreateMutex();
	return lock;
}


INT32 osd_scalable_lock_acquire(osd_scalable_lock *lock)
{
	SDL_mutexP(lock->mutex);
	return 0;
}


void osd_scalable_lock_release(osd_scalable_lock *lock, INT32 myslot)
{
	SDL_mutexV(lock->mutex);
}

void osd_scalable_lock_free(osd_scalable_lock *lock)
{
	SDL_DestroyMutex(lock->mutex);
	free(lock);
}

//============================================================
//  osd_lock_alloc
//============================================================

osd_lock *osd_lock_alloc(void)
{
	hidden_mutex_t *mutex;

	mutex = (hidden_mutex_t *)calloc(1, sizeof(hidden_mutex_t));

	mutex->id = SDL_CreateMutex();

	return (osd_lock *)mutex;
}

//============================================================
//  osd_lock_acquire
//============================================================

void osd_lock_acquire(osd_lock *lock)
{
	hidden_mutex_t *mutex = (hidden_mutex_t *) lock;

	LOG(("osd_lock_acquire"));
	/* get the lock */
	mutex->locked++; /* signal that we are *about* to lock - prevent osd_lock_try */
	SDL_mutexP(mutex->id);
	mutex->threadid = SDL_ThreadID();
}

//============================================================
//  osd_lock_try
//============================================================

int osd_lock_try(osd_lock *lock)
{
	hidden_mutex_t *mutex = (hidden_mutex_t *) lock;

	LOG(("osd_lock_try"));
	if (mutex->locked && mutex->threadid == SDL_ThreadID())
	{
		/* get the lock */
		SDL_mutexP(mutex->id);
		mutex->locked++;
		mutex->threadid = SDL_ThreadID();
		return 1;
	}
	else if ((mutex->locked == 0))
	{
		/* get the lock */
		mutex->locked++;
		SDL_mutexP(mutex->id);
		mutex->threadid = SDL_ThreadID();
		return 1;
	}
	else
	{
		/* fail */
		return 0;
	}
}

//============================================================
//  osd_lock_release
//============================================================

void osd_lock_release(osd_lock *lock)
{
	hidden_mutex_t *mutex = (hidden_mutex_t *) lock;

	LOG(("osd_lock_release"));
	mutex->locked--;
	if (mutex->locked == 0)
		mutex->threadid = -1;
	SDL_mutexV(mutex->id);
}

//============================================================
//  osd_lock_free
//============================================================

void osd_lock_free(osd_lock *lock)
{
	hidden_mutex_t *mutex = (hidden_mutex_t *) lock;

	LOG(("osd_lock_free"));
	//osd_lock_release(lock);
	SDL_DestroyMutex(mutex->id);
	free(mutex);
}

//============================================================
//  osd_event_alloc
//============================================================

osd_event *osd_event_alloc(int manualreset, int initialstate)
{
	osd_event *ev;

	ev = (osd_event *)calloc(1, sizeof(osd_event));

	ev->mutex = SDL_CreateMutex();
	ev->cond = SDL_CreateCond();
	ev->signalled = initialstate;
	ev->autoreset = !manualreset;
		
	return ev;
}

//============================================================
//  osd_event_free
//============================================================

void osd_event_free(osd_event *event)
{
	SDL_DestroyMutex(event->mutex);
	SDL_DestroyCond(event->cond);
	free(event);
}

//============================================================
//  osd_event_set
//============================================================

void osd_event_set(osd_event *event)
{
	LOG(("osd_event_set"));
	SDL_mutexP(event->mutex);
	if (event->signalled == FALSE)
	{
		event->signalled = TRUE;
		if (event->autoreset)
			SDL_CondSignal(event->cond);
		else
			SDL_CondBroadcast(event->cond);
	}
	SDL_mutexV(event->mutex);
}

//============================================================
//  osd_event_reset
//============================================================

void osd_event_reset(osd_event *event)
{
	LOG(("osd_event_reset"));
	SDL_mutexP(event->mutex);
	event->signalled = FALSE;
	SDL_mutexV(event->mutex);
}

//============================================================
//  osd_event_wait
//============================================================

int osd_event_wait(osd_event *event, osd_ticks_t timeout)
{
	LOG(("osd_event_wait"));
	SDL_mutexP(event->mutex);
	if (!timeout)
	{
		if (!event->signalled)
		{
			SDL_mutexV(event->mutex);
				return FALSE;
		}
	}
	else
	{
		if (!event->signalled)
		{
			UINT64 msec = (timeout * 1000) / osd_ticks_per_second();
			
			do {
				int ret = SDL_CondWaitTimeout(event->cond, event->mutex, msec);
				if ( ret == SDL_MUTEX_TIMEDOUT )
				{
					if (!event->signalled)
					{
						SDL_mutexV(event->mutex);
						return FALSE;
					}
					else
						break;
				}
				if (ret == 0)
					break;
				printf("Error %d while waiting for pthread_cond_timedwait:  %s\n", ret, strerror(ret));
			} while (TRUE);
		}
	}

	if (event->autoreset)
		event->signalled = 0;

	SDL_mutexV(event->mutex);

	return TRUE;
}

//============================================================
//  osd_thread_create
//============================================================

static int worker_thread_entry(void *param)
{
	osd_thread *thread = (osd_thread *) param;
	void *res;
//#ifdef PTR64
	res = thread->callback(thread->param);
#ifdef PTR64
	return (int) (INT64) res;
#else
	return (int) res;
#endif
}

osd_thread *osd_thread_create(osd_thread_callback callback, void *cbparam)
{
	osd_thread *thread;

	thread = (osd_thread *)calloc(1, sizeof(osd_thread));
	thread->callback = callback;
	thread->param = cbparam;
	thread->thread = SDL_CreateThread(worker_thread_entry, thread);
	if ( thread->thread == NULL )
	{
		free(thread);
		return NULL;
	}
	return thread;
}

//============================================================
//  osd_thread_adjust_priority
//============================================================

int osd_thread_adjust_priority(osd_thread *thread, int adjust)
{
	return TRUE;
}

//============================================================
//  osd_thread_cpu_affinity
//============================================================

int osd_thread_cpu_affinity(osd_thread *thread, UINT32 mask)
{
	return TRUE;
}

//============================================================
//  osd_thread_wait_free
//============================================================

void osd_thread_wait_free(osd_thread *thread)
{
	int status;
	SDL_WaitThread(thread->thread, &status);
	free(thread);
}

