#include "ThreadFunctions.h"
#include "threadpool_types.h"

ThreadFunctions::ThreadFunctions(int create_function_list)
{
	if (create_function_list)
	{
		functions_semaphore = CreateSemaphore(0, 0, ThreadPoolTypes::MAX_SEMAPHORE_VALUE, 0);
		InitializeCriticalSectionAndSpinCount(&functions_guard, 200);
	}
	else
		functions_semaphore = 0;
}

ThreadFunctions::~ThreadFunctions()
{
	if (functions_semaphore)
	{
		CloseHandle(functions_semaphore);
		DeleteCriticalSection(&functions_guard);
	}
}

void ThreadFunctions::Add(HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id)
{
	Nullsoft::Utility::AutoLock l(guard);
	Data *new_data = (Data *)calloc(1, sizeof(Data));
	new_data->func = func;
	new_data->user_data = user_data;
	new_data->id = id;
	data[handle] = new_data;
}

bool ThreadFunctions::Get(HANDLE handle, api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id)
{
	Nullsoft::Utility::AutoLock l(guard);
	DataMap::iterator found = data.find(handle);
	if (found == data.end())
		return false;

	const Data *d = found->second;
	*func = d->func;
	*user_data = d->user_data;
	*id = d->id;
	return true;
}

void ThreadFunctions::QueueFunction(api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id)
{
	Data *new_data = (Data *)calloc(1, sizeof(Data));
	new_data->func = func;
	new_data->user_data = user_data;
	new_data->id = id;
	EnterCriticalSection(&functions_guard);
	functions_list.push_front(new_data);
	LeaveCriticalSection(&functions_guard); // unlock before releasing the semaphore early so we don't lock convoy
	ReleaseSemaphore(functions_semaphore, 1, 0);
}

bool ThreadFunctions::PopFunction(api_threadpool::ThreadPoolFunc *func, void **user_data, intptr_t *id)
{
	EnterCriticalSection(&functions_guard);
	if (!functions_list.empty())
	{
		ThreadFunctions::Data *data = functions_list.back();
		functions_list.pop_back();
		LeaveCriticalSection(&functions_guard);
		*func = data->func;
		*user_data = data->user_data;
		*id = data->id;
		free(data);
		return true;
	}
	else
	{
		LeaveCriticalSection(&functions_guard);
		return false;
	}
}