/*!\file:  issm.cpp
 * \brief: ISSM main parallel program
 */ 

#include "../issm.h"
#include "../include/globals.h"

int main(int argc,char **argv){

	/*I/O: */
	FILE *output_fid       = NULL;
	FILE *petscoptionsfid  = NULL;
	bool  waitonlock       = false;
	bool  dakota_analysis,control_analysis,tao_analysis;

	/*FemModel: */
	FemModel *femmodel = NULL;

	/*configuration: */
	void (*solutioncore)(FemModel*)=NULL; //core solution function pointer
	int* analyses=NULL;
	int  numanalyses;
	int  solution_type;

	/*File names*/
	char *lockfilename   = NULL;
	char *binfilename    = NULL;
	char *outbinfilename = NULL;
	char *petscfilename  = NULL;

	/*time*/
	IssmPDouble   start, finish;
	IssmPDouble   start_core, finish_core;
	IssmPDouble   start_init, finish_init;
	int      ierr;

	/*profiling*/   
	bool profiling = false;
	IssmPDouble Time_start, Flops_start;
	IssmPDouble Solution_time, Memory_use, Current_flops;

	ISSMBOOT();

	/*Initialize environments: Petsc, MPI, etc...: */
	#ifdef _HAVE_PETSC_
	ierr=PetscInitialize(&argc,&argv,(char*)0,"");  
	if(ierr) _error2_("Could not initialize Petsc");
	#else
	#ifdef _HAVE_MPI_
	MPI_Init(&argc,&argv);
	#endif
	#endif

	#ifdef _HAVE_MPI_
	MPI_Barrier(MPI_COMM_WORLD); start=MPI_Wtime();
	#else
	start=(IssmPDouble)clock();
	#endif

	/*Size and rank: */
	#ifdef _HAVE_MPI_
	MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);  
	MPI_Comm_size(MPI_COMM_WORLD,&num_procs); 
	#endif

	/*First process inputs*/
	_pprintLine_("");
	_pprintLine_("Ice Sheet System Model (" << PACKAGE_NAME << ") version " << PACKAGE_VERSION);
	_pprintLine_("(website: " << PACKAGE_URL << " contact: " << PACKAGE_BUGREPORT << ")");
	_pprintLine_("");
	ProcessArguments(&solution_type,&binfilename,&outbinfilename,&petscfilename,&lockfilename,argc,argv);

	/*out of solution_type, figure out types of analyses needed in the femmodel: */
	AnalysisConfiguration(&analyses,&numanalyses,solution_type);
	
	/*Create femmodel, using input file: */
	#ifdef _HAVE_MPI_
	MPI_Barrier(MPI_COMM_WORLD); start_init=MPI_Wtime();
	#else
	start_init=(IssmPDouble)clock();
	#endif
	femmodel=new FemModel(binfilename,outbinfilename,solution_type,analyses,numanalyses);
	
	/*get type of solution we are going to run: */
	CorePointerFromSolutionEnum(&solutioncore,femmodel->parameters,solution_type);

	/*Open output file once for all*/
	output_fid=pfopen(outbinfilename,"wb");
	femmodel->parameters->SetParam(output_fid,OutputFilePointerEnum);

	/*add petsc options to parameters: */
	petscoptionsfid=pfopen(petscfilename,"r");
	ParsePetscOptionsx(femmodel->parameters,petscoptionsfid);
	pfclose(petscoptionsfid,petscfilename);

	/*get parameters: */
	femmodel->parameters->FindParam(&waitonlock,SettingsWaitonlockEnum);
	femmodel->parameters->FindParam(&dakota_analysis,QmuIsdakotaEnum);
	femmodel->parameters->FindParam(&control_analysis,InversionIscontrolEnum);
	femmodel->parameters->FindParam(&tao_analysis,InversionTaoEnum);
	femmodel->parameters->FindParam(&profiling,DebugProfilingEnum); 

	#ifdef _HAVE_MPI_
	MPI_Barrier(MPI_COMM_WORLD); finish_init=MPI_Wtime();
	#else
	finish_init=(IssmPDouble)clock();
	#endif

	_pprintLine_("call computational core:");
	#ifdef _HAVE_MPI_
	MPI_Barrier(MPI_COMM_WORLD); start_core=MPI_Wtime( );
	#else
	start_core=(IssmPDouble)clock();
	#endif
	
	if(profiling)ProfilingStart(&Time_start,&Flops_start);

	if(dakota_analysis){
		#ifdef _HAVE_DAKOTA_
		Dakotax(femmodel);
		#else
		_error2_("ISSM was not compiled with dakota support, cannot carry out dakota analysis!");
		#endif
	}
	else if(control_analysis){
		#ifdef _HAVE_CONTROL_
		if(tao_analysis)
		 controltao_core(femmodel);
		else
		 control_core(femmodel);
		#else
		_error2_("ISSM was not compiled with control support, cannot carry out dakota analysis!");
		#endif
	}
	else{
		solutioncore(femmodel);
	}

	if(profiling){
		ProfilingEnd(&Solution_time,&Memory_use,&Current_flops,Time_start,Flops_start);
		femmodel->results->AddObject(new DoubleExternalResult(femmodel->results->Size()+1, ProfilingSolutionTimeEnum, Solution_time, 1, 0));
		femmodel->results->AddObject(new DoubleExternalResult(femmodel->results->Size()+1, ProfilingCurrentMemEnum, Memory_use, 1, 0));
		femmodel->results->AddObject(new DoubleExternalResult(femmodel->results->Size()+1, ProfilingCurrentFlopsEnum, Current_flops, 1, 0));
	}


	#ifdef _HAVE_MPI_
	MPI_Barrier(MPI_COMM_WORLD); finish_core=MPI_Wtime( );
	#else
	finish_core=(IssmPDouble)clock();
	#endif

	_pprintLine_("write results to disk:");
	OutputResultsx(femmodel->elements, femmodel->nodes, femmodel->vertices, femmodel->loads, femmodel->materials, femmodel->parameters,femmodel->results);

	/*Close output and petsc options file and write lock file if requested*/
	pfclose(output_fid,lockfilename);
	if (waitonlock>0){
		_pprintLine_("write lock file:");
		WriteLockFile(lockfilename);
	}

	/*Free resources */
	xDelete<int>(analyses);
	xDelete<char>(lockfilename);
	xDelete<char>(binfilename);
	xDelete<char>(outbinfilename);
	xDelete<char>(petscfilename);
	delete femmodel;

	/*Get finish time and close*/
	#ifdef _HAVE_MPI_
	MPI_Barrier(MPI_COMM_WORLD); finish = MPI_Wtime( );
	_pprintLine_("");
	_pprintLine_("   "<<setw(40)<<left<<"FemModel initialization elapsed time:"<<finish_init-start_init);
	_pprintLine_("   "<<setw(40)<<left<<"Core solution elapsed time:"<<finish_core-start_core);
	_pprintLine_("");
	_pprintLine_("   Total elapsed time:"<<int((finish-start)/3600)<<" hrs "<<int(int(finish-start)%3600/60)<<" min "<<int(finish-start)%60<<" sec");
	_pprintLine_("");
	#else
	finish=(IssmPDouble)clock();
	_pprintLine_("");
	_pprintLine_("   "<<setw(40)<<left<<"FemModel initialization elapsed time:"<<(finish_init-start_init)/CLOCKS_PER_SEC);
	_pprintLine_("   "<<setw(40)<<left<<"Core solution elapsed time:"<<(finish_core-start_core)/CLOCKS_PER_SEC);
	_pprintLine_("");
	_pprintLine_("   Total elapsed time:"
				<<int((finish-start)/CLOCKS_PER_SEC/3600)<<" hrs "
				<<int(int(finish-start)/CLOCKS_PER_SEC%3600/60)<<" min "
				<<int(finish-start)/CLOCKS_PER_SEC%60<<" sec");
	_pprintLine_("");
	#endif
	
	#ifdef _HAVE_PETSC_
	_pprintLine_("closing MPI and Petsc");
	PetscFinalize(); 
	#else
	#ifdef _HAVE_MPI_
	_pprintLine_("closing MPI and Petsc");
	MPI_Finalize();
	#endif
	#endif
	
	/*end module: */
	ISSMEND();

	return 0; //unix success return;
}
