#include "SalomeModuleFns.hpp"
#include <regex>
#include <filesystem>
#include <iostream>
#include <fstream>
#include "cenos_exception.h"


std::string mergeDumpFiles(std::string dumpFileString, std::string shaperDumpFileString)
{
	std::istringstream iss(dumpFileString);
	std::string line;
	std::string returnString;
	while (std::getline(iss, line))
	{
		if (line.find("### SHAPER component") != std::string::npos)
		{
			returnString.append(line + "\n");
			std::getline(iss, line);
			returnString.append(line + "\n");
			std::istringstream shaperStringStreams(shaperDumpFileString);
			while (std::getline(shaperStringStreams, line))
			{
				returnString.append(line + "\n");
			}
		}
		else
		{
			returnString.append(line + "\n");
		}
	}
	return returnString;
}

std::string getMeshName(std::string meshFile)
{
	std::ifstream meshStreamFile;
	meshStreamFile.open(meshFile);
	while (meshStreamFile.good())
	{
		std::string line;
		getline(meshStreamFile, line);
		std::istringstream lString(line);
		if (line.find("$SalomeMeshName") != std::string::npos)
		{

			getline(meshStreamFile, line);
			std::istringstream lString(line);
			std::string meshname;
			lString >> meshname;

			return meshname;
		}
	}
	return "Mesh_1";
}


std::string replaceNotepadValue(std::string dumpStr, json params, std::string meshName)
{
	std::istringstream iss(dumpStr);
	std::string line;
	std::string returnString;
	while (std::getline(iss, line))
	{


		if (line.find("geompy.ImportXAO(") != std::string::npos)
		{
			size_t start = line.find("geompy.ImportXAO(");
			std::replace(line.begin(), line.end(), '\\', '/');
			returnString.append(line + "\n");
		}
		// ignore lines with multiple commas. This is known problem with Salome dump scripts
		else if (line.find(",,,,") != std::string::npos)
		{

		}
		//ignore lines where brep export is done. Usually this is leftover from exporting geometryData to Cenos
		// Since geometry data is created everytime, there is no need to keep this!
		else if (line.find("ExportBREP") != std::string::npos)
		{

		}
		//add assertion of exportToXAO without errors
		//if export to xao is with errors, no file is overwritten and previous is used.
		// this leads to the case that parameters are changed, but old geometry appears
		// assertion here allows returning error if such situation occurs
		else if (line.find("exportToXAO") != std::string::npos)
		{
			size_t start = line.find("=");
			returnString.append(line + "\n");
			returnString.append("assert(" + line.substr(0, start) + ".feature().error() == \"\")\n");
		}
		else if (line.find(meshName + ".Compute") != std::string::npos)
		{
			returnString.append("if not GMSH.isSameTopology(wDir, " + meshName + "):\n");
			returnString.append("    jsondata['status'] = \"error\"\n");
			returnString.append("    jsondata['message'] = \"Topology of your geometry changed. Please open Salome and update geometry manually for this parameter set!\"\n");
			returnString.append("    with open(wDir + \"//geometry//batch_response.json\", \"w\") as write_file :\n");
			returnString.append("        json.dump(jsondata, write_file)\n");
			returnString.append("    if not salome.sg.hasDesktop():\n");
			returnString.append("        from killSalomeWithPort import killMyPort\n");
			returnString.append("        killMyPort(os.getenv('NSPORT'))\n");
			returnString.append("        exit()\n");
			returnString.append("try:\n");
			returnString.append("    isDone = " + meshName + ".Compute(True)\n");
			returnString.append("except:\n");
			returnString.append("    jsondata['status'] = \"error\"\n");
			returnString.append("    jsondata['message'] = \"Error while generating mesh\"\n");
			returnString.append("    with open(wDir + \"//geometry//batch_response.json\", \"w\") as write_file :\n");
			returnString.append("        json.dump(jsondata, write_file)\n");
			returnString.append("    if not salome.sg.hasDesktop():\n");
			returnString.append("        from killSalomeWithPort import killMyPort\n");
			returnString.append("        killMyPort(os.getenv('NSPORT'))\n");
			returnString.append("        exit()\n\n");

			returnString.append("if not isDone:\n");
			returnString.append("    jsondata['status'] = \"error\"\n");
			returnString.append("    jsondata['message'] = \"Error while generating mesh\"\n");
			returnString.append("    with open(wDir + \"//geometry//batch_response.json\", \"w\") as write_file :\n");
			returnString.append("        json.dump(jsondata, write_file)\n");
			returnString.append("    if not GMSH.isSameTopology(wDir, " + meshName + "):\n");
			returnString.append("        jsondata['message'] = \"Topology of your geometry changed. Please open Salome and update geometry manually for this parameter set!\"\n");
			returnString.append("    else:\n");
			returnString.append("        jsondata['message'] = \"Please check input!\"\n");
			returnString.append("    if not salome.sg.hasDesktop():\n");
			returnString.append("        from killSalomeWithPort import killMyPort\n");
			returnString.append("        killMyPort(os.getenv('NSPORT'))\n");
			returnString.append("        exit()\n\n");

		}
		else
		{
			bool replacedValue = false;
			for (auto par : params)
			{
				// here the replacement of parameter values takes part
				// in Salome GEOM parameter is set as:
				// notebook.set("varName", 15.2) # here 15.2 is the value
				//
				// in Salome Shape :
				// model.addParameter("varName", 15.2)
				// in this block values are replaced with new ones
				if (line.find("notebook.set(\"" + par["name"].get<std::string>() + "\",") != std::string::npos)
				{
					std::string replace = "notebook.set(\"" + par["name"].get<std::string>() + "\", " + std::to_string(par["value"].get<float>()) + ") \n";
					returnString.append(replace);
					replacedValue = true;
				}
				else if (line.find("model.addParameter(") != std::string::npos)
				{
					size_t start = line.find("model.addParameter(");
					size_t end = line.find(",");
					std::string docName = line.substr(start, end - start);
					std::string replace = docName + ", \"" + par["name"].get<std::string>() + "\", \"" + std::to_string(par["value"].get<float>()) + "\") \n";
					returnString.append(replace);
					replacedValue = true;
				}
			}

			if (!replacedValue)
				returnString.append(line + "\n");
		}
	}
	return returnString;
}


