// ThreadPool.hh
#include "shared_queue.hh"

#include <thread>
#include <vector>

#ifndef THREAD_POOL_H
#define THREAD_POOL_H

// Implementations for producers and consumers
void producer( const int id, SharedQueue<int>& _q, int max_t, int max_n );
void consumer( const int id, SharedQueue<int>& _q, int max_t, int max_n );

class ThreadPool
{
	public:
		ThreadPool() :
			_q(),
			_consumers(),
			_producers()
		{};
		void addProducer( int max_t = 3000, int max_n = 10)
		{
			std::thread * tp = new std::thread( producer, _producers.size(), std::ref(_q), max_t, max_n );

			_producers.push_back(tp);

		}

		void addConsumer( int max_t = 2000, int max_n = 10)
		{
			std::thread * tp = new std::thread( consumer, _consumers.size(), std::ref(_q), max_t, max_n );

			_consumers.push_back(tp);
		}

		void run()
		{
			// join all producers
			for ( int i = 0; i < _producers.size() ; i++ )
			{
				(*_producers[i]).join();
			}

			// join all consumers
			for ( int i = 0; i < _consumers.size() ; i++ )
			{
				(*_consumers[i]).join();
			}
		}

	
	private:
		// Consumer and Producers threads
		std::vector<std::thread * > _consumers;
		std::vector<std::thread * > _producers;

		// queue
		SharedQueue<int> _q;
};

#if ENABLE_PYBIND
/*
 * Define Python access
 */
#include <pybind11/pybind11.h>

namespace py = pybind11;

PYBIND11_MODULE(ThreadPool, tp)
{
	py::class_<ThreadPool>( tp, "ThreadPool")
		.def(py::init())
		.def("addProducer", &ThreadPool::addProducer, "Add a new Producer to the Pool")
		.def("addConsumer", &ThreadPool::addConsumer, "Add a new Consumer to the Pool")
		.def("run", &ThreadPool::run, "Run all registered Producers and Consumers");
}
#endif
#endif
