#ifndef SUMMAACTOR_H_ 
#define SUMMAACTOR_H_

#include "SummaManager.h"

using namespace caf;
using json = nlohmann::json;
/**
 * Top Level Actor for Summa. This actor recieves the number of GRUs to compute from main and
 * divides them into jobs that compute one at a time.
 *
 * @param startGRU - starting GRU for the simulation 
 * @param numGRU - total number of GRUs to compute
 * @param configPath - location of file information for SUMMA
 * @return behavior 
 */
behavior summa_actor(stateful_actor<summa_manager>* self, int startGRU, int numGRU, std::string configPath) {
 	self->state.start = std::chrono::high_resolution_clock::now();
	// Set Variables
	self->state.startGRU = startGRU;
	self->state.numGRU = numGRU;
	self->state.configPath = configPath;

	parseSettings(self, configPath);
	aout(self) << "SETTINGS FOR SUMMA_ACTOR\n";
	aout(self) << "Output Structure Size = " << self->state.outputStrucSize << "\n";
	aout(self) << "Max GRUs Per Job = " << self->state.maxGRUPerJob << "\n";

	// Create the job_actor and start SUMMA
	spawnJob(self);

	return {
		[=](done_job, int numFailed) {
			self->state.numFailed += numFailed;
			aout(self) << "Job Done\n"; 
			if (self->state.numGRU <= 0) {

				self->state.end = std::chrono::high_resolution_clock::now();
            	self->state.duration = calculateTime(self->state.start, self->state.end);
				aout(self) << "Total Program Duration:\n";
            	aout(self) << "     " << self->state.duration / 1000  << " Seconds\n";
            	aout(self) << "     " << (self->state.duration / 1000) / 60  << " Minutes\n";
            	aout(self) << "     " << ((self->state.duration / 1000) / 60) / 60 << " Hours\n";

				aout(self) << "Program Finished \n";

			} else {
				// spawn a new job
				spawnJob(self);
			}
		},
	};
}


void spawnJob(stateful_actor<summa_manager>* self) {
	// Ensure we do not start a job with too many GRUs
	if (self->state.numGRU > self->state.maxGRUPerJob) {
		// spawn the job actor
		self->state.currentJob = self->spawn(job_actor, self->state.startGRU, self->state.maxGRUPerJob, 
			self->state.configPath, self->state.outputStrucSize, self);
		
		// Update GRU count
		self->state.numGRU = self->state.numGRU - self->state.maxGRUPerJob;
		self->state.startGRU = self->state.startGRU + self->state.maxGRUPerJob;

	} else {

		self->state.currentJob = self->spawn(job_actor, self->state.startGRU, self->state.numGRU, 
			self->state.configPath, self->state.outputStrucSize, self);
		self->state.numGRU = 0;
	}
}

void parseSettings(stateful_actor<summa_manager>* self, std::string configPath) {
	json settings;
	std::string SummaActorsSettings = "/Summa_Actors_Settings.json";
	std::ifstream settings_file(configPath + SummaActorsSettings);
	settings_file >> settings;
	settings_file.close();
	
	if (settings.find("SummaActor") != settings.end()) {
		json SummaActorConfig = settings["SummaActor"];
		
		// Find the desired OutputStrucSize
		if (SummaActorConfig.find("OuputStrucureSize") != SummaActorConfig.end()) {
			self->state.outputStrucSize = SummaActorConfig["OuputStrucureSize"];
		} else {
			aout(self) << "Error Finding OutputStructureSize in JOSN - Reverting to default value\n";
			self->state.outputStrucSize = 250;
		}

		// Find the desired maxGRUPerJob size
		if (SummaActorConfig.find("maxGRUPerJob") != SummaActorConfig.end()) {
			self->state.maxGRUPerJob = SummaActorConfig["maxGRUPerJob"];
		} else {
			aout(self) << "Error Finding maxGRUPerJob in JOSN - Reverting to default value\n";
			self->state.maxGRUPerJob = 500;
		}

	} else {
		aout(self) << "Error Finding SummaActor in JSON - Reverting to default values\n";
		self->state.outputStrucSize = 250;
		self->state.maxGRUPerJob = 500;
	}
}



#endif