/**
 *  \brief     implementation of setup class for getdp.
 *  \details   implementation of setup class for getdp.
 *  \author    vadims
 *  \date      Dec 05, 2019
 *  \bug       Not all memory is freed when deleting an object of this class.
 *  \warning   Improper use can crash your application
 *  \copyright CENOS LLC
 */

#ifndef GETDPSETUP_HPP_
#define GETDPSETUP_HPP_

#include <boost/format.hpp>
#include "setup/Setup.hpp"
#include "setup/Flag.hpp" 
#include "GetdpNetwork.hpp" 
#include <sstream> //
#include <iostream>
#include <vector>

class GetdpSetup : public Setup
{

public:
	std::string isA() override;
	void writeSetup() override;
	void writePostSetup() override;
	void readConfigurations() override;
	messagedata calculate() override;
	messagedata postProcess() override;
	messagedata convertCaseNewResultJson();
	void outputGlobals() override;
	void outputResultsJson(bool isLegacyCase);
	void initialize() override;
	bool remesh() override;
	bool update() override;
	void finalize() override;
	void stop() override;
	void validateInput() override;

	//Node config;

	void readSolverConfig();
	Numerics::ALGO GetdpSetup::getCouplingType();
	std::string getDomainGroup(Domains_);
	std::string getBoundaryGroup(Bcs_);
	bool copyResFiles();

private:
	enum PostType 
	{
		SOLUTION = 0,
		RUNTIME = 1,
		ENDTIME = 2
	};
	struct constraint {
		std::string name;
		std::string region;
		std::string solverKey;
		std::string limitedTo;
		std::string assignFromResolution;
		constraint(std::string name_, std::string solverKey_) :
			name(name_), solverKey(solverKey_) {};
	};

	struct boundaryGroup {
		std::string key;
		std::string groupName;
		std::string solverKey;
		std::map<std::string, std::vector<std::string>> subgroups;
		bool needsCohomologyCut;
		boundaryGroup(std::string key_, std::string solverKey_) :
			key(key_), solverKey(solverKey_) {};
	};


	struct domainGroup {
		std::string key;
		std::string solverKey;
		std::pair<std::string, std::vector<std::string>> oneSideOf;
		std::map<std::string, std::vector<std::string>> subgroups;  // required if boundaries in domain or domain itself has to be grouped with others
		domainGroup(std::string key_, std::string solverKey_) :
			key(key_), solverKey(solverKey_) {};
	};

	bool hasTransientConstraints;
	std::vector<double> writtenTimeSteps;
	std::vector<boundaryGroup> boundaryGroups;
	std::vector<domainGroup> domainGroups;
	std::vector<constraint> constraints;
	std::vector<std::string> case_constants;
	std::vector<GetdpNetwork> networks;
	json globalsJson;
	bool initial_step; //flag for first time step in simulation
	float current_time;
	float end_time;
	float current_timestep;
	std::vector<float> timeStepsForMotion;

	void writeMain(PostType postType);
	void writeJakobian();
	void writeIntegration();
	void writeGroup();
	void writeFormulation();
	void writeFunctionSpace();
	void writeUserFunction();
	void writeFunction();
	void WriteConstraint();
	void writeResolution();
	void WritePostProc();
	void WritePostOp(PostType postType);

	std::vector<double> getTimeList(PostType postType);
	void addNetworkDomain();


	//reads config and returns config json
	//for particular physics
	void readConstraints(json solverConfig);
	void readBoundaryGroups(json solverConfig);
	void readBoundaryCohomology(json solverConfig);
	void readNetworks(json solverConfig);
	void readDomainGroups(json solverConfig);
	void readFlags(json solverConfig);
	void readGlobalsJson();

	float readLastParameterValue(std::string varName, bool& foundVariableFile);

	bool isConstraint(std::string);
	bool isCaseConstant(std::string);
	bool hasRestart();
	void setFlags();

	void updateBoundaryConditions();
	void updateMeshEntities();
	void convertResults();
	json getGlobalPostValuesJson(std::vector<Physics> casePhysics, std::string wDir, bool isLegacyCase);
	json getBlockGroupsJson();
};

#endif // GETDPSETUP_HPP_