#include "GetdpPostFns.hpp"
#include "cenos_exception.h"
#include <sstream> 

std::string getPostOperations(std::shared_ptr<PostValues> postValue, std::string entityName, int timeStep, std::optional<PostValues::PostValueType> restrictedType)
{
	//if restrictedType is passed, check if it matches postValue type, return empty string otherwise.
	if (restrictedType.has_value())
	{
		if (restrictedType.value() != postValue->getType())
			return "";
	}

	if (postValue->isExternal())
		return "";

	//needed for multiport 
	std::string multiPortIreator = "";
	if (postValue->getSolverKey() == "getdpMicrowaves") {
		multiPortIreator = "\", Sprintf(\"_port%g\",i),\"";
	}


	std::string outString;
	if (entityName.length() < 1)
	{
		if ((postValue->getType() == PostValues::RESIDUAL))
		{
			outString.append("    Print[" + postValue->getStringId() + ", OnElementsOf Region[{ ");
			int g = 0;
			for (auto& domain : postValue->getDomains())
			{
				if (g != 0) outString.append(", ");
				outString.append(domain);
				g++;
			}
			outString.append("}], StoreMaxInRegister " + std::to_string(postValue->getValueRegister()) + ",");
			outString.append("File \"..\\getdpresults\\" + postValue->getStringId() + ".pos\", LastTimeStepOnly]; \n");
		}
		else if ((postValue->getType() == PostValues::MAX_VAL))
		{
			outString.append("    Print[" + postValue->getStringId() + ", OnRegion Region[{ ");
			int g = 0;
			for (auto& domain : postValue->getDomains())
			{
				if (g != 0) outString.append(", ");
				outString.append(domain);
				g++;
			}
			outString.append("}], StoreInRegister " + std::to_string(postValue->getValueRegister()) + ",");
			outString.append("File \"..\\getdpresults\\" + postValue->getStringId() + ".pos\",  Format Table, LastTimeStepOnly]; \n");
		}
		else if ((postValue->getType() == PostValues::RESIDUAL_FOR_ADAPTIVE))
		{
			outString.append("    Print[" + postValue->getStringId() + ", OnElementsOf Region[{ ");
			int g = 0;
			for (auto& domain : postValue->getDomains())
			{
				if (g != 0) outString.append(", ");
				outString.append(domain);
				g++;
			}
			outString.append("}], ");
			outString.append("File \"..\\getdpresults\\" + postValue->getStringId() + ".pos\"]; \n");
		}
		else if ((postValue->getType() == PostValues::SCALING_VALUE))
		{
			outString.append("    Print[" + postValue->getStringId() + "[Region[{");
			int g = 0;
			for (auto& domain : postValue->getDomains())
			{
				if (g != 0) outString.append(", ");
				outString.append(domain);
				g++;
			}
			outString.append("}]], OnGlobal , StoreInRegister " + std::to_string(postValue->getValueRegister()) + ",");
			outString.append("File \"..\\getdpresults\\" + postValue->getStringId() + ".pos\",  Format Table, LastTimeStepOnly]; \n");
		}
	}
	else
	{
		if ((postValue->getType() == PostValues::INTEGRAL) or
			(postValue->getType() == PostValues::BOUNDARY_INTEGRAL))
		{
			outString.append("    Print[" + postValue->getStringId() + "[");
			outString.append(entityName + "], OnGlobal, File \"..\\getdpresults\\globals\\" + postValue->getStringId() + "_" + entityName + "_globals.dat\",");
			if (timeStep!=0)
				outString.append(" AppendToExistingFile 1,");
			outString.append(" Format Table, TimeStep " + std::to_string(timeStep));
			if (postValue->storeInVariable())
			{
				outString.append(", StoreInVariable $" + postValue->getStringId());
			}
			outString.append(" ]; \n");
		}
		else if ((postValue->getType() == PostValues::GLOBAL) or
			(postValue->getType() == PostValues::BOUNDARY_GLOBAL))
		{
			if (postValue->needLoop()) {
				outString.append("    Print[" + postValue->getStringId() + ", OnRegion ");
				if (postValue->getStringId() == "acS~{j}~{k}") {

					outString.append("Sur_UNIFPORT_N~{j}");
					outString.append(", File StrCat[ \"..\\getdpresults\\globals\\" + postValue->getStringId());
					outString.append("\", Sprintf(\"_port%g\", i), \"");
					outString.append("_globals.dat\"],");
				}
				else {
					outString.append("Sur_UNIFPORT_N~{i}");
					outString.append(", File StrCat[ \"..\\getdpresults\\globals\\" + postValue->getStringId());
					outString.append("\", Sprintf(\"_%g\", j)");
					outString.append(", Sprintf(\"_Sur_UNIFPORT_N_%g\", j), Sprintf(\"_port%g\", i), \"");
					outString.append("_globals.dat\"],");
				}
				
				outString.append(" AppendToExistingFile 1,");
				outString.append(" Format Table, TimeStep " + std::to_string(timeStep));
				if (postValue->storeInVariable())
				{
					outString.append(", StoreInVariable $" + postValue->getStringId());
				}
				outString.append(" ]; \n");

			}
			else {
				outString.append("    Print[" + postValue->getStringId() + ", OnRegion ");
				outString.append(entityName + ", File StrCat[ \"..\\getdpresults\\globals\\" + postValue->getStringId() + "_" + entityName + multiPortIreator + "_globals.dat\"],");
				if (timeStep != 0)
					outString.append(" AppendToExistingFile 1,");
				outString.append(" Format Table, TimeStep " + std::to_string(timeStep));
				if (postValue->storeInVariable())
				{
					outString.append(", StoreInVariable $" + postValue->getStringId());
				}
				outString.append(" ]; \n");
			}
		}

		else if (postValue->getType() == PostValues::TOTAL_VALUE)
		{
			outString.append("    Print[" + postValue->getStringId() + "[");
			outString.append(entityName + "], OnGlobal,File \"..\\getdpresults\\globals\\" + postValue->getStringId() + "__globals.dat\",");
			if (timeStep != 0)
					outString.append(" AppendToExistingFile 1,");
			outString.append(" Format Table, TimeStep " + std::to_string(timeStep) + "]; \n");
		}
		else if ((postValue->getType() == PostValues::NODE_FIELD) or
			(postValue->getType() == PostValues::BOUNDARY_NODE_FIELD))
		{
			outString.append("    Print[" + postValue->getStringId() + ",  OnElementsOf Region[{ ");
			outString.append(entityName + "}], File \"..\\getdpresults\\" + postValue->getStringId() + "_" + entityName + "_" + std::to_string(timeStep) + ".dat\", ");
			outString.append("TimeStep " + std::to_string(timeStep) + ", Smoothing " + std::to_string(postValue->getSmoothing()) + ", Format NodeTable]; \n");
		}
		else if (postValue->getType() == PostValues::BOUNDARY_CELL_FIELD)
		{
			outString.append("    Print[" + postValue->getStringId() + ",  OnElementsOf Region[{ ");
			outString.append(entityName + "}], File \"..\\getdpresults\\" + postValue->getStringId() + "_" + entityName + "_" + std::to_string(timeStep) + ".dat\", ");
			outString.append("TimeStep " + std::to_string(timeStep) + ", Smoothing " + std::to_string(postValue->getSmoothing()) + ", Format ElementTable]; \n");
		}
		else if (postValue->getType() == PostValues::INTERMEDIATE_SUR)
		{
			outString.append("    Print[" + postValue->getStringId() + ",  OnElementsOf Region[{ ");
			outString.append(entityName + "}], File \"..\\getdpresults\\" + postValue->getStringId()  + ".dat\", ");
			outString.append("TimeStep " + std::to_string(timeStep) + ", Smoothing " + std::to_string(postValue->getSmoothing()) + ", Format ElementTable]; \n");
		}
	}



	return outString;
}

