/*!\file: CreateDataSets
 * \brief general driver for creating all datasets that make a finite element iomodel
 */ 

#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif

#include "../../classes/classes.h"
#include "../../shared/shared.h"
#include "../MeshPartitionx/MeshPartitionx.h"
#include "./ModelProcessorx.h"

void CreateDataSets(Elements** pelements,Nodes** pnodes, Vertices** pvertices, Materials** pmaterials, Constraints** pconstraints, Loads** ploads,Parameters** pparameters,IoModel* iomodel,char* rootpath,const int solution_type,const int analysis_type,const int nummodels,int analysis_counter){

	Elements   *elements   = NULL;
	Materials  *materials  = NULL;
	Parameters *parameters = NULL;

	/*Process Finite Element Mesh*/

	/*Partition Elements and Nodes*/
	ElementsAndVerticesPartitioning(&iomodel->my_elements,&iomodel->my_vertices,iomodel);

	/*Create elements, vertices and materials, independent of analysis_type: */
	CreateElementsVerticesAndMaterials(pelements, pvertices, pmaterials, iomodel,nummodels);

	/*Recover elements and materials, for future update: */
	elements  = *pelements;
	materials = *pmaterials;

	/*Creates Nodes and constraints datasets if empty*/
	if(!*pnodes)       *pnodes       = new Nodes();
	if(!*pconstraints) *pconstraints = new Constraints();
	if(!*ploads)       *ploads       = new Loads();

	/*Now, branch onto analysis dependent model generation: */
	switch(analysis_type){

		#ifdef _HAVE_STRESSBALANCE_
		case StressbalanceAnalysisEnum:
			CreateNodesStressbalance(pnodes,iomodel);
			CreateConstraintsStressbalance(pconstraints,iomodel);
			CreateLoadsStressbalance(ploads,iomodel);
			UpdateElementsStressbalance(elements,iomodel,analysis_counter,analysis_type);
			break;

		case StressbalanceVerticalAnalysisEnum:
			CreateNodesStressbalanceVertical(pnodes, iomodel);
			CreateConstraintsStressbalanceVertical(pconstraints,iomodel);
			CreateLoadsStressbalanceVertical(ploads,iomodel);
			UpdateElementsStressbalanceVertical(elements,iomodel,analysis_counter,analysis_type);
			break;

		case StressbalanceSIAAnalysisEnum:
			CreateNodesStressbalanceSIA(pnodes, iomodel);
			CreateConstraintsStressbalanceSIA(pconstraints,iomodel);
			CreateLoadsStressbalanceSIA(ploads,iomodel);
			UpdateElementsStressbalanceSIA(elements,iomodel,analysis_counter,analysis_type);
			break;
		#endif

		#ifdef _HAVE_HYDROLOGY_
		case HydrologyShreveAnalysisEnum:
			CreateNodesHydrologyShreve(pnodes, iomodel);
			CreateConstraintsHydrologyShreve(pconstraints,iomodel);
			CreateLoadsHydrologyShreve(ploads,iomodel);
			UpdateElementsHydrologyShreve(elements,iomodel,analysis_counter,analysis_type);
			break;
		case HydrologyDCInefficientAnalysisEnum:
			CreateNodesHydrologyDCInefficient(pnodes, iomodel);
			CreateConstraintsHydrologyDCInefficient(pconstraints,iomodel);
			CreateLoadsHydrologyDCInefficient(ploads,iomodel);
			UpdateElementsHydrologyDCInefficient(elements,iomodel,analysis_counter,analysis_type);
			break;
		case HydrologyDCEfficientAnalysisEnum:
			CreateNodesHydrologyDCEfficient(pnodes, iomodel);
			CreateConstraintsHydrologyDCEfficient(pconstraints,iomodel);
			CreateLoadsHydrologyDCEfficient(ploads,iomodel);
			UpdateElementsHydrologyDCEfficient(elements,iomodel,analysis_counter,analysis_type);
			break;
		#endif

		#ifdef _HAVE_THERMAL_
		case ThermalAnalysisEnum:
			CreateNodesThermal(pnodes, iomodel);
			CreateConstraintsThermal(pconstraints,iomodel);
			CreateLoadsThermal(ploads,iomodel);
			UpdateElementsThermal(elements,iomodel,analysis_counter,analysis_type);
			break;

		case EnthalpyAnalysisEnum:
			CreateNodesEnthalpy(pnodes, iomodel);
			CreateConstraintsEnthalpy(pconstraints,iomodel);
			CreateLoadsEnthalpy(ploads,iomodel);
			UpdateElementsEnthalpy(elements,iomodel,analysis_counter,analysis_type);
			break;

		case MeltingAnalysisEnum:
			CreateNodesMelting(pnodes, iomodel);
			CreateConstraintsMelting(pconstraints,iomodel);
			CreateLoadsMelting(ploads,iomodel);
			UpdateElementsMelting(elements,iomodel,analysis_counter,analysis_type);
			break;
		#endif

		#ifdef _HAVE_BALANCED_
		case BalancethicknessAnalysisEnum:
			CreateNodesBalancethickness(pnodes, iomodel);
			CreateConstraintsBalancethickness(pconstraints,iomodel);
			CreateLoadsBalancethickness(ploads,iomodel);
			UpdateElementsBalancethickness(elements,iomodel,analysis_counter,analysis_type);
			break;
		case BalancevelocityAnalysisEnum:
			CreateNodesBalancevelocity(pnodes, iomodel);
			CreateConstraintsBalancevelocity(pconstraints,iomodel);
			CreateLoadsBalancevelocity(ploads,iomodel);
			UpdateElementsBalancevelocity(elements,iomodel,analysis_counter,analysis_type);
			break;
		#endif

		#ifdef _HAVE_GIA_
		case GiaAnalysisEnum:
			CreateNodesGia(pnodes, iomodel);
			CreateConstraintsGia(pconstraints,iomodel);
			CreateLoadsGia(ploads,iomodel);
			UpdateElementsGia(elements,iomodel,analysis_counter,analysis_type);
			break;
		#endif

		#ifdef _HAVE_SLOPE_
		case BedSlopeAnalysisEnum:
			CreateNodesBedSlope(pnodes, iomodel);
			CreateConstraintsBedSlope(pconstraints,iomodel);
			CreateLoadsBedSlope(ploads,iomodel);
			UpdateElementsBedSlope(elements,iomodel,analysis_counter,analysis_type);
			break;

		case SurfaceSlopeAnalysisEnum:
			CreateNodesSurfaceSlope(pnodes, iomodel);
			CreateConstraintsSurfaceSlope(pconstraints,iomodel);
			CreateLoadsSurfaceSlope(ploads,iomodel);
			UpdateElementsSurfaceSlope(elements,iomodel,analysis_counter,analysis_type);
			break;
		#endif

		#ifdef _HAVE_MASSTRANSPORT_
		case MasstransportAnalysisEnum:
			CreateNodesMasstransport(pnodes, iomodel);
			CreateConstraintsMasstransport(pconstraints,iomodel);
			CreateLoadsMasstransport(ploads,iomodel);
			UpdateElementsMasstransport(elements,iomodel,analysis_counter,analysis_type);
			break;
		case FreeSurfaceTopAnalysisEnum:
			CreateNodesFreeSurfaceTop(pnodes, iomodel);
			CreateConstraintsFreeSurfaceTop(pconstraints,iomodel);
			CreateLoadsFreeSurfaceTop(ploads,iomodel);
			UpdateElementsFreeSurfaceTop(elements,iomodel,analysis_counter,analysis_type);
			break;
		case FreeSurfaceBaseAnalysisEnum:
			CreateNodesFreeSurfaceBase(pnodes, iomodel);
			CreateConstraintsFreeSurfaceBase(pconstraints,iomodel);
			CreateLoadsFreeSurfaceBase(ploads,iomodel);
			UpdateElementsFreeSurfaceBase(elements,iomodel,analysis_counter,analysis_type);
			break;
		#endif

		default:
			_error_("analysis_type: " << EnumToStringx(analysis_type) << " not supported yet!");
	}

	/*Update Elements and Materials For Control methods*/
	#ifdef _HAVE_CONTROL_
	UpdateElementsAndMaterialsControl(elements,materials,iomodel);
	#endif

	/*Generate objects that are not dependent on any analysis_type: */
	CreateParameters(pparameters,iomodel,rootpath,solution_type,analysis_type,analysis_counter);

	/*Update Elements in case we are running a transient solution: */
	#ifdef _HAVE_TRANSIENT_
	parameters=*pparameters;
	if(analysis_counter==(nummodels-1)&& solution_type==TransientSolutionEnum){
		UpdateElementsTransient(elements,parameters,iomodel,analysis_counter,analysis_type);
	}
	#endif

	/*Sort datasets: */
	SortDataSets(pelements,pnodes,pvertices, ploads, pmaterials, pconstraints, pparameters);

	/* Update counters, because we have created more nodes, loads and
	 * constraints, and ids for objects created in next call to CreateDataSets
	 * will need to start at the end of the updated counters: */
	UpdateCounters(iomodel,pnodes,ploads,pconstraints);
}
