/*
	ZazouMiniWebServer

	Copyright (C) 2003-2011 Xavier Garreau <xavier@xgarreau.org>

	This file is part of ZazouMiniWebServer.

    ZazouMiniWebServer is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    ZazouMiniWebServer is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with ZazouMiniWebServer; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
	
#ifndef _ZTHREADPOOL_H
#define _ZTHREADPOOL_H 1

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

// #define zprintf printf
#define zprintf

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

/* *** ZReqProvider class *** */
class ZReqProvider
{
	HANDLE_MUTEX queue_mutex;
	HANDLE_SEMAPHORE 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_MUTEX stop_mutex;
	HANDLE_THREAD 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_THREAD get_handle() { return handle; }
	~ZThread();
	BYTE stop();
protected:
	ZThread();
	BOOL should_stop();
	virtual void handle_request(ZRequest* r) { std::cout << "WARNING: Default request handler called !" << std::endl << std::endl; 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;
	t->handle = (HANDLE_THREAD)_beginthread(ZThread::start, 0, t);
	if (t->handle == (HANDLE_THREAD)-1) {
		delete t;
		return NULL;
	}
	return t;
}
/* *** End ZThread class *** */

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

/* *** Template implementation *** */
template<class T> ZThreadPool<T>::ZThreadPool(WORD min_threads) : ZReqProvider()
{
	addSomeThreads(min_threads);
}

template<class T> WORD ZThreadPool<T>::addSomeThreads(WORD nb_threads) {
	T* t;
	WORD i = 0;
	WORD created = 0;
	for (i = 0; i < nb_threads; ++i) {
		t = ZThread::create<T>(this);
		if (t)
		{
			pool.push(t);
			++created;
		} else {
			print_dbg("\nERROR: in ZThreadPool, could not create ZThread.\n\n");
		}
	}
	return created;
}

template<class T> WORD ZThreadPool<T>::delSomeThreads(WORD nb_threads) {
	DWORD nbThreadsInPool = static_cast<WORD>(pool.size());
	WORD i = 0;
	WORD deleted;
	if (nb_threads>=nbThreadsInPool) {
		nb_threads = nbThreadsInPool - 1;
	}
	deleted = nb_threads;
	// On dit aux threads qu'ils peuvent s'arrêter
	while (nb_threads--) {
		pool.front()->stop();
		pool.pop();
	}
	return deleted;
}

template<class T> ZThreadPool<T>::~ZThreadPool()
{
	DWORD nbThreads = static_cast<WORD>(pool.size());
	HANDLE_THREAD* thread_handles_tab = new HANDLE_THREAD[nbThreads*sizeof(HANDLE)];
	WORD i = 0;
	// On dit aux 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) {
		WaitForSingleThread(thread_handles_tab[i], INFINITE);
	}
	delete thread_handles_tab;
}
/* *** End ZThreadPool class *** */

#endif