std::string getPostProcessing(std::shared_ptr<PostValues> postValue)
{
	if (postValue->isExternal())
	{
		return "";
	}

	std::string outString;
	outString.append("      { Name " + postValue->getStringId() + " ; Value { \n");
	for (unsigned i = 0; i < postValue->getTerms().size(); i++)
	{
		if ((postValue->getType() == PostValues::RESIDUAL))
		{
			outString.append("      	Term { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JVol ; }\n ");
		}
		if ((postValue->getType() == PostValues::MAX_VAL))
		{
			outString.append("      	Term { Type Global; [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JVol ; }\n ");
		}
		if ((postValue->getType() == PostValues::RESIDUAL_FOR_ADAPTIVE))
		{
			outString.append("      	Term { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JVol ; }\n ");
		}
		if ((postValue->getType() == PostValues::NODE_FIELD))
		{
			outString.append("      	Term { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JVol ; }\n ");
		}
		else if (postValue->getType() == PostValues::INTEGRAL)
		{
			outString.append("      	Integral { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JVol ; Integration I1; }\n ");
		}
		else if (postValue->getType() == PostValues::GLOBAL)
		{
			outString.append("      	Term { Type Global;  [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; }\n ");
		}
		else if (postValue->getType() == PostValues::TOTAL_VALUE)
		{
			outString.append("      	Integral { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JVol ; Integration I1; }\n ");
		}
		else if (postValue->getType() == PostValues::SCALING_VALUE)
		{
			outString.append("      	Integral { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JVol ; Integration I1; }\n ");
		}
		else if (postValue->getType() == PostValues::BOUNDARY_GLOBAL)
		{
			if (postValue->hasTwoTerm()) {
				outString.append("      	If (j==k)\n");
				outString.append("      	Term { Type Global; [ " + postValue->getTerms()[i] + " ] ; ");
				outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; }\n ");
				outString.append("      	Else\n");
				outString.append("      	Term { Type Global; [ " + postValue->getTerms2()[i] + " ] ; ");
				outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; }\n ");
				outString.append("      	EndIf\n");
			}
			else {
				outString.append("      	Term { Type Global; [ " + postValue->getTerms()[i] + " ] ; ");
				outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; }\n ");
			}

		}
		else if (postValue->getType() == PostValues::BOUNDARY_NODE_FIELD)
		{
			outString.append("      	Term { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JSur ; }\n ");
		}
		else if (postValue->getType() == PostValues::BOUNDARY_CELL_FIELD)
		{
			outString.append("      	Term { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JSur ; }\n ");
		}	
		else if (postValue->getType() == PostValues::INTERMEDIATE_VOL)
		{
			outString.append("      	Term { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JVol ; }\n ");
		}
		else if (postValue->getType() == PostValues::INTERMEDIATE_SUR)
		{
			outString.append("      	Term { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JSur ; }\n ");
		}
		else if (postValue->getType() == PostValues::BOUNDARY_INTEGRAL)
		{
			outString.append("      	Integral { [ " + postValue->getTerms()[i] + " ] ; ");
			outString.append(" In Region[{" + postValue->getDomains()[i] + "}] ; Jacobian JSur ; Integration I1; }\n ");
		}
	}
	outString.append("       } }\n");
	return outString;
}