std::string updateStepPaths(std::string dumpFileString, std::string wDir) //if .step file cannot be found, searches for it in geomtery/inputs
{
	std::istringstream iss(dumpFileString);
	std::string line;
	std::string returnString;
	while (std::getline(iss, line))
	{
		if ((line.find("geompy.Import") != std::string::npos)
			&& !(line.find("geompy.ImportXAO") != std::string::npos))
		{
			
			//Find .step path
			const std::string s = line;
			std::regex rgx("\"(.*)\"");
			std::smatch match;
			if (std::regex_search(s.begin(), s.end(), match, rgx)) { //get .step path
				std::string originalFilePath = match[1];
				std::error_code ec;
				if (std::filesystem::exists(originalFilePath, ec) == false) {
					std::string missingFileName = originalFilePath.substr(originalFilePath.find_last_of("/\\") + 1);
					std::string inputDir = wDir + "\\geometry\\input";
					
					if (std::filesystem::exists(inputDir, ec) == false) {
						throw cenos_exception("'geometry/input' folder not found in case folder, please create this folder with the used STEP files inside it.");
					}
					bool fileFound = false;
					for (const auto& entry : std::filesystem::directory_iterator(inputDir)) {
						std::string availableFile = entry.path().u8string();
						std::string availableFileName = availableFile.substr(availableFile.find_last_of("/\\") + 1);

						if (availableFileName == missingFileName) {
							fileFound = true;
							returnString.append("\n#Replaced path:\n");
							std::string beginning = s.substr(0, s.find_first_of("\""));
							std::string end = s.substr(s.find_last_of("\""));
							returnString.append(beginning + "r\"" + availableFile + end + "\n");
						}
					}
					if (fileFound == false) {
						std::string message = missingFileName + " not found in 'geometry/input' folder";
						throw cenos_exception(message);
					}
				}
				else{
					returnString.append(line + "\n");
				}
			}
		
		}
		else
		{
			returnString.append(line + "\n");
		}
	}
	return returnString;
}


/// <summary>
/// Add indent in front of every line of dump file
/// </summary>
/// <param name="dumpFileString"></param>
/// <param name="indent"> indent, e.g. "    "</param>
/// <returns></returns>
std::string addPythonIndent(std::string dumpFileString, std::string indent)
{
	std::istringstream iss(dumpFileString);
	std::string line;
	std::string returnString;
	while (std::getline(iss, line))
	{
		returnString.append(indent + line + "\n");
	}
	return returnString;
}