/*------------------------------------------------------------------------*/
/* Copyright (C) SIEMENS CORP., 2018 All rights reserved.*/
/* All rights reserved.                                                   */
/* Redistribution and use in source and binary forms, with or without     */
/* modification, are permitted provided that the following conditions     */
/* are met:                                                               */
/* 1. Redistributions of source code must retain the above copyright      */
/* notice, this list of conditions and the following disclaimer.          */
/* 2. Redistributions in binary form must reproduce the above copyright   */
/* notice, this list of conditions and the following disclaimer in the    */
/* documentation and/or other materials provided with the distribution.   */
/* 3. Neither the name of the copyright holder nor the names of its       */
/* contributors may be used to endorse or promote products derived from   */
/* this software without specific prior written permission.               */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS      */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE         */
/* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,   */
/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,   */
/* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  */
/* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)                    */
/* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,    */
/* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING  */
/* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE     */
/* POSSIBILITY OF SUCH DAMAGE.                                            */
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
/* Attention : Callbacks are running concurrent in other threads so all   */
/*             printf statements should be synchronized. But this sample  */
/*             application doesn't synchronize for simplicity  !          */
/*------------------------------------------------------------------------*/

#include "os/thread.h"

#include <stdlib.h>

#if defined(_WIN32)
#include <Windows.h>
#elif defined(__unix)
#include <pthread.h>
#endif

typedef struct OsThread
{
#if defined(_WIN32)
	HANDLE win_thread;
#elif defined(__unix)
	pthread_t lin_thread;
#endif
}OsThread;

typedef struct
{
	ThreadRoutine pFunc;
	void* pParam;
}ThreadData;

/* os dependent wrapper function */
#if defined(_WIN32)
DWORD _stdcall threadFunc(void* pParam)
#elif defined(__unix)
void* threadFunc(void* pParam)
#endif
{
	ThreadData* tempData = (ThreadData*)pParam;
	ThreadRoutine pFunc = tempData->pFunc;
	void* pRemoteParam = tempData->pParam;

	free(tempData);

	pFunc(pRemoteParam);

	return 0;
}

/* start a thread */
void ThreadStart(Thread* thread, ThreadRoutine pFunc, void* pParam)
{
#if defined(_WIN32)
	HANDLE thread_handle;
#elif defined(__unix)
	pthread_t thread_id;
#endif

	if (pFunc != 0)
	{
		ThreadData* data = (ThreadData*)malloc(sizeof(ThreadData));
		data->pFunc = pFunc;
		data->pParam = pParam;

#if defined(_WIN32)
		thread_handle = CreateThread(0, 0, threadFunc, data, 0, 0);
		if (thread != 0)
		{
			thread->os_thread = (OsThread*)malloc(sizeof(OsThread*));
			thread->os_thread->win_thread = thread_handle;
		}
		else
		{
			CloseHandle(thread_handle);
		}
#elif defined(__unix)
		pthread_create(&thread_id, 0, threadFunc, data);
		if (thread != 0)
		{
			thread->os_thread = (OsThread*)malloc(sizeof(OsThread*));
			thread->os_thread->lin_thread = thread_id;
		}
#endif
	}
}

/* wait for a thread to finish */
void ThreadJoin(Thread* thread)
{
#if defined(_WIN32)
	WaitForSingleObject(thread->os_thread->win_thread, INFINITE);
#elif defined(__unix)
	pthread_join(thread->os_thread->lin_thread, 0);
#endif
}

/* delete a thread object (does not end the thread) */
void ThreadFree(Thread* thread)
{
#if defined(_WIN32)
	CloseHandle(thread->os_thread->win_thread);
#endif
	free(thread->os_thread);
}