std::vector<std::pair<double, double>> processGlobalValuesV2(std::shared_ptr<PostValues> postValue, std::string domName, std::string wDir) {
	std::ifstream resStreamFile;
	std::string domainType; // Only used for error message string
	std::vector<std::pair<double, double>> returnVector;

	switch (postValue->getType()) {
	case PostValues::GLOBAL:
	case PostValues::INTEGRAL:
	case PostValues::TOTAL_VALUE:
		resStreamFile.open(wDir + "/getdpresults/globals/" + postValue->getStringId() + "_" + domName + "_globals.dat");
		domainType = "domain";
		break;
	case PostValues::GLOBAL_EXTERNAL:
		resStreamFile.open(wDir + "/getdpresults/globals/" + postValue->getStringId() + "_globals.dat");
		domainType = "domain";
		break;
	case PostValues::BOUNDARY_GLOBAL:
	case PostValues::BOUNDARY_INTEGRAL:
	case PostValues::CIRCULATION:
		resStreamFile.open(wDir + "/getdpresults/globals/" + postValue->getStringId() + "_" + domName + "_globals.dat");
		domainType = "boundary";
		break;
	}


	std::string line;
	if (!resStreamFile.is_open())
	{
		std::string msg = "could not open global .dat file for " + postValue->getStringId() + " in " + domainType + " " + domName;
		LOG(ERROR) << msg;
		throw cenos_exception(msg);
	}

	// Sometimes GetDP writes the final time step twice, this removes that duplicate line
	std::string previousLine;
	while (resStreamFile.good())
	{
		getline(resStreamFile, line);
		if (line.empty())
			break;

		if (line == previousLine)
			break;

		previousLine = line;

		std::istringstream lString(line);
		double tstep, val;
		lString >> tstep >> val;
		returnVector.push_back(std::pair(tstep, val));

	}

	// I guess this removes the 0th time step if it is not the only one
	if ((returnVector[0].first == 0.0) and (returnVector.size() > 1))
	{
		returnVector.erase(returnVector.begin());
	}

	return returnVector;
}

