/*
 * PostValues.cpp
 *
 *  Created on: Sep 13, 2017
 *      Author: vadims
 */

#include "PostValues.hpp"
#include <sstream>
#include "postValues.hpp"

std::optional<PostValues::PostValueType> PostValues::getPostType(std::string pt)
{
	if (pt == "NODE_FIELD")
		return PostValueType::NODE_FIELD;
	else if (pt == "BOUNDARY_NODE_FIELD")
		return PostValueType::BOUNDARY_NODE_FIELD;
	else if (pt == "INTEGRAL")
		return PostValueType::INTEGRAL;
	else if (pt == "BOUNDARY_INTEGRAL")
		return PostValueType::BOUNDARY_INTEGRAL;
	else if (pt == "GLOBAL")
		return PostValueType::GLOBAL;
	else if (pt == "BOUNDARY_GLOBAL")
		return PostValueType::BOUNDARY_GLOBAL;
	else if (pt == "RESIDUAL")
		return PostValueType::RESIDUAL;
	else if (pt == "MAX_VAL")
		return PostValueType::MAX_VAL;
	else if (pt == "RESIDUAL_FOR_ADAPTIVE")
		return PostValueType::RESIDUAL_FOR_ADAPTIVE;
	else if (pt == "TOTAL_VALUE")
		return PostValueType::TOTAL_VALUE;
	else if (pt == "BOUNDARY_CELL_FIELD")
		return PostValueType::BOUNDARY_CELL_FIELD;
	else if (pt == "FAR_FIELD")
		return PostValueType::FAR_FIELD;
	else if (pt == "INTERMEDIATE_VOL")
		return PostValueType::INTERMEDIATE_VOL;
	else if (pt == "INTERMEDIATE_SUR")
		return PostValueType::INTERMEDIATE_SUR;
	else if (pt == "CIRCULATION")
		return PostValueType::CIRCULATION;	
	else if (pt == "GLOBAL_EXTERNAL")
		return PostValueType::GLOBAL_EXTERNAL;	  	
	else if (pt == "SCALING_VALUE")
		return PostValueType::SCALING_VALUE;
	else
		return {};
}

int PostValues::getId()
{
	return id;
}

std::string PostValues::getName()
{
	return name;
}

std::string PostValues::getLabel()
{
	return label;
}

std::string PostValues::getSymbol()
{
	return symbol;
}

std::string PostValues::getUnits()
{
	return units;
}

std::string PostValues::getNameNoCommas()
{
	std::string tempName = name;
	replace(tempName.begin(), tempName.end(), ',', ' ');
	replace(tempName.begin(), tempName.end(), '_', ' ');
	return tempName;
}


std::string PostValues::getStringId()
{
	return string_id;
}

PostValues::PostValueType PostValues::getType()
{
	return type;
}

int PostValues::getValueRegister()
{
	return valueRegister;
}  

std::string PostValues::getEquationForScaling()
{
	return equation_for_scaling;
}

std::string PostValues::getEnablingField()
{
	return enable_if_defined;
}


std::string PostValues::getEfield()
{
	return Efield;
}

std::string PostValues::getHfield()
{
	return Hfield;
}

bool PostValues::getSumAllDomains()
{
	return sumAllDomains;
}

bool PostValues::storeInVariable()
{
	return storeInVariable_;
}

bool PostValues::isIntermediate()
{
	return isIntermediate_;
}

std::vector<std::string> PostValues::getScalingConstraints()
{
	return scaling_constraints;
}
bool PostValues::needLoop()
{
	return needLoop_;
}

bool PostValues::hasTwoTerm()
{
	return hasTwoTerm_;
}




std::vector<std::string> PostValues::getTerms()
{
	// ONLY REPLACES FIRST OCCURENCE!!!!!
	std::vector<std::string> returnVec;
	for (auto retString : term)
	{
		for (auto& arg : argList)
		{
			if (arg.getPhys() == physics_key)
			{
				if (retString.find("#" + std::to_string(arg.getId())) != std::string::npos)
					retString.replace(retString.find("#" + std::to_string(arg.getId())),
						2, arg.getReplaceString());
				if (retString.find("#" + std::to_string(arg.getId())) != std::string::npos)
					retString.replace(retString.find("#" + std::to_string(arg.getId())),
						2, arg.getReplaceString());
			}
			else
			{
				if (retString.find("#" + std::to_string(arg.getId())) != std::string::npos)
					retString.replace(retString.find("#" + std::to_string(arg.getId())),
						2, "0");
			}

		}
		returnVec.push_back(retString);
	}

	return returnVec;
}
std::vector<std::string> PostValues::getTerms2()
{
	// ONLY REPLACES FIRST OCCURENCE!!!!!
	std::vector<std::string> returnVec;
	for (auto retString : term2)
	{
		for (auto& arg : argList)
		{
			if (arg.getPhys() == physics_key)
			{
				if (retString.find("#" + std::to_string(arg.getId())) != std::string::npos)
					retString.replace(retString.find("#" + std::to_string(arg.getId())),
						2, arg.getReplaceString());
				if (retString.find("#" + std::to_string(arg.getId())) != std::string::npos)
					retString.replace(retString.find("#" + std::to_string(arg.getId())),
						2, arg.getReplaceString());
			}
			else
			{
				if (retString.find("#" + std::to_string(arg.getId())) != std::string::npos)
					retString.replace(retString.find("#" + std::to_string(arg.getId())),
						2, "0");
			}

		}
		returnVec.push_back(retString);
	}

	return returnVec;
}


