#include "globalResults.h"

// This function a member of the GetdpSetup class only because of the getBoundaryGroup and getDomainGroup functions
GlobalResults::GlobalResults(std::vector<Physics> casePhysics, std::string wDir) 
{
	json postValueArray = json::object();


	// Aggregate all postValues, domains and boundaries, we will check if they contain anything later
	for (Physics phys : casePhysics) {
		for (Domains_ domain : phys.getDomainsList()) {
			allDomains.insert(domain);
		}

		for (Bcs_ boundary : phys.getBoundariesList()) {
			allBoundaries.insert(boundary);
		}

		for (std::shared_ptr<PostValues> post : phys.getEnabledPostValueList()) {
			if (post->isIntermediate())
				continue;

			switch (post->getType()) {
			case PostValues::GLOBAL:
			case PostValues::INTEGRAL:
			case PostValues::TOTAL_VALUE:
			case PostValues::GLOBAL_EXTERNAL:
			case PostValues::BOUNDARY_GLOBAL:
			case PostValues::BOUNDARY_INTEGRAL:
			case PostValues::CIRCULATION:
				allPostValues.insert(post);
			}
		}
	}

	for (std::shared_ptr<PostValues> post : allPostValues) {
		json postValueObj = json::object();

		postValueObj["name"] = post->getLabel();
		postValueObj["units"] = post->getUnits();
		postValueObj["domainValues"] = json::array();

		std::vector<double> timeValues;

		// Use this to check if a postvalue has any domains or boundaries where it is defined.
		// Do not add it to the output if there are no values in any domain or boundary.
		bool isDefinedAnywhere = false;

		for (Domains_ domain : allDomains) {
			// This is needed to avoid writing the same PostValues::TOTAL_VALUE (All_Domains) for each domain
			bool isAllDomains = false;

			if (post->isDefinedInDomain(getDomainGroup(domain))) {
				isDefinedAnywhere = true;
				json domainObj = json::object();

				std::vector<std::pair<double, double>> values;

				if (post->getType() == PostValues::TOTAL_VALUE) {
					values = processGlobalValuesV2(post, "", wDir);
					domainObj["name"] = "All_Domains";
					domainObj["group"] = "none";
					isAllDomains = true;
				}
				else {
					values = processGlobalValuesV2(post, domain->getName(), wDir);

					if (domain->isPointDomain()) {
						// This is the special group domain with the "..."
						domainObj["blockName"] = domain->getGroupKey();
						domainObj["blockGroup"] = "none";
					}
					else {
						// This domain is a normal one
						domainObj["blockName"] = domain->getName();
						domainObj["blockGroup"] = domain->getGroupKey();
					}
				}

				domainObj["values"] = json::array();


				if (timeValues.empty()) {
					// Populate time values for the first time
					for (std::pair val : values) {
						timeValues.push_back(val.first);
						domainObj["values"].push_back(val.second);
					}
				}
				else {
					// Time values already populated, check that they match up
					int i = 0;
					for (std::pair val : values) {
						if (!timeValues[i] == val.first) {
							throw cenos_exception("Time values in " + post->getStringId() + " do not match previous ones");
						}
						domainObj["values"].push_back(val.second);
						i++;
					}
				}

				postValueObj["domainValues"].push_back(domainObj);
				if (isAllDomains) {
					break;
				}

			}
		}

		for (Bcs_ boundary : allBoundaries) {
			if (post->isDefinedInDomain(getBoundaryGroup(boundary))) {
				isDefinedAnywhere = true;
				json domainObj = json::object();

				domainObj["blockName"] = boundary->getName();
				domainObj["blockGroup"] = "none";


				std::vector<std::pair<double, double>> values;
				values = processGlobalValuesV2(post, boundary->getName(), wDir);
				domainObj["values"] = json::array();

				if (timeValues.empty()) {
					// Populate time values for the first time
					for (std::pair val : values) {
						timeValues.push_back(val.first);
						domainObj["values"].push_back(val.second);
					}
				}
				else {
					// Time values already populated, check that they match up
					int i = 0;
					for (std::pair val : values) {
						if (!timeValues[i] == val.first) {
							throw cenos_exception("Time values in " + post->getStringId() + " do not match previous ones");
						}
						domainObj["values"].push_back(val.second);
						i++;
					}
				}

				postValueObj["domainValues"].push_back(domainObj);
			}
		}

		json timeValueObj;
		if (misc::getCenosApp() == "ANTENNAS") {
			// Because the frequencies are in MHz, convert them to Hz
			int k = 1000000; // 10^6
			std::transform(timeValues.begin(), timeValues.end(), timeValues.begin(), [k](double& c) { return c * k; });

			timeValueObj = {
				{"name", "Frequency"},
				{"units", "Hz"},
				{"values", timeValues},
			};
		}
		else {
			timeValueObj = {
				{"name", "Time"},
				{"units", "s"},
				{"values", timeValues},
			};
		}

		if (isDefinedAnywhere) {
			postValueArray[post->getStringId()]["x"] = timeValueObj;
			postValueArray[post->getStringId()]["y"] = postValueObj;
		}
	}
	return postValueArray;
}

GlobalResults::~GlobalResults()
{
}