#include <process.h>
#include <queue>
#include <iostream>

#define zprintf printf

/* *** ZRequest class *** */
class ZRequest
{
public:
	ZRequest () {};
	virtual ~ZRequest() {};
};
/* *** End ZRequest class *** */

/* *** ZReqProvider class *** */
class ZReqProvider
{
	HANDLE queue_mutex;
	HANDLE request_semaphore;
	std::queue<ZRequest*> requests;
public:
	ZReqProvider();
	~ZReqProvider();
	BYTE request_add (ZRequest* r);
    ZRequest* request_get();
protected:
	void CloseSyncObjects();
};
/* *** End ZReqProvider class *** */

/* *** ZThread class *** */
class ZThread {
	HANDLE stop_mutex;
	HANDLE handle;
	volatile BOOL stop_thread;
	static void __cdecl start(void* data);
	ZReqProvider* request_provider;
	void run();
public:
	template<class T> static T* create(ZReqProvider* rp);
	HANDLE get_handle() { return handle; }
	~ZThread();
	BYTE stop();
protected:
	ZThread();
	BOOL should_stop();
	virtual void handle_request(ZRequest* r) { delete r; }
};

/* *** Template implementation *** */
template<class T> static T* ZThread::create(ZReqProvider* rp){
	T* t = new T();
	t->stop_thread = FALSE;
	t->request_provider = rp;
	HANDLE h = (HANDLE)_beginthread(ZThread::start, 0, t);
	t->handle = h;
	if (h == (HANDLE)-1) {
		delete t;
		return NULL;
	}
	return t;
}
/* *** End ZThread class *** */

/* *** ZThreadPool class *** */
template<class T> class ZThreadPool : public ZReqProvider
{
std::queue <ZThread*> pool;
HANDLE* thread_handles;
public:
	ZThreadPool(WORD min_threads = 1);
	~ZThreadPool();
};

/* *** Template implementation *** */
template<class T> ZThreadPool<T>::ZThreadPool(WORD min_threads) : ZReqProvider()
{
	T* t;
	for (WORD i=0; i< min_threads; ++i) {
		t = ZThread::create<T>(this);
		if (t)
		{
			pool.push(t);
		}
	}
}

template<class T> ZThreadPool<T>::~ZThreadPool()
{
	DWORD nbThreads = static_cast<WORD>(pool.size());
	HANDLE* thread_handles_tab = new HANDLE[nbThreads*sizeof(HANDLE)];
	WORD i = 0;
	// On dit au threads qu'ils peuvent s'arrêter
	while (!pool.empty()) {
		ZThread* t = pool.front();
		thread_handles_tab[i++] = t->get_handle();
		t->stop();
		pool.pop();
	}
	// Pour suppression des semaphores et mutexes
	// Et déblocage des threads
	CloseSyncObjects();

	// Attente des threads
	// 1 par 1 parce que WaitForMultipleObjects est limité en nombre d'objets
	for (i = 0; i<nbThreads; ++i) {
		WaitForSingleObject(thread_handles_tab[i], INFINITE);
	}
	delete thread_handles_tab;
}
/* *** End ZThreadPool class *** */
