/*!\file Profiler.c
 * \brief: implementation of the Profiler object
 */

/*Include files: {{{*/
#ifdef HAVE_CONFIG_H
	#include <config.h>
#else
#error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!"
#endif

#include "./Profiler.h"
/*}}}*/

/*Profiler constructors and destructors:*/
/*FUNCTION Profiler::Profiler() default constructor {{{*/
Profiler::Profiler(){
		 this->time=new Parameters();
		 this->flops=new Parameters();
		 this->memory=new Parameters();
}
/*}}}*/
/*FUNCTION Profiler::~Profiler(){{{*/
Profiler::~Profiler(){
	delete time;
	delete flops;
	delete memory;
}
/*}}}*/

/*Object virtual functions definitions:*/
/*FUNCTION Profiler::Echo{{{*/
void Profiler::Echo(void){

	_printLine_("Profiler:");
	_printLine_("   time tags: ");
	this->time->Echo();

}
/*}}}*/
/*FUNCTION Profiler::DeepEcho{{{*/
void Profiler::DeepEcho(void){

	_printLine_("Profiler:");
	_printLine_("   time tags: ");
	this->time->DeepEcho();

}
/*}}}*/
/*FUNCTION Profiler::Id{{{*/
int    Profiler::Id(void){ return -1; }
/*}}}*/
/*FUNCTION Profiler::ObjectEnum{{{*/
int Profiler::ObjectEnum(void){

	return ProfilerEnum;

}
/*}}}*/

/*Profiler routines:*/
/*FUNCTION Profiler::Tag {{{*/
void  Profiler::Tag(int tagenum,bool dontmpisync){

	IssmDouble t;
	IssmDouble f;
	IssmDouble m;

	/*If mpisync requested, make sure all the cpus are at the same point 
	 *in the execution: */
	if(!dontmpisync){
		#ifdef _HAVE_MPI_
		MPI_Barrier(IssmComm::GetComm()); 
		#endif
	}

	/*Capture time: */
	#ifdef _HAVE_MPI_
	t=MPI_Wtime();
	#else
	t=(IssmPDouble)clock();
	#endif

	/*Capture flops: */
	#ifdef _HAVE_PETSC_
		PetscGetFlops(&f);
		PetscMemoryGetCurrentUsage(&m);
	#else
		/*do nothing for now:*/
	#endif

	/*Plug into this->time: */
	this->time->AddObject(new DoubleParam(tagenum,t));
	this->flops->AddObject(new DoubleParam(tagenum,f));
	this->memory->AddObject(new DoubleParam(tagenum,m));

}
/*}}}*/
/*FUNCTION Profiler::DeltaTime {{{*/
IssmDouble  Profiler::DeltaTime(int inittag, int finaltag){

	IssmDouble init, final;
	this->time->FindParam(&init,inittag);
	this->time->FindParam(&final,finaltag);

	#ifdef _HAVE_MPI_
	return final-init;
	#else
	return (final-init)/CLOCKS_PER_SEC;
	#endif
}
/*}}}*/
/*FUNCTION Profiler::DeltaFlops {{{*/
IssmDouble  Profiler::DeltaFlops(int inittag, int finaltag){

	IssmDouble init, final;
	this->flops->FindParam(&init,inittag);
	this->flops->FindParam(&final,finaltag);

	return final-init;
}
/*}}}*/
/*FUNCTION Profiler::DeltaTimeModHour {{{*/
int Profiler::DeltaTimeModHour(int inittag, int finishtag){

	IssmDouble init, finish;
	this->time->FindParam(&init,inittag);
	this->time->FindParam(&finish,finishtag);

	#ifdef _HAVE_MPI_
	return int((reCast<int,IssmDouble>(finish-init))/3600);
	#else
	return int((reCast<int,IssmDouble>(finish-init))/CLOCKS_PER_SEC/3600);
	#endif

}
/*}}}*/
/*FUNCTION Profiler::DeltaTimeModMin {{{*/
int Profiler::DeltaTimeModMin(int inittag, int finishtag){

	IssmDouble init, finish;
	this->time->FindParam(&init,inittag);
	this->time->FindParam(&finish,finishtag);

	#ifdef _HAVE_MPI_
	return int(int(reCast<int,IssmDouble>(finish-init))%3600/60);
	#else
	return int(int(reCast<int,IssmDouble>(finish-init))/CLOCKS_PER_SEC%3600/60);
	#endif
}
/*}}}*/
/*FUNCTION Profiler::DeltaTimeModSec {{{*/
int Profiler::DeltaTimeModSec(int inittag, int finishtag){

	IssmDouble init, finish;
	this->time->FindParam(&init,inittag);
	this->time->FindParam(&finish,finishtag);

	#ifdef _HAVE_MPI_
	return int(reCast<int,IssmDouble>(finish-init))%60;
	#else
	return int(reCast<int,IssmDouble>(finish-init))/CLOCKS_PER_SEC%60;
	#endif
}
/*}}}*/
/*FUNCTION Profiler::Memory {{{*/
IssmDouble  Profiler::Memory(int tag){

	IssmDouble m;
	this->memory->FindParam(&m,tag);

	return m;
}
/*}}}*/
