/*******************************************************
 * Copyright (C) 2017-2020 CENOS Ltd vg@cenos-platform.com
 *
 * This file is part of CENOS.
 *
 * CENOS can not be copied and/or distributed without the express
 * permission of CENOS Ltd
 *******************************************************/

#include "Job.h"
#include "misc/miscFunctions.hpp"
#include "cenos_exception.h"

Job::~Job()
{
	remove(configFileName_.c_str());
	t_.setMessage("Job " + command_ + " finished in : ");
}

Job::Job(std::string input, websocketpp::connection_hdl hdl): hdl_(hdl)
{
	// get input
	bool valid_json = true;
	try
	{
		valid_json = true;
		std::stringstream ss(input);
		inpJson_ = json::parse(ss);
	}
	catch (json::parse_error & e)
	{
		valid_json = false;
		LOG(INFO) << "load JSON error " << e.what();
		throw cenos_exception("JSON load error. Check the input format");
	}


	//check if necessary fields are found in input json (localJob).
	//otherwise return an error message
	std::vector<std::string> necessaryFields = { "command", "module", "guid", "port" };

	for (auto& field : necessaryFields)
	{
		if (inpJson_.find(field) == inpJson_.end())
		{
			throw cenos_exception("No " + field + " specified.");
		}
	}

	//get job guid
	command_ = inpJson_["command"].get<std::string>();
	module_ = inpJson_["module"].get<std::string>();
	guid_ = inpJson_["guid"].get<std::string>();
	port_ = inpJson_["port"].get<int>();
	readArguments();
	writeJobConfig();
	jobState = QUEUED;
	stopper = std::make_shared<bool>();
	LOG(INFO) << "Created Job : " << command_ << "\n";
}



void Job::writeJobConfig()
{
	char const* tempFolderPath = getenv("TEMP");
	std::ofstream confFile;
	configFileName_ = std::string(tempFolderPath) + "/cenos/" + guid_ + ".json";
	if (misc::fileExist(configFileName_) )
		configFileName_ = std::string(tempFolderPath) + "/cenos/" + guid_ + "_sub.json";
	LOG(INFO) << "Writing config to file " << configFileName_ << "\n";
	confFile.open(configFileName_);
	confFile << inpJson_;
	confFile.close();
}

Job::Job(const Job& that)
{
	this->inpJson_ = that.inpJson_;
	this->command_ = that.command_;
	this->module_ = that.module_;
	this->guid_ = that.guid_;
	this->arg_ = that.arg_;
	this->hdl_ = that.hdl_;
	this->stopper = that.stopper;
	this->subjobs = that.subjobs;
	this->readArguments();
}

void Job::operator=(const Job& that)
{
	this->inpJson_ = that.inpJson_;
	this->command_ = that.command_;
	this->module_ = that.module_;
	this->guid_ = that.guid_;
	this->arg_ = that.arg_;
	this->hdl_ = that.hdl_;
	this->stopper = that.stopper;
	this->subjobs = that.subjobs;
	this->readArguments();
}

std::optional<json> Job::getArg(std::string key) const
{
	if (arg_.find(key) != arg_.end())
		return arg_.at(key);
	return {};
}

websocketpp::connection_hdl Job::hdl()
{
	return hdl_;
}

Timer Job::t() const
{
	return t_;
}

std::string Job::command() const
{
	return command_;
}

std::string Job::guid() const
{
	return guid_;
}

std::string Job::module() const
{
	return module_;
}

int Job::port() const
{
	return port_;
}

void Job::changeStateToRunning()
{
	jobState = RUNNING;
}

void Job::changeStateToFinished()
{
	jobState = FINISHED;
}

void Job::changeStateToStopRequested()
{
	jobState = STOP_REQUESTED;
	*stopper = true;
}

bool Job::isFinished()
{
	if (jobState == FINISHED)
		return true;
	else 
		return false;
}

bool Job::isStopRequested()
{
	if (jobState == STOP_REQUESTED)
		return true;
	else
		return false;
}

Stopper Job::getStopper()
{
	return stopper;
}


void Job::readArguments()
{
	if (inpJson_.find("args") == inpJson_.end())
		return;
	for (const auto& item : inpJson_.at("args").items())
	{
		arg_[item.key()] = item.value();
	}
}


std::string Job::getCommandFromInput(std::string input) throw()
{
	try
	{
		json inputJson;
		std::stringstream ss(input);
		inputJson = json::parse(ss);

		if (inputJson.find("command") != inputJson.end())
		{
			return inputJson["command"].get<std::string>();
		}
		else
		{
			LOG(INFO) << "load JSON error: no command in JSON";
			return "";
		}
	}
	catch (json::parse_error &e)
	{
		// Job::getCommandFromInput is used only in JobManager::addJob,
		// and there empty result is fine. -- AG
		LOG(INFO) << "load JSON error " << e.what();
		return "";
	}
}

void Job::addSubJob(std::shared_ptr<Job> j)
{
	subjobs.push_back(j);
}

std::vector< std::shared_ptr<Job>> Job::getSubJobs()
{
	return subjobs;
}
