/*!\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"
#include "./Params/Parameters.h"
#include "./Params/DoubleParam.h"
#include "../toolkits/toolkits.h"
/*}}}*/

/*Profiler constructors and destructors:*/
Profiler::Profiler(){/*{{{*/
		 this->time=new Parameters();
		 this->flops=new Parameters();
		 this->memory=new Parameters();
}
/*}}}*/
Profiler::~Profiler(){/*{{{*/
	delete time;
	delete flops;
	delete memory;
}
/*}}}*/
Object* Profiler::copy(){/*{{{*/
	/*First do simple copy: */
	Profiler* output=new Profiler();
	delete output->time;
	delete output->flops;
	delete output->memory;

	/*Now for deep copy: */
	output->time=(Parameters*)this->time->Copy();
	output->flops=(Parameters*)this->flops->Copy();
	output->memory=(Parameters*)this->memory->Copy();

	return (Object*)output;
}
/*}}}*/

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

	_printf_("Profiler:\n");
	_printf_("   time tags: \n");
	this->time->Echo();

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

	_printf_("Profiler:\n");
	_printf_("   time tags: \n");
	this->time->DeepEcho();

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

	return ProfilerEnum;

}
/*}}}*/

/*Profiler routines:*/
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){
		ISSM_MPI_Barrier(IssmComm::GetComm()); 
	}

	/*Capture time: */
	#ifdef _HAVE_MPI_
	t=ISSM_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));

}
/*}}}*/
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
}
/*}}}*/
IssmDouble  Profiler::DeltaFlops(int inittag, int finaltag){/*{{{*/

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

	return final-init;
}
/*}}}*/
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

}
/*}}}*/
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
}
/*}}}*/
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
}
/*}}}*/
IssmDouble  Profiler::Memory(int tag){/*{{{*/

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

	return m;
}
/*}}}*/