std::vector<std::pair<double, double>> processGlobalValuesV2Legacy(std::shared_ptr<PostValues> postValue, std::string domName, std::string wDir) {
	std::ifstream ifs(wDir + "/results/globalResultsFile.json");
	json globalResultsJson = json::parse(ifs);

	json boundaries = globalResultsJson["bcs"].get<std::vector<json>>();
	json domainsAndBoundareis = globalResultsJson["domains"].get<std::vector<json>>();
	domainsAndBoundareis.insert(domainsAndBoundareis.end(), boundaries.begin(), boundaries.end());


	std::vector<std::pair<double, double>> returnVector;

	std::vector<double> values;
	std::vector<double> times;

	for (json dom : domainsAndBoundareis) {
		if (dom["name"] == domName) {
			times = dom["time"]["timeValues"].get<std::vector<double>>();
			for (json postParam : dom["postValues"]) {
				if (postParam["postStringId"] == postValue->getStringId()) {
					values = postParam["values"].get<std::vector<double>>();
					break;
				}
			}
		}
	}

	if (values.size() == 0) {
		std::string msg = "processGlobalValuesV2Legacy: number of values are 0 in " + domName + " for " + postValue->getStringId();
		LOG(WARNING) << msg;
		return returnVector;
	}

	for (int i = 0; i < values.size(); i++) {
		double tstep = times[i];
		double val = values[i];
		returnVector.push_back(std::pair(tstep, val));
	}

	return returnVector;
}

json processGlobalValues(std::shared_ptr<PostValues> postValue, std::string domName, std::string wDir, std::string portNumber)
{
	json returnJson;
	std::string port = "";
	if (portNumber != "") {
		port = "_port" + portNumber;
	}

	if (((postValue->getType() == PostValues::GLOBAL) or (postValue->getType() == PostValues::INTEGRAL)) or (postValue->getType() == PostValues::TOTAL_VALUE) or (postValue->getType() == PostValues::GLOBAL_EXTERNAL))
	{
		json postObj = json::object();
		
		std::ifstream resStreamFile;

		if (postValue->getType() != PostValues::GLOBAL_EXTERNAL)
			resStreamFile.open(wDir + "/getdpresults/globals/" + postValue->getStringId() + "_" + domName + "_globals.dat");
		else
			resStreamFile.open(wDir + "/getdpresults/globals/" + postValue->getStringId() + "_globals.dat");

		std::string line;
		if (!resStreamFile.is_open())
		{
			LOG(ERROR) << "could not open result file for read " + postValue->getStringId() + " in domain " + domName;
			return json::object();
		}

		std::vector<double> localVec;
		std::vector<double> timeData;
		std::string previousLine;
		while (resStreamFile.good())
		{
			getline(resStreamFile, line);
			if (line.empty())
				break;

			if (line == previousLine)
				break;

			previousLine = line;

			std::istringstream lString(line);
			double tstep, val;
			lString >> tstep >> val;
			timeData.push_back(tstep);
			localVec.push_back(val);
		}
		if ((timeData[0] == 0.0) and (timeData.size() > 1))
		{
			timeData.erase(timeData.begin());
			localVec.erase(localVec.begin());
		}
		postObj["postName"] = postValue->getName();
		postObj["postStringId"] = postValue->getStringId();
		postObj["values"] = localVec;

		if (returnJson.find("time") == returnJson.end())
		{
			json timeObject;
			timeObject["postName"] = "Time";
			timeObject["timeValues"] = timeData;
			returnJson["time"] = timeObject;
		}
		returnJson["postValues"].push_back(postObj);
	}
	else if ( ((postValue->getType() == PostValues::BOUNDARY_GLOBAL) or (postValue->getType() == PostValues::BOUNDARY_INTEGRAL)) or (postValue->getType() == PostValues::CIRCULATION) )
	{
		json postObj = json::object();

		std::ifstream resStreamFile;
		//resStreamFile.open(wDir + "/getdpresults/globals/" + postValue->getStringId() + "_" + domName + port + "_globals.dat");
		if (postValue->needLoop() ) {
				if (postValue->getStringId() == "acS~{j}~{k}") {
					resStreamFile.open(wDir + "/getdpresults/globals/" + "acS~{j}~{k}" + port + "_globals.dat");
				}
				else {
					resStreamFile.open(wDir + "/getdpresults/globals/" + postValue->getStringId() + "_" + portNumber + "Sur_UNIFPORT_N_" + portNumber + port + "_globals.dat");

				}
				
			}
			else {
			resStreamFile.open(wDir + "/getdpresults/globals/" + postValue->getStringId() + "_" + domName + port + "_globals.dat");
			}
		

		std::string line;
		if (!resStreamFile.is_open())
		{
			std::cout << "could not open result file for read " << postValue->getStringId() << " in boundary " << domName << std::endl;
		}

		std::vector<double> localVec;
		std::vector<double> timeData;
		std::string previousLine;
		while (resStreamFile.good())
		{
			getline(resStreamFile, line);
			if (line.empty())
				break;

			if (line == previousLine)
				break;

			previousLine = line;

			std::istringstream lString(line);
			double tstep, val;
			lString >> tstep >> val;
			timeData.push_back(tstep);
			localVec.push_back(val);
		}
		if ((timeData[0] == 0.0) and (timeData.size() > 1))
		{
			timeData.erase(timeData.begin());
			localVec.erase(localVec.begin());
		}
		postObj["postName"] = postValue->getName();
		postObj["postStringId"] = postValue->getStringId();
		postObj["values"] = localVec;

		if (returnJson.find("time") == returnJson.end())
		{
			json timeObject;
			timeObject["postName"] = "Time";
			timeObject["timeValues"] = timeData;
			returnJson["time"] = timeObject;
		}
		returnJson["postValues"].push_back(postObj);
	}

	return returnJson;
}



