/// Copyright (c) 2010 Ruben Penalva. 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. The name of the author may not be used to endorse or promote products derived
///    from this software without specific prior written permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
#include "ThreadPool.h"

#include "Exception.h"
#include <boost\scoped_array.hpp>
#include "Win32LastError.h"
#include "ITask.h"
#include "TaskContext.h"

namespace
{
	size_t PopulationCount32bit(unsigned int x)
	{
		x = x - ((x >> 1) & 0x55555555);
		x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
		x = (x + (x >> 4)) & 0x0f0f0f0f;
		x = x + (x >> 8);
		x = x + (x >> 16);
		
		return x & 0x0000003f;
	}

	/// Hackers Delight, Chapter 5
	size_t PopulationCountArray(ULONG_PTR bitMask)
	{
#if _WIN64
		size_t bitsSetCount = 0;

		/// word = 32bit
		size_t wordsCount = sizeof(ULONG_PTR) / 4;
		

		for (size_t i = 0; i < wordsCount; ++i, bitMask = bitMask >> 32)
		{
			bitsSetCount += PopulationCount32bit(static_cast<unsigned int>(bitMask));
		}
#else
        size_t bitsSetCount = PopulationCount32bit(static_cast<unsigned int>(bitMask));
#endif


		return bitsSetCount;
	}

	size_t HardwareThreadsCount(void)
	{
		boost::scoped_array<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> logicalPocessorsInfo;
		DWORD logicalPocessorsInfoCount = 0;

		/// The expected result is FALSE as we are querying the logicalPocessorsInfoCount
		if (GetLogicalProcessorInformation(0, &logicalPocessorsInfoCount) != FALSE)
		{
			throw TestFrameWork::Exception();
		}

		TestFrameWork::Win32LastError lastError;
		if (lastError.GetErrorCode() != ERROR_INSUFFICIENT_BUFFER)
		{
			throw TestFrameWork::Exception();
		}
		
		logicalPocessorsInfo.reset(new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[logicalPocessorsInfoCount]);
		if (GetLogicalProcessorInformation(logicalPocessorsInfo.get(), &logicalPocessorsInfoCount) != TRUE)
		{
			throw TestFrameWork::Exception();
		}

		int processorPackageCount = 0;
		size_t hwThreadsCount = 0;
		for (size_t i = 0; i < logicalPocessorsInfoCount; ++i)
		{
			switch (logicalPocessorsInfo[i].Relationship) 
			{
				case RelationProcessorCore:
					hwThreadsCount += PopulationCountArray(logicalPocessorsInfo[i].ProcessorMask);
					break;

				case RelationProcessorPackage:
					// Logical processors share a physical package.
					processorPackageCount++;
					break;

				default:
					break;
			}
		}

		return hwThreadsCount;
	}
}

using namespace TaskBasedFlockSim;

namespace TaskBasedFlockSim
{

VOID CALLBACK TaskCallback(PTP_CALLBACK_INSTANCE instance, PVOID context)
{
	if (!context)
	{
		return;
	}

	ThreadPool::TaskCallbackContext* taskCallBackContext((ThreadPool::TaskCallbackContext*)context);

	taskCallBackContext->m_task->DoWork();

	if (!taskCallBackContext->m_context->DecrementCompletedTasksCount())
	{
		taskCallBackContext->m_context->SignalTasksCompleted(instance);
		taskCallBackContext->m_threadPool->m_taskCallbacksContext.clear();
	}
}

}

ThreadPool::ThreadPool(void)	:	m_pool(CreateThreadpool(NULL))
{
	if (!m_pool)
	{
		throw TestFrameWork::Exception();
	}

	SetDefaultThreadsCount();
}

ThreadPool::ThreadPool(size_t minThreadsCount, size_t maxThreadsCount)	:	m_pool(CreateThreadpool(NULL))
{
	if (!m_pool)
	{
		throw TestFrameWork::Exception();
	}

	SetThreadsCount(minThreadsCount, maxThreadsCount);
}

ThreadPool::~ThreadPool(void)
{
	CloseThreadpool(m_pool);
}

void ThreadPool::SubmitTasks(TaskContext* context)
{
	{
		const Tasks& tasks = context->GetTasks();
		Tasks::const_iterator it = tasks.begin();
		Tasks::const_iterator itEnd = tasks.end();

		while (it != itEnd)
		{
			TaskCallbackContext taskCallbackContext(this, (*it).get(), context);
			m_taskCallbacksContext.push_back(taskCallbackContext);

			++it;
		}
	}

	size_t taskCallbackContextsCount = m_taskCallbacksContext.size();
	for (size_t i = 0; i < taskCallbackContextsCount; ++i)
	{	
		if (TrySubmitThreadpoolCallback(TaskCallback, (void*)&m_taskCallbacksContext[i], 0) != TRUE)
		{
			throw TestFrameWork::Exception();
		}
	}
}

void ThreadPool::SetDefaultThreadsCount(void)
{
	size_t hwThreadsCount = HardwareThreadsCount();

	SetThreadsCount(hwThreadsCount, hwThreadsCount);
}

void ThreadPool::SetThreadsCount(size_t minThreadsCount, size_t maxThreadsCount)
{
	if (SetThreadpoolThreadMinimum(m_pool, static_cast<DWORD>(minThreadsCount)) != TRUE)
	{
		throw TestFrameWork::Exception();
	}

	if (SetThreadpoolThreadMinimum(m_pool, static_cast<DWORD>(maxThreadsCount)) != TRUE)
	{
		throw TestFrameWork::Exception();
	}
}

