#ifndef SCHEDULERS_H
#define SCHEDULERS_H

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <vector>
#include <algorithm>
#include <cerrno>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <boost/chrono.hpp>

#include "row.hpp"
#include "sorting_algorithms.hpp"


void run_sort(int *arr, int n, int algorithm)
{
	row<int> r(arr, n);

	switch (algorithm)
	{
		case 1:
			quick_sort(r);
			break;
		case 2:
			merge_sort(r);
			break;
		case 3:
			insertion_sort(r);
			break;
		case 4:
			selection_sort(r);
			break;
		case 5:
			bubble_sort(r);
			break;
		case 6:
			enumeration_sort(r);
			break;
		default:
			quick_sort(r);
			break;
	}
	for (int i=0; i<n;i++)
		arr[i] = r[i];
}



//PROLIFIC

class ProlificScheduler{

public:

//FOR mainB: B(n, n, k)

void run(int *A, int n, int k, int algorithm)
{

	using namespace boost::chrono;

	pid_t root = getpid();
	std::vector<pid_t> pids;
	int worker_id= -1;


	steady_clock::time_point start_setup, stop_setup, stop_exec;

	if(getpid()==root)
		start_setup = steady_clock::now();

	//Root spawns children and stores PIDs
	for (int s=0; s<k; ++s)
	{
		if (getpid() != root){
			break;
			//_exit(0);
		}
		pid_t pid = fork();
		if (pid == 0)
		{
		//	std::cout << "[" << getpid() << "," << s 
		//	<< "]"<< std::endl;
			worker_id=s;
			break;
		}
		else {
			pids.push_back(pid);
		}
	}
	if(getpid() == root)
		stop_setup = steady_clock::now();
	
	//ROOT reaping loop
	if(getpid() == root)
	{
	
		int status;
		while (1)
		{
			pid_t finished = waitpid(-1, &status, 0);

			if (finished > 0)
			{
	//		std::cout << "Reaped PID " << finished << "\n";

			pids.erase(std::remove(pids.begin(), pids.end(),
						finished), pids.end());
			continue;
			}
			if (finished == -1 && errno == ECHILD)
				break;
			break;
		}

	stop_exec = steady_clock::now();

	std::cout<<"\n[Prolific Timing]\n";
	std::cout << "Setup time: "
	<<duration_cast<microseconds>(stop_setup - start_setup).count()
	<<" ms\n";
	std::cout << "Execution time: "
	<<duration_cast<microseconds>(stop_exec - stop_setup).count()
	<<" ms\n";
	std::cout << "Total time: "
	<<duration_cast<microseconds>(stop_exec - start_setup).count()
	<<" ms\n";
	
	}
	else
	{
		//EACH WORKER SORTS ONE WHOLE SLICE
		for(int i=0; i<n; ++i){
			//if(i% worker_count == worker_id) {

	//		quick_sort(A + worker_id*n*n +i*n, n);
				//random_task(i);

			run_sort(A +worker_id*n*n +i*n, n, algorithm);

			//	std::cout<<"Worker "<<worker_id <<
			//		" sorted row "<< i 
			//		<<" of slice " << worker_id 
			//		<< "\n";
				}
				_exit(0);
			//}
	}


}


};


//COLLECTIVE

class CollectiveScheduler{

public:

//FOR mainB : B(n, n, k)
void run(int *A, int n, int k, int algorithm){

	using namespace boost::chrono;

	pid_t root = getpid();
	pid_t child_pid = -1;
	int worker_id=-1;

	steady_clock::time_point start_setup, stop_setup, stop_exec;

	if(getpid() == root)
		start_setup = steady_clock::now();

	//Collective forkL children continue the loop
	for(int s=0; s<k; ++s)
	{
		pid_t pid = fork();

		if(pid == 0){
			worker_id=s;
	//		std::cout <<"["<<getpid() <<", worker " <<
	//			worker_id << "]\n";
			continue; //child continues to next worker
		}
		else
		{
			child_pid = pid;
			break;
		}
	}

	if(getpid()==root)
		stop_setup = steady_clock::now();

	//Root reaps all descendants
	//does not sort only waits for chain

	if(getpid() == root)
	{
		int status;
		while(1)
		{
			pid_t finished = waitpid(-1, &status, 0);

			if(finished > 0)
			{
	//		std::cout << "Reaped PID "<< finished<<"\n";
			continue;
			}

			if (finished == -1 && errno == ECHILD)
				break;
		break;
		}

	stop_exec = steady_clock::now();

	std::cout<<"\n[Collective Timing]\n";
	std::cout<<"Setup time: "
	<<duration_cast<microseconds>(stop_setup - start_setup).count()
	<<" ms\n";
	std::cout<<"Execution time: "
	<<duration_cast<microseconds>(stop_exec - stop_setup).count()
	<<" ms\n";
	std::cout<<"Total time: "
	<<duration_cast<microseconds>(stop_exec - start_setup).count()
	<<" ms\n";
	
	} else {
		//each worker sorts one whole slice
		if(worker_id >= 0 && worker_id <k)
		{
			for(int i=0; i<n; ++i)
			{
	//			quick_sort(A + worker_id*n*n +i*n, n);

			run_sort(A +worker_id*n*n +i*n, n, algorithm);

	//			std::cout<<"Worker "<< worker_id <<
	//				" sorted row "<< i <<
	//				" of slice "<<worker_id << "\n";
			}
		}

		//wait for child created by this worker
		if(child_pid >0)
		{
			int status;
			waitpid(child_pid, &status, 0);
		}

		_exit(0);
	}
}

};



#endif