json mergeGroupedDomainsInJson(json inpJson)
{
	json outputJson = inpJson;
	json domainsJsonForOutput = json::array();
	json domainsJson = inpJson["domains"];
	// key - group name, value - vector of jsons containing post information for domain
	std::map<std::string, std::vector<json> > jsonsToGroup;
	for (json::iterator it = domainsJson.begin(); it != domainsJson.end(); ++it)
	{
		json domJson = *it;
		if (domJson.at("group").get<std::string>() != "none")
		{
			if (jsonsToGroup.find(domJson.at("group")) == jsonsToGroup.end())
			{
				jsonsToGroup[domJson.at("group")] = { domJson };
			}
			else
			{
				jsonsToGroup[domJson.at("group")].push_back(domJson);
			}
		}
		else
		{
			domainsJsonForOutput.push_back(domJson);
		}
	}


	//loop over groups
	for (std::map<std::string, std::vector<json>>::iterator it = jsonsToGroup.begin(); it != jsonsToGroup.end(); it++)
	{

		// key - post value name. Value - vector of time dependent data
		std::map<std::string, std::vector<double> > postValMap;

		std::map<std::string, std::string > postNameMap;

		json groupedObject = json::object();
		groupedObject["name"] = it->first;
		std::vector<json> jsonVector = it->second;
		//loop over jsons for every group

		for (auto js : jsonVector)
		{
			std::vector<json> postValuesJson = js["postValues"].get<std::vector<json>>();

			if (groupedObject.find("time") == groupedObject.end())
			{
				groupedObject["time"] = js.at("time").get<json>();
			}


			for (auto pvjs : postValuesJson)
			{
				if (postValMap.find(pvjs.at("postStringId")) == postValMap.end())
				{
					postValMap[pvjs.at("postStringId").get<std::string>()] = pvjs.at("values").get<std::vector<double>>();
					postNameMap[pvjs.at("postStringId").get<std::string>()] = pvjs.at("postName").get<std::string>();
				}
				else
				{
					std::vector<double>& a = postValMap[pvjs.at("postStringId")];
					std::vector<double> b = pvjs.at("values").get<std::vector<double>>();
					std::transform(a.begin(), a.end(), b.begin(), a.begin(), std::plus<double>());
				}
			}
		}

		for (auto pv : postValMap)
		{
			json newPv = json::object();
			newPv["postStringId"] = pv.first;
			newPv["postName"] = postNameMap[pv.first];
			newPv["values"] = pv.second;
			groupedObject["postValues"].push_back(newPv);
		}

		domainsJsonForOutput.push_back(groupedObject);
	}

	outputJson["domains"] = domainsJsonForOutput;

	return outputJson;

}

