/*
 *  Copyright 2016 MZ Automation GmbH
 *
 *  This file is part of lib60870-C
 *
 *  lib60870-C is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  lib60870-C is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with lib60870-C.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  See COPYING file for the complete license text.
 */

#include <windows.h>

#include "lib_memory.h"
#include "hal_thread.h"

struct sThread {
	ThreadExecutionFunction function;
	void* parameter;
	HANDLE handle;
	int state;
	bool autodestroy;
};

static DWORD WINAPI
destroyAutomaticThreadRunner(LPVOID parameter)
{
	Thread thread = (Thread) parameter;

	thread->function(thread->parameter);

	thread->state = 0;

	Thread_destroy(thread);

	return 0;
}

static DWORD WINAPI
threadRunner(LPVOID parameter)
{
	Thread thread = (Thread) parameter;

	return (UINT) thread->function(thread->parameter);
}

Thread
Thread_create(ThreadExecutionFunction function, void* parameter, bool autodestroy)
{
	DWORD threadId;
	Thread thread = (Thread) GLOBAL_MALLOC(sizeof(struct sThread));

	thread->parameter = parameter;
	thread->function = function;
	thread->state = 0;
	thread->autodestroy = autodestroy;

	if (autodestroy == true)
		thread->handle = CreateThread(0, 0, destroyAutomaticThreadRunner, thread, CREATE_SUSPENDED, &threadId);
	else
		thread->handle = CreateThread(0, 0, threadRunner, thread, CREATE_SUSPENDED, &threadId);

	return thread;
}

void
Thread_start(Thread thread)
{
	thread->state = 1;
	ResumeThread(thread->handle);
}

void
Thread_destroy(Thread thread)
{
	if (thread->state == 1)
		WaitForSingleObject(thread->handle, INFINITE);

	CloseHandle(thread->handle);

	GLOBAL_FREEMEM(thread);
}

void
Thread_sleep(int millies)
{
	Sleep(millies);
}

Semaphore
Semaphore_create(int initialValue)
{
    HANDLE self = CreateSemaphore(NULL, 1, 1, NULL);

    return self;
}

/* Wait until semaphore value is greater than zero. Then decrease the semaphore value. */
void
Semaphore_wait(Semaphore self)
{
    WaitForSingleObject((HANDLE) self, INFINITE);
}

void
Semaphore_post(Semaphore self)
{
    ReleaseSemaphore((HANDLE) self, 1, NULL);
}

void
Semaphore_destroy(Semaphore self)
{
    CloseHandle((HANDLE) self);
}