bool PostValues::isDefinedInDomain(std::string domKey)
{
	for (auto& defDom : definedDomains)
	{
		std::stringstream ss(defDom);
		std::string item;
		while (std::getline(ss, item, ','))
		{
			item.erase(remove_if(item.begin(), item.end(), isspace), item.end());
			if (item == domKey)
				return true;
		}
	}
	return false;
}
bool PostValues::isDefinedInDomainMultiPort(std::string domKey)
{
	if (domKey.find("Sur_UNIFPORT_N") != std::string::npos) {



		for (auto& defDom : definedDomains)
		{
			std::stringstream ss(defDom);
			std::string item;
			while (std::getline(ss, item, ','))
			{
				item.erase(remove_if(item.begin(), item.end(), isspace), item.end());
				if (item.find("Sur_UNIFPORT_N") != std::string::npos)
					return true;

			}
		}
	}
	return false;
}





std::vector<std::string> PostValues::getDomains()
{
	return definedDomains;
}

std::vector<std::string> PostValues::getSubDomains()
{
	return definedSubDomains;
}


int PostValues::getSmoothing()
{
	return smooth;
}

int PostValues::getNumComponents()
{
	return ncomp;
}

std::string PostValues::getSolverKey()
{
	return solver_key;
}

void PostValues::setPhysicsKey(std::string phys_key)
{
	physics_key = phys_key;
}

void PostValues::setSolverKey(std::string slv_key)
{
	solver_key = slv_key;
}

void PostValues::setExportToVTK(bool expToVTK)
{
	exportToVTK = expToVTK;
}

void PostValues::setEHfields(std::string ef, std::string hf)
{
	Efield = ef;
	Hfield = hf;
}

void PostValues::setStoreInVariable(bool b)
{
	storeInVariable_ = b;
}

void PostValues::setIntermediate(bool b)
{
	isIntermediate_ = b;
}

void PostValues::setNeedLoop(bool b)
{
	needLoop_ = b;
}

void PostValues::setHasTwoTerm(bool b)
{
	hasTwoTerm_ = b;
}

bool PostValues::needsExportToVTK()
{
	return exportToVTK;
}


void PostValues::addArguments(int id, std::string phys, std::string replaceString)
{
	argList.push_back(args(id, phys, replaceString));
}