void appendTimeToGlobalsJson(json inpJson, json& globalsJson)
{
	json oldGlobalsJson = globalsJson;
	json newGlobalsJson;
	json domainsJson1 = inpJson["domains"];
	json domainsJson2 = oldGlobalsJson["domains"];
	for (json::iterator it1 = domainsJson1.begin(); it1 != domainsJson1.end(); ++it1)
	{
		for (json::iterator it2 = domainsJson2.begin(); it2 != domainsJson2.end(); ++it2)
		{
			json domJson1 = *it1;
			json domJson2 = *it2;
			if (domJson1.at("name").get<std::string>() == domJson2.at("name").get<std::string>())
			{
				json localDomain;
				localDomain["time"]["postName"] = domJson1["time"]["postName"].get<std::string>();
				std::vector<double> t1 = domJson2["time"]["timeValues"].get<std::vector<double>>();
				std::vector<double> t2 = domJson1["time"]["timeValues"].get<std::vector<double>>();
				t1.insert(std::end(t1), std::begin(t2), std::end(t2));
				bool deleteFirst = false;
				if (t1[0] == 0.0)
				{
					deleteFirst = true;
					t1.erase(t1.begin());
				}
				localDomain["time"]["timeValues"] = t1;

				localDomain["name"] = domJson1.at("name").get<std::string>();
				localDomain["group"] = domJson1.at("group").get<std::string>();

				std::vector<json> postValuesJson1 = domJson1["postValues"].get<std::vector<json>>();
				std::vector<json> postValuesJson2 = domJson2["postValues"].get<std::vector<json>>();
				for (std::vector<json>::iterator itPv1 = postValuesJson1.begin(); itPv1 != postValuesJson1.end(); ++itPv1)
				{
					for (std::vector<json>::iterator itPv2 = postValuesJson2.begin(); itPv2 != postValuesJson2.end(); ++itPv2)
					{
						json localPv;
						json pvJson1 = *itPv1;
						json pvJson2 = *itPv2;
						if (pvJson1.at("postName").get<std::string>() == pvJson2.at("postName").get<std::string>())
						{
							std::vector<double> a = pvJson2["values"].get<std::vector<double>>();
							std::vector<double> b = pvJson1["values"].get<std::vector<double>>();
							a.insert(std::end(a), std::begin(b), std::end(b));
							if (deleteFirst)
								a.erase(a.begin());
							localPv["postName"] = pvJson1.at("postName").get<std::string>();
							localPv["postStringId"] = pvJson1.at("postStringId").get<std::string>();
							localPv["values"] = a;
							localDomain["postValues"].push_back(localPv);
						}
					}
				}
				if (localDomain.find("postValues") != localDomain.end())
					newGlobalsJson["domains"].push_back(localDomain);
			}
		}
	}

	json bcsJson1 = inpJson["bcs"];
	json bcsJson2 = oldGlobalsJson["bcs"];
	for (json::iterator it1 = bcsJson1.begin(); it1 != bcsJson1.end(); ++it1)
	{
		for (json::iterator it2 = bcsJson2.begin(); it2 != bcsJson2.end(); ++it2)
		{
			json bcJson1 = *it1;
			json bcJson2 = *it2;
			if (bcJson1.at("name").get<std::string>() == bcJson2.at("name").get<std::string>())
			{
				json localBc;
				localBc["time"]["postName"] = bcJson1["time"]["postName"].get<std::string>();
				std::vector<double> t1 = bcJson2["time"]["timeValues"].get<std::vector<double>>();
				std::vector<double> t2 = bcJson1["time"]["timeValues"].get<std::vector<double>>();
				t1.insert(std::end(t1), std::begin(t2), std::end(t2));

				bool deleteFirst = false;
				if (t1[0] == 0.0)
				{
					deleteFirst = true;
					t1.erase(t1.begin());
				}

				localBc["time"]["timeValues"] = t1;
				localBc["name"] = bcJson1.at("name").get<std::string>();

				std::vector<json> postValuesJson1 = bcJson1["postValues"].get<std::vector<json>>();
				std::vector<json> postValuesJson2 = bcJson2["postValues"].get<std::vector<json>>();
				for (std::vector<json>::iterator itPv1 = postValuesJson1.begin(); itPv1 != postValuesJson1.end(); ++itPv1)
				{
					for (std::vector<json>::iterator itPv2 = postValuesJson2.begin(); itPv2 != postValuesJson2.end(); ++itPv2)
					{
						json localPv;
						json pvJson1 = *itPv1;
						json pvJson2 = *itPv2;
						if (pvJson1.at("postName").get<std::string>() == pvJson2.at("postName").get<std::string>())
						{
							std::vector<double> a = pvJson2["values"].get<std::vector<double>>();
							std::vector<double> b = pvJson1["values"].get<std::vector<double>>();
							a.insert(std::end(a), std::begin(b), std::end(b));
							if (deleteFirst)
								a.erase(a.begin());
							localPv["postName"] = pvJson1.at("postName").get<std::string>();
							localPv["postStringId"] = pvJson1.at("postStringId").get<std::string>();
							localPv["values"] = a;
							localBc["postValues"].push_back(localPv);
						}
					}
				}
				if (localBc.find("postValues") != localBc.end())
					newGlobalsJson["bcs"].push_back(localBc);
			}
		}
	}
	globalsJson = newGlobalsJson;
}


