#include "deThread_priv.hpp"

deThreadPool* deThreadPool::mInstance = 0;

IdeThreadPool* IdeThreadPool_GetPtr(u32 minThreadCount)
{
	if(!deThreadPool::mInstance)
		deThreadPool::mInstance = new deThreadPool(minThreadCount);
	return deThreadPool::mInstance;
}

void IdeThreadPool_Destroy()
{
	delete deThreadPool::mInstance;
	deThreadPool::mInstance = 0;
}

deThreadPool::deThreadPool(u32 minStartThreadCount)
:	mLock(IdeThreadLock_CreateThreadLock()) 
{
}

deThreadPool::~deThreadPool()
{
	mLock->Lock();
	ThreadPoolIterator start = mThreads.begin();
	ThreadPoolIterator end = mThreads.end();
	while(start != end) {
		RemoveThread(*start);
		++start;
	}
	mLock->Unlock();
	mLock->Release();
}

void deThreadPool::QueueUserWorkItem(IdeThread::ThreadProc function, void* parameter)
{
	mLock->Lock();
	ThreadPoolItem* item = GetAvailableThread();
	if(!item) {
		item = CreateNewThread();
	}
	item->workerFunction = function;
	item->param = parameter;
	ResetEvent(item->doneSignal); //in use
	SetEvent(item->workAvailableSignal); //tell thread to run.
	mLock->Unlock();
}

u32 deThreadPool::GetAvailableThreadCount()
{
	u32 count = 0;
	mLock->Lock();
	ThreadPoolIterator start = mThreads.begin();
	ThreadPoolIterator end = mThreads.end();
	while(start != end) {
		if(IsThreadAvailable(*start))
			count++;
		++start;
	}
	mLock->Unlock();
	return count;
}

u32 deThreadPool::GetCurrentThreadCount()
{
	mLock->Lock();
	u32 count = mThreads.Length();
	mLock->Unlock();
	return count;
}

void deThreadPool::RemoveUnusedThreads()
{
	mLock->Lock();
	ThreadPoolIterator start = mThreads.begin();
	ThreadPoolIterator end = mThreads.end();
	while(start != end) {
		if(IsThreadAvailable(*start)) {
			RemoveThread(*start);
			start = mThreads.erase(start);
		} else {
			++start;
		}
	}
	mLock->Unlock();
}

deThreadPool::ThreadPoolItem* deThreadPool::GetAvailableThread()
{
	ThreadPoolIterator start = mThreads.begin();
	ThreadPoolIterator end = mThreads.end();
	while(start != end) {
		if(IsThreadAvailable(*start)) {
			return *start;
		}
		++start;
	}
	return 0;
}

deThreadPool::ThreadPoolItem* deThreadPool::CreateNewThread()
{
	ThreadPoolItem* item = new ThreadPoolItem;
	item->doneSignal = CreateEvent(0, TRUE, TRUE, 0);
	item->workAvailableSignal = CreateEvent(0, TRUE, FALSE, 0);
	item->workerFunction = 0;
	item->param = 0;
	item->thread = IdeThread_CreateThread(ThreadPoolWorkerThreadFunc);
	item->thread->Start(item);
	mThreads.AddElement(item);
	return item;
}

bool deThreadPool::IsThreadAvailable(ThreadPoolItem* item)
{
	DWORD retVal = WaitForSingleObject(item->doneSignal, 0);
	if(retVal == WAIT_OBJECT_0) //It's free
		return true;
	return false;
}

void deThreadPool::RemoveThread(ThreadPoolItem* item)
{
	if(IsThreadAvailable(item)) {
		item->param = 0;
		item->workerFunction = 0;
		ResetEvent(item->doneSignal);
		SetEvent(item->workAvailableSignal);
		item->thread->Release();
		delete item;
	} else {
		WaitForSingleObject(item->doneSignal, INFINITE);
		RemoveThread(item);
	}
}

//static
u32 deThreadPool::ThreadPoolWorkerThreadFunc(void* parameter)
{
	ThreadPoolItem* item = reinterpret_cast<ThreadPoolItem*>(parameter);
	while(true) {
		WaitForSingleObject(item->workAvailableSignal, INFINITE);
		if(item->workerFunction == 0)
			return 0;
		(item->workerFunction)(item->param);
		ResetEvent(item->workAvailableSignal);
		SetEvent(item->doneSignal);
	}
	return 0;
}