PostValues::PostValues(std::string str_id, json pv_json)
{
	string_id = str_id;

	if (pv_json.find("term") != pv_json.end())
	{
		for (json::iterator kt = pv_json["term"].begin(); kt != pv_json["term"].end(); ++kt) 
		{
			term.push_back(kt.value());
		}
	}
	if (pv_json.find("term2") != pv_json.end())
	{
		for (json::iterator kt = pv_json["term2"].begin(); kt != pv_json["term2"].end(); ++kt)
		{
			term2.push_back(kt.value());
		}
	}




	name = (pv_json.find("safeName") == pv_json.end()) ? pv_json["name"].get<std::string>() : pv_json["safeName"].get<std::string>();
	
	// Have to create "label" because "name" is actaully safeName. So this will be the actual name
	label = pv_json["name"].get<std::string>();

	if (pv_json.find("symbol") != pv_json.end())
	{
		symbol = pv_json["symbol"].get<std::string>();
	}

	units = (pv_json.find("units") != pv_json.end()) ? pv_json["units"].get<std::string>() : "";

	if (pv_json.find("smoothing") != pv_json.end())
	{
		smooth = pv_json["smoothing"].get<int>();
	}

	if (pv_json.find("components") != pv_json.end())
	{
		ncomp = pv_json["components"].get<int>();
	}
	else
	{
		ncomp = 1;
	}

	if (pv_json.find("type") != pv_json.end())
	{
		auto tp = PostValues::getPostType(pv_json["type"].get<std::string>());
		if (tp.has_value())
			type = tp.value();
		else
		{
			//logger("Post type " + postVal["type"].get<std::string>() + " of field " + jt.key() + " unknown. ", logger::warn);
			type = PostValues::PostValueType::NODE_FIELD;
		}
	}
	else
	{
		//LOG(WARNING) << "Empty post type for field " + jt.key();
		type = PostValues::PostValueType::NODE_FIELD;
	}

	if (pv_json.find("exportToVTK") != pv_json.end())
	{
		exportToVTK = pv_json["exportToVTK"].get<bool>();
	}
	else
		exportToVTK = false;


	if (pv_json.find("valueRegister") != pv_json.end())
	{
		valueRegister = pv_json["valueRegister"].get<int>();
	}

	for (json::iterator kt = pv_json["domains"].begin(); kt != pv_json["domains"].end(); ++kt) {
		definedDomains.push_back(kt.value());
	}

	if (pv_json.find("subdomains") != pv_json.end())
	{
		for (json::iterator kt = pv_json["subdomains"].begin(); kt != pv_json["subdomains"].end(); ++kt) {
			definedSubDomains.push_back(kt.value());
		}
	}
	else
	{
		definedSubDomains.push_back("none");
	}
	if (pv_json.find("args") != pv_json.end())
	{
		for (json::iterator kt = pv_json["args"].begin(); kt != pv_json["args"].end(); ++kt) {
			addArguments(std::stoi(kt.key()),
				kt.value()["physics"].get<std::string>(),
				kt.value()["string"].get<std::string>());
		}
	}

	if ((pv_json.find("EField") != pv_json.end()) and (pv_json.find("HField") != pv_json.end()))
	{
		setEHfields(pv_json["EField"].get<std::string>(), pv_json["HField"].get<std::string>());
	}

	if (pv_json.find("storeInVariable") != pv_json.end())
	{
		setStoreInVariable(pv_json["storeInVariable"].get<bool>());
	}
	else
	{
		setStoreInVariable(false);
	}

	if (pv_json.find("intermediate") != pv_json.end())
	{
		setIntermediate(pv_json["intermediate"].get<bool>());
	}
	else
	{
		setIntermediate(false);
	}
        if (pv_json.find("needLoop") != pv_json.end())
	{
		setNeedLoop(pv_json["needLoop"].get<bool>());
	}
	else
	{
		setNeedLoop(false);
	}
	if (pv_json.find("hasTwoTerm") != pv_json.end())
	{
		setHasTwoTerm(pv_json["hasTwoTerm"].get<bool>());
	}
	else
	{
		setHasTwoTerm(false);
	}

	if (pv_json.find("external") != pv_json.end())
	{
		external = true;
		external_data = pv_json["external"].get<json>();
	}
	else
	{
		external = false;
		external_data = json::object();
	}
        if (pv_json.find("equationForScaling") != pv_json.end())
	{
		equation_for_scaling = pv_json["equationForScaling"].get<std::string>();
	}
	if (pv_json.find("enableIfDefined") != pv_json.end())
	{
		enable_if_defined = pv_json["enableIfDefined"].get<std::string>();
	}

	if (pv_json.find("scalingConstraints") != pv_json.end())
	{
		scaling_constraints = pv_json["scalingConstraints"].get<std::vector<std::string>>();
	}

	if (pv_json.find("sumAllDomains") != pv_json.end())
	{
		sumAllDomains = pv_json["sumAllDomains"].get<bool>();
	}
	else
	{
		sumAllDomains = false;
	}
}

PostValues& PostValues::operator=(const PostValues& other)
{
	name = other.name;
	label = other.label;
	symbol = other.symbol;
	units = other.units;
	term = other.term;
	definedDomains = other.definedDomains;
	definedSubDomains = other.definedSubDomains;
	string_id = other.string_id;
	solver_key = other.solver_key;
	physics_key = other.physics_key;
	id = other.id;
	smooth = other.smooth;
	ncomp = other.ncomp;
	valueRegister = other.valueRegister;
	exportToVTK = other.exportToVTK;
	external = other.external;
	enabled = other.enabled;
	storeInVariable_ = other.storeInVariable_;
	isIntermediate_ = other.isIntermediate_;
	type = other.type;
	argList = other.argList;
	external_data = other.external_data;
	sumAllDomains = other.sumAllDomains;
	return *this;
}

PostValues::PostValues(const PostValues& other)
{
	name = other.name;
	label = other.label;
	symbol = other.symbol;
	units = other.units;
	term = other.term;
	definedDomains = other.definedDomains;
	definedSubDomains = other.definedSubDomains;
	string_id = other.string_id;
	solver_key = other.solver_key;
	physics_key = other.physics_key;
	id = other.id;
	smooth = other.smooth;
	ncomp = other.ncomp;
	valueRegister = other.valueRegister;
	exportToVTK = other.exportToVTK;
	external = other.external;
	enabled = other.enabled;
	storeInVariable_ = other.storeInVariable_;
	isIntermediate_ = other.isIntermediate_;
	type = other.type;
	argList = other.argList;
	external_data = other.external_data;
	sumAllDomains = other.sumAllDomains;
}

bool PostValues::isExternal()
{
	return external;
}

json PostValues::getExternalData()
{
	return external_data;
}

bool PostValues::isEnabled()
{
	return enabled;
}

void PostValues::setEnabled(bool val)
{
	enabled = val;
}
