/*******************************************************
 * 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 "SalomeModule_Worker.h"
#include "module/SalomeModuleFns.hpp"
#include "misc/miscFunctions.hpp"
#include <boost/filesystem.hpp>


SalomeModule_Worker::SalomeModule_Worker(std::shared_ptr<Job>& j) : Worker(j)
{

}


messagedata SalomeModule_Worker::run(MessageQueue_ messageQueue)
{
	SalomeModule salome;
	messagedata returnData;

	auto runFuture = std::async(std::launch::async, &SalomeModule_Worker::executeSalome, this, std::ref(salome), std::ref(messageQueue));

	do
	{
		Sleep(100);

		if (assignedJob_->isStopRequested())
		{
			salome.stop();
			runFuture.wait();
		}

	} while (runFuture.wait_for(std::chrono::milliseconds(1)) != std::future_status::ready);

	returnData = runFuture.get();

	return returnData;
}

messagedata SalomeModule_Worker::executeSalome(SalomeModule& salome, MessageQueue_ messageQueue)
{
	try
	{
		//create instance of SalomeModule
		std::optional<json> wdir = assignedJob_->getArg("caseDir");

		if (!wdir.has_value())
			return messagedata(messagedata::error, messagedata::response, "Argument 1 not specified for Salome input defined");
		salome.setWDir(wdir.value());
		salome.setGuid(assignedJob_->guid());

		//analyze commands


		//if salome_study.hdf exists, salome with hdf is loaded. If not - empty salome is open
		// prior to 0.6.7 salome_study.hdf was saved in case folder
		// if salome_study.hdf is in case folder, it is copied to geometry folder,
		// renamed to salome_study.hdf.old
		std::string wDir = wdir.value();
		std::string oldStudyFile = wDir + "\\salome_study.hdf";
		std::string oldStudyFile_old = wDir + "\\salome_study.hdf.old";
		std::string newStudyFile = wDir + "\\geometry\\salome_study.hdf";

		// runSalome
		// this command opens salome in gui mode
		if (assignedJob_->command() == "runSalome")
		{
			salome.setRunMode(SalomeModule::GUI);
			if (assignedJob_->getArg("hdfFile").has_value())
			{
				std::string hdf_file = assignedJob_->getArg("hdfFile").value().get<std::string>();
				if (misc::fileExist(hdf_file))
				{
					salome.writeSalomeOpeningScript(hdf_file);
				}
				else
				{
					LOG(INFO) << "Specified file " + hdf_file + " does not exist ";
					return messagedata(messagedata::error, messagedata::response, "Specified file " + hdf_file + " does not exist ", assignedJob_->guid());
				}
			}
			else if (misc::fileExist(newStudyFile))
			{
				salome.writeSalomeOpeningScript(newStudyFile);
			}
			else if (misc::fileExist(oldStudyFile))
			{
				if (!misc::fileExist(newStudyFile))
					copy_file(oldStudyFile, newStudyFile, boost::filesystem::copy_option::none);
				std::rename(oldStudyFile.c_str(), oldStudyFile_old.c_str());
				salome.writeSalomeOpeningScript(newStudyFile);
			}
			else
			{
				salome.writeSalomeOpeningScript();
			}
			return salome.runSalome();
		}

		// updateSalome
		// generates new geometryData AND mesh with new parameter values
		// in BATCH mode
		else if (assignedJob_->command() == "updateSalome")
		{
			salome.setRunMode(SalomeModule::BATCH);

			salome.writeUpdateSalomeScript(assignedJob_->getArg("geometryData").value());
			return salome.runSalome();
		}		

		// runSalome with cad files passed as arguments
		// opensa salome in GUI with cad preloaded
		else if (assignedJob_->command() == "runSalomeWithCad")
		{
			salome.setRunMode(SalomeModule::GUI);

			salome.writeLoadCadScript(assignedJob_->getArg("inputFiles").value());
			return salome.runSalome();
		}

		// getGeometryDataFromMeshFile
		// generate geometryData for old salome case
		else if (assignedJob_->command() == "getGeometryDataFromMeshFile")
		{
			salome.setRunMode(SalomeModule::BATCH);

			std::optional<json> geometry_data_json = assignedJob_->getArg("geometryData");

			// do not use GeometryData o read geometry_data_json, as it is old format, not supported by GeometryData
			json domains_json = geometry_data_json.value().at("domains");
			json boundaries_json = geometry_data_json.value().at("boundaries");
			std::vector<std::string> group_names;
			for (auto dom : domains_json)
				group_names.push_back(dom.at("name"));
			for (auto bnd : boundaries_json)
				group_names.push_back(bnd.at("name"));
			
			salome.writeGenerateGeometryDataScript(group_names);
			return salome.runSalome();
		}

		// runSalomeWithGeometryData
		// open salome in gui with geometryData loaded and grouped
		else if (assignedJob_->command() == "runSalomeWithGeometryData")
		{
			salome.setRunMode(SalomeModule::GUI);

			std::optional<json> geometry_data_json = assignedJob_->getArg("geometryData");
			json phys_json = assignedJob_->getArg("physicsParams").value();


			salome.writeGenerateGroupsFromGeometryData(geometry_data_json.value(), phys_json);
			return salome.runSalome();
		}
		else
		{
			return messagedata(messagedata::error, messagedata::response, "No command " + assignedJob_->command() + " defined", assignedJob_->guid());
		}

	}
	catch (json::exception& e)
	{
		std::string message = e.what();
		messagedata retData(messagedata::error, messagedata::response, "Salome worker exception " + message);
		retData.guid = assignedJob_->guid();
		return retData;
	}
	catch (std::exception e)
	{
		std::string message = e.what();
		messagedata retData(messagedata::error, messagedata::response, "Salome worker exception " + message);
		retData.guid = assignedJob_->guid();
		return retData;
	}
	catch (std::string e)
	{
		messagedata retData(messagedata::error, messagedata::response, "Salome worker exception " + e);
		retData.guid = assignedJob_->guid();
		return retData;
	}
}