#pragma once

#include <windows.h>
#include <bfc/platform/types.h>
#include <vector>
#include "../autolock.h"
#include "ThreadID.h"
#include "ThreadFunctions.h"
#include "threadpool_types.h"
/* random notes 

HANDLEs common to all threads

WaitForMultipleObjectsEx() around these
0 - killswitch
1 - shared APC event.  since threads might want to use APCs themselves, we'll use a different mechanism (thread-safe FIFO and an event).  the intention is that APCs that can go on any thread will use this handle
2 - per thread APC event.


parameters for "run my function" method
function pointer, user data, flags
flags:
interrupt - for very short non-locking functions where it is safe to interrupt another thread, uses QueueUserAPC
no_wait - spawn a new thread if all threads are busy
com_multithreaded - all threads are created with CoInitialize(0), if you need a COINIT_MULTITHREADED thread, use this flag

parameters for "add my handle" method
handle, function pointer, user data, flags
flags:
single_thread - only one thread in the pool will wait on your object, useful if your handle is not auto-reset

parameters for "function call repeat" - calls your function until you return 0
function pointer, user data, flags
flags:
single_thread - keep calling on the same thread
*/


class ThreadPool : public api_threadpool
{
public:
	static const char *getServiceName() { return "Thread Pool API"; }
	static const GUID getServiceGuid() { return ThreadPoolGUID; }	
public:
	// Owner API:
	ThreadPool();
	void Kill();

	// User API:
	/* If you have multiple events, APCs, etc and you need them to always run on the same thread
	you can reserve one */
	ThreadID *ReserveThread(int flags);
	/* Release a thread you've previously reserved */
	void ReleaseThread(ThreadID *thread_id);

	/* adds a waitable handle to the thread pool.  when the event is signalled, your function ptr will get called 
	user_data and id values get passed to your function.
	your function should return 1 to indicate that it can be removed
	flags, see api_threadpool	*/
	int AddHandle(ThreadID *threadid, HANDLE handle, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);
	void RemoveHandle(ThreadID *threadid, HANDLE handle);
	int RunFunction(ThreadID *threadid, api_threadpool::ThreadPoolFunc func, void *user_data, intptr_t id, int flags);

	size_t GetNumberOfThreads(); // total number of threads in the threadpool
	size_t GetNumberOfActiveThreads(); // number of threads that are currently being used (inside user function but not necessarily busy)

private:
	enum
	{
		TYPE_MT = 0,
		TYPE_STA = 1,
		TYPE_MT_RESERVED = 2,
		TYPE_STA_RESERVED = 3,		

		THREAD_TYPES = 4, // two thread types, single threaded apartment COM and multithreaded COM
	};
private:
	static DWORD CALLBACK WatchDogThreadProcedure_stub(LPVOID param);
	ThreadID *CreateNewThread_Internal(int thread_type = 0);
	DWORD CALLBACK WatchDogThreadProcedure();
	static int GetThreadType(int flags, int reserved = 0);
	static void GetThreadTypes(int flags, bool types[THREAD_TYPES]);
	void RemoveHandle_Internal(size_t start, HANDLE handle); // recursive helper function for RemoveHandle()
	void AddHandle_Internal(size_t start, HANDLE handle, int flags); // recursive helper function for RemoveHandle()

	Nullsoft::Utility::LockGuard guard; // guards threads, any_thread_handles, and non_reserved_handles data structures
	typedef std::vector<ThreadID*> ThreadList;
	ThreadList threads;
	ThreadPoolTypes::HandleList any_thread_handles[THREAD_TYPES];
	HANDLE killswitch;
	HANDLE watchdog_thread_handle;
	volatile LONG num_threads_available[THREAD_TYPES];
	ThreadFunctions thread_functions;
	HANDLE max_load_event[THREAD_TYPES];
protected:
	RECVS_DISPATCH;
};