/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


	#if		__PLATFORM_WINDOWS__
		#define		WIN32_LEAN_AND_MEAN
		#include <windows.h>
		#include <process.h>
	#elif	__PLATFORM_POSIX__
		#include <pthread.h>
	#endif
		#include <string.h>
		#include "thread/thread.h"
		#include "assert/nassert.h"
		#include "log/log.h"
		#include <processthreadsapi.h>
		#include <string>


#if		__PLATFORM_WINDOWS__

namespace GS {
	namespace Threading {

//------------------------------------------------------------------------------
static unsigned __stdcall win32_thread_entrypoint(void *parm)
{
	Thread *t = (Thread *)parm;
	__ASSERT__(t != NULL);
	t->Execute();
	return 1;
}
void				Thread::Join()
{
	if	(handle != 0)
	{
		WaitForSingleObject((HANDLE)handle, INFINITE);

		CloseHandle((HANDLE)handle);
		handle = 0;
	}
}
bool				Thread::Start()
{
	handle = (void *)_beginthreadex(NULL, 0, &win32_thread_entrypoint, (void *)this, 0, NULL);
	return handle != 0;
}
bool				Thread::SetPriority(int)
{
	return false;
}
void				Thread::Kill()
{
	if	(handle != 0)
	{
		CloseHandle((HANDLE)handle);
		handle = 0;
	}
}
void Thread::SetName(const char *name)
{
    if (name == nullptr)
        return;

    // Conversion UTF-8 (ou ANSI selon votre projet) vers UTF-16
    int length = MultiByteToWideChar(CP_UTF8, 0, name, -1, nullptr, 0);
    if (length == 0)
        return;

    std::wstring wname(length, L'\0');
    MultiByteToWideChar(CP_UTF8, 0, name, -1, &wname[0], length);

    SetThreadDescription(GetCurrentThread(), wname.c_str());
}
void				Thread::Switch()
{	SwitchToThread();	}

Thread::Thread()
{	handle = 0;	}
Thread::~Thread()
{	Kill();	}

	}
}
//------------------------------------------------------------------------------

#elif	__PLATFORM_EMSCRIPTEN__

namespace GS {
	namespace Threading {

//------------------------------------------------------------------------------
void				*pthread_execute(void *parm)
{	return NULL;	}
void				Thread::Join()
{}
bool				Thread::Start()
{	return false;	}
bool				Thread::SetPriority(int priority)
{	return false;	}
void				Thread::Kill()
{}
void				Thread::SetName(const char *)
{}
void				Thread::Switch()
{}
Thread::Thread()
{}
Thread::~Thread()
{}

	}
}
//------------------------------------------------------------------------------

#elif	__PLATFORM_POSIX__

namespace GS {
	namespace Threading {

//------------------------------------------------------------------------------
void				*pthread_execute(void *parm)
{
	Thread *t = (Thread * const)parm;
	t->Execute();
	return NULL;
}
void				Thread::Join()
{
	pthread_join(*((pthread_t *)handle), NULL);
}
bool				Thread::Start()
{
	if	(!(handle = (void *)new pthread_t))
		return false;
	return pthread_create((pthread_t *)handle, NULL, pthread_execute, this) == 0;
}
bool				Thread::SetPriority(int priority)
{
#ifdef	EMSCRIPTEN
	return false;
#else
	if	(!handle)
		return false;

	sched_param	param;
	memset(&param, 0, sizeof(param));
	param.sched_priority = priority;

	return asbool(pthread_setschedparam(*((pthread_t *)handle), SCHED_OTHER, &param) == 0);
#endif
}
void				Thread::Kill()
{
#if		__PLATFORM_ANDROID_NDK__
	__ASSERT_MSG__(true, "Android NDK cannot kill a thread.");
#else
	if	(handle)
		pthread_cancel(*((pthread_t *)handle));

	delete (pthread_t *)handle;
	handle = NULL;
#endif
}
void				Thread::SetName(const char *)
{}
void				Thread::Switch()
{	sched_yield();	}
Thread::Thread()
{	handle = NULL;	}
Thread::~Thread()
{	delete (pthread_t *)handle;	}

	}
}
//------------------------------------------------------------------------------

#elif		__PLATFORM_NINTENDO_WII__

namespace GS {
	namespace Threading {

//------------------------------------------------------------------------------
void				*__OSthread_wii_execute(void *parm)
{
	nThread *t = (nThread * const)parm;
	t->alive.Set(1);
	return (void *)(t->Execute() ? 1 : 0);
}
void				Thread::Join()
{
	void *rv;
	if	(alive.Get())
		OSJoinThread(&thread, &rv);
	alive.Set(0);
}
void				Thread::Resume()
{
	if	(alive.Get())
		OSResumeThread(&thread);
}
void				Thread::Suspend()
{
	if	(alive.Get())
		OSSuspendThread(&thread);
}
bool				Thread::Start()
{	return OSCreateThread(&thread, &__OSthread_wii_execute, this, thread_stack + 32768, 32768, 20, 0);	}
bool				Thread::SetPriority(int priority)
{	return alive.Get() ? OSSetThreadPriority(&thread, priority) : false;	}
void				Thread::Kill()
{
	if	(alive.Get())
		OSCancelThread(&thread);
	alive.Set(0);
}
void				Thread::SetName(const char *)
{}
Thread::Thread()
{}
Thread::~Thread()
{	Kill();	}
//------------------------------------------------------------------------------

	}
}

#endif
