#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <iostream>
using namespace std;

class ThreadPool{
	private:
		vector<thread> workers;
		queue<function<void()>> taskQueue;
		mutex queueMutex;
		condition_variable condition;
		bool stopPool;
	public:
		ThreadPool(int numThreads);
		~ThreadPool();
		void enqueueTask(function<void()> task);
};

ThreadPool::ThreadPool(int numThreads):stopPool(false){
	for(int i=0; i<numThreads;i++){
		workers.emplace_back([this,i]{
			while(true){
				function<void()> currentTask;
				{
					unique_lock<mutex> lock(this->queueMutex);
					this->condition.wait(lock,[this]{return this->stopPool || ! this->taskQueue.empty();});
					if(this->stopPool&&this->taskQueue.empty()){
						return;
					}
				currentTask=move(this->taskQueue.front());
				this->taskQueue.pop();
				}
				currentTask();
			}
		});
	}
	cout<< "[THREAD POOL] Successfully booted up "<<numThreads<<" pemanent workers."<<endl;
}

void ThreadPool::enqueueTask(function<void()> task){
	{
		unique_lock<mutex> lock(queueMutex);
		taskQueue.push(move(task));
	}
	condition.notify_one();
}

ThreadPool::~ThreadPool(){
	{
		unique_lock<mutex> lock(queueMutex);
		stopPool=true;
	}
	condition.notify_all();
	for(thread &worker:workers){
		if(worker.joinable()){
			worker.join();
		}
	}
	cout<<"[THREAD POOL] All workers have safely terminated. Pool closed."<<endl;
}




#endif