std::string prepareCsvFromJson(json postJson)
{
	std::string outString;
	json domainsJson = postJson["domains"];
	for (json::iterator it = domainsJson.begin(); it != domainsJson.end(); ++it)
	{
		json domJson = *it;
		std::string domain_header = "Domain: " + domJson["name"].get<std::string>();
		if (domJson.find("role") != domJson.end())
			domain_header.append(", role : " + domJson["role"].get<std::string>());
		outString.append(domain_header + "\n");
		json postValues = domJson["postValues"];

		outString.append("Time, ");
		std::vector< std::vector<double> > postValueData;
		std::vector<double > timeData = domJson["time"]["timeValues"].get<std::vector<double>>();

		for (json::iterator jt = postValues.begin(); jt != postValues.end(); ++jt)
		{
			json postVal = *jt;
			outString.append(postVal["postName"]);
			if (postValues.end() - jt > 1)
				outString.append(", ");
			else
				outString.append("\n");

			postValueData.push_back(postVal["values"].get<std::vector<double>>());
		}

		for (size_t i = 0; i < timeData.size(); i++)
		{
			outString.append(std::to_string(timeData[i]) + ", ");
			for (size_t j = 0; j < postValueData.size(); j++)
			{
				outString.append(std::to_string(postValueData[j][i]));
				if (postValueData.size() - j > 1)
					outString.append(", ");
				else
					outString.append("\n");

			}
		}
	}

	json bcsJson = postJson["bcs"];
	for (json::iterator it = bcsJson.begin(); it != bcsJson.end(); ++it)
	{
		json bcJson = *it;
		std::string boundary_header = "Boundary: " + bcJson["name"].get<std::string>();
		if (bcJson.find("role") != bcJson.end())
			boundary_header.append(", role : " + bcJson["role"].get<std::string>());
		outString.append(boundary_header + "\n");
		json postValues = bcJson["postValues"];

		outString.append("Time, ");
		std::vector< std::vector<double> > postValueData;
		std::vector<double > timeData = bcJson["time"]["timeValues"].get<std::vector<double>>();

		std::vector<size_t> postValueSize;//for each value, needed to store multiple values for each time

		for (json::iterator jt = postValues.begin(); jt != postValues.end(); ++jt)
		{
			json postVal = *jt;
				outString.append(postVal["postName"]);
				if (postValues.end() - jt > 1)
					outString.append(", ");
				else
					outString.append("\n");

			postValueSize.push_back((postVal["values"].size())/ timeData.size());

			postValueData.push_back(postVal["values"].get<std::vector<double>>());
		}


		auto aatest = postValueData;
		for (size_t i = 0; i < timeData.size(); i++)
		{
			outString.append(std::to_string(timeData[i]) + ", ");
			for (size_t j = 0; j < postValueData.size(); j++)
			{
				//size_t i_aux = i;
				for (size_t k = 0; k < postValueSize[j]; k++)
				{
					auto aatest = postValueData[j][k + i * postValueSize[j]];
					outString.append(std::to_string(postValueData[j][k + i * postValueSize[j]]));
					if (postValueData.size() - j > 1)
						outString.append(", ");
					else
						outString.append("\n");
					//i_aux++;
				}

			}
		}


	}
	return outString;
}
