#define SUMAA_MAIN_LOG
#include "GR_config.h"
#include <stdlib.h>
#include <string.h>
#include "SUMAA_log.h"
#include "SUMAA_function.h"

#ifndef WIN_VC32
#include <sys/time.h>
#else
#include <sys/timeb.h>
#endif

#ifdef SUMAA_LOG
/* initialize the logging facility */
void SUMAAlogInit(int argc, char **argv)
{ 
  int	i; 
  if (SUMAA_LOG_initialized != 1) {
    for (i=0;i<SUMAA_LOG_MAX_EVENTS;i++) { 
	    SUMAA_LOG_event[i].name = (char *)malloc(sizeof(char)*128);
	    SUMAA_LOG_event[i].num_calls   = 0; 
	    SUMAA_LOG_event[i].parent      = SUMAA_NO_PARENT; 
	    SUMAA_LOG_event[i].level       = 0; 
	    SUMAA_LOG_event[i].time_stamp  = 0.; 
	    SUMAA_LOG_event[i].total_time  = 0.; 
	    SUMAA_LOG_event[i].event_time  = 0.; 
        SUMAA_LOG_event[i].max_time    = -300.;
        SUMAA_LOG_event[i].min_time    = 1E300;
	    SUMAA_LOG_event[i].total_flops = 0.; 
	    SUMAA_LOG_event[i].event_flops = 0.; 
        SUMAA_LOG_event[i].max_flops   = -300;
        SUMAA_LOG_event[i].min_flops   = 1E300;
        SUMAA_LOG_event[i].registered  = 0;
    }
    SUMAA_LOG_total_flops = 0.;
    SUMAA_LOG_time = 0.;
    SUMAA_LOG_current = -1;
    SUMAA_LOG_file = NULL;
    SUMAA_LOG_initialized = 1;

    /* check to see if should write to a file */
    for (i=1;i<argc;i++) {
       if (!strcmp (argv[i], "-logfile") && (i+1 < argc)) {
          SUMAA_LOG_file = (char *)malloc(sizeof(char)*128);
	      strncpy(SUMAA_LOG_file, argv[++i], (size_t)(128));
       }
    }
  }
}

void SUMAAlogEventRegister(int event_num, const char *event_name)
{ 
    if (event_num >= SUMAA_LOG_MAX_EVENTS) { 
    	fprintf(stderr,"Log full, increase SUMAA_LOG_MAX_EVENTS\n"); 
    } else if (SUMAA_LOG_event[event_num].registered != 0) {
        fprintf(stderr,"Warning: Attempting to overwrite a previously registered event (No.: %d Name: %s)\n",
                event_num,SUMAA_LOG_event[event_num].name);
    } else {
        strcpy(SUMAA_LOG_event[event_num].name,event_name);
        SUMAA_LOG_event[event_num].registered = 1;
    }
}


void SUMAAlogFree(void)
{
    int	i; 
    SUMAA_LOG_initialized = 0;
    for (i=0;i<SUMAA_LOG_MAX_EVENTS;i++) { 
	    free(SUMAA_LOG_event[i].name);
    }

}

/* print out the logging information 
Note that this is very similar to the logging information
printed by PETSC   */
void SUMAAlogPrintSingle(void)
{ 
    int	                  i, num_calls; 
    double                mflops, time_per_call;
    double                my_time = 0, percent_time = 0;
    double                flops, percent_flops = 0;
    SUMAA_LOG_struct      *event; 
    FILE                  *fp;

    if (SUMAA_LOG_file != NULL) {
      fp = fopen(SUMAA_LOG_file,"w");
      printf("Writing the logging information to file %s\n",SUMAA_LOG_file);
    } else {
      fp = stdout;
    }
    mflops = 0.;
    if (SUMAA_LOG_time>0) mflops = SUMAA_LOG_total_flops/(SUMAA_LOG_time*1E6);
    
    fprintf(fp,"Log Performance Summary (Single Processor) :----------------------------------\n");
    fprintf(fp,"Total Time (sec):   %5.3e\n",SUMAA_LOG_time);
    if (SUMAA_LOG_total_flops > 0) {
      fprintf(fp,"Total Flops:        %5.3e\n",SUMAA_LOG_total_flops);
      fprintf(fp,"MegaFlops:          %5.3e\n",mflops);
    }
    fprintf(fp,"------------------------------------------------------------------------------\n");
    if (SUMAA_LOG_time <= 0) {
      percent_time = -1.;
      fprintf(fp,"No global time accumulated, printing -1 in percent time column\n");
    }
    fprintf(fp,"\nEvent Summary:--------------------------------------------------------------\n");
    fprintf(fp,"   Count: number of times phase was executed\n");
    if (SUMAA_LOG_total_flops > 0) 
      fprintf(fp,"   Time and Flops/sec:\n");
    else
      fprintf(fp,"   Time:\n");
    fprintf(fp,"      %%Time - percent time in this event\n");
    if (SUMAA_LOG_total_flops > 0)  
      fprintf(fp,"      %%Flops - percent flops in this event\n");
    fprintf(fp,"------------------------------------------------------------------------------\n");
    if (SUMAA_LOG_total_flops > 0)
      fprintf(fp,"%-25s %5s   %8s  %8s  %9s   %5s %6s\n", "Phase", "Count",
	      "Time(s)", "Time/call", "Flops", "%Time", "%Flops"); 
    else
      fprintf(fp,"%-25s %5s   %8s  %9s    %5s\n", "Phase", "Count",
	      "Time(s)", "Time/call", "%Time"); 
    fprintf(fp,"------------------------------------------------------------------------------\n");
    for (i=0;i<SUMAA_LOG_MAX_EVENTS;i++) { 
      if (SUMAA_LOG_event[i].num_calls > 0) { 
	event = &(SUMAA_LOG_event[i]);
	my_time = event->total_time;
	num_calls = event->num_calls; 
	time_per_call = my_time/num_calls;
	flops = event->total_flops;
	if (SUMAA_LOG_time > 0) percent_time = (my_time*100)/SUMAA_LOG_time;
	if (SUMAA_LOG_total_flops > 0) {
	  percent_flops = (flops*100)/SUMAA_LOG_total_flops;
	  fprintf(fp,"%-25s%7d  %8.2e  %8.2e   %8.2e   %5.1f%%  %5.1f%%\n",
                  (event->name),num_calls,my_time,time_per_call,flops,
		  percent_time,percent_flops);
	}
	else {
	  fprintf(fp,"%-25s%7d  %8.2e  %8.2e   %5.1f%%\n",
                  (event->name),num_calls,my_time,time_per_call,
		  percent_time);
	}
      }
    }
}
	
#ifdef PARALLEL_LOG
void SUMAAlogPrintParallel(int nprocs, int myid, MPI_Comm procset)
{ 
    int	                  i, num_calls; 
    double                mflops, time_per_call;
    SUMAA_LOG_maxmind     flops, time, time_call, calls;
    SUMAA_LOG_maxmind     total_flops, total_time, mega_flops;    
    SUMAA_LOG_struct      *event; 
    FILE                  *fp;

    if (SUMAA_LOG_file != NULL) {
	    fp = fopen(SUMAA_LOG_file,"w");
        printf("Writing the logging information to file %s\n",SUMAA_LOG_file);
    } else {
        fp = stdout;
    }

    mflops = 0.;
    if (SUMAA_LOG_time) mflops = SUMAA_LOG_total_flops/(SUMAA_LOG_time*1E6);
    SUMAA_INIT_MAX_MIN(total_flops,SUMAA_LOG_total_flops);  
    SUMAA_INIT_MAX_MIN(total_time,SUMAA_LOG_time);
    SUMAA_INIT_MAX_MIN(mega_flops,mflops); 
    SUMAA_MIN_MAX_AVGd(total_flops,nprocs,procset)
    SUMAA_MIN_MAX_AVGd(total_time,nprocs,procset)
    SUMAA_MIN_MAX_AVGd(mega_flops,nprocs,procset)
    
    if (myid == 0) { 
        fprintf(fp,"\nNumber of processors used: %d\n",nprocs);
        fprintf(fp,"Log Performance Summary:----------------------------------------------------\n");
	fprintf(fp,"                Max         Min        Avg        Total \n"); 
	fprintf(fp,"Time (sec):  %5.3e   %5.3e   %5.3e\n",total_time.max, total_time.min, total_time.avg); 
	fprintf(fp,"Flops:       %5.3e   %5.3e   %5.3e  %5.3e\n",
	                     total_flops.max, total_flops.min, total_flops.avg, total_flops.sum); 
	fprintf(fp,"Mflops:      %5.3e   %5.3e   %5.3e  %5.3e\n",
    	                     mega_flops.max, mega_flops.min, mega_flops.avg, mega_flops.sum); 
	fprintf(fp,"------------------------------------------------------------------------------\n");
    }

    if (myid==0) {
	fprintf(fp,"\nEvent Summary:--------------------------------------------------------------\n");
	fprintf(fp,"   Count: number of times phase was executed\n");
    fprintf(fp,"   Time and Flops/sec:\n");
	fprintf(fp,"      Max - maximum over all the processors\n");
	fprintf(fp,"      Ratio - ratio of max to min over all processors\n");
	fprintf(fp,"   Global: entire computation\n");
	fprintf(fp,"      %%T - percent time in this event\n");
	fprintf(fp,"      %%F - percent flops in this event\n");
	fprintf(fp,"------------------------------------------------------------------------------\n");
	if (nprocs == 1) {
	    fprintf(fp,"Phase           Count     Total Time(s)    Time/call     Flops    %%Time   %%Flops\n");
	} else {
	    fprintf(fp,"Phase                   Count              Total Time(s)     Time/call     \n");
	    fprintf(fp,"                 Max     Avg     Ratio      Max   Ratio     Max   Ratio    \n");
	} 
	fprintf(fp,"------------------------------------------------------------------------------\n");

    }
    for (i=0;i<SUMAA_LOG_MAX_EVENTS;i++) { 
	if (SUMAA_LOG_event[i].num_calls > 0) { 
	    event = &(SUMAA_LOG_event[i]); 
	    num_calls = event->num_calls; 
	    time_per_call = event->total_time/num_calls;

	    SUMAA_INIT_MAX_MIN(flops,event->total_flops); 
	    SUMAA_INIT_MAX_MIN(calls,num_calls); 
	    SUMAA_INIT_MAX_MIN(time,event->total_time);
	    SUMAA_INIT_MAX_MIN(time_call,time_per_call);

	    SUMAA_MIN_MAX_AVGd(calls,nprocs,procset);
	    SUMAA_MIN_MAX_AVGd(flops,nprocs,procset);
            SUMAA_MIN_MAX_AVGd(time,nprocs,procset);
	    SUMAA_MIN_MAX_AVGd(time_call,nprocs,procset);
	    
	    if (myid==0) {
	        if (nprocs == 1) {
	     	    fprintf(fp,"%-15s %4d        %8.2e       %8.2e     %8.2e   %4.1f    %4.1f\n",
                          (event->name),num_calls,time.max,time_call.max,flops.max,
		          (time.max*100)/total_time.max,(100*flops.max)/total_flops.max);
		    } else {
		        fprintf(fp,"%-15s %6.0f %6.0f %6.2f    %2.1e %6.1f    %2.1e %6.1f  \n",
                          (event->name),calls.max,calls.avg,calls.max/calls.avg,
                          time.max,time.max/time.avg,
		                  time_call.max,time_call.max/time_call.avg);
		    }
	    }
	}		
    }
	MAX_MIN_LOG({
        double  max_time_local;
		double  min_time_local;
        double  max_flops_local;
		double  min_flops_local;
        SUMAA_LOG_maxmind max_time; 
		SUMAA_LOG_maxmind min_time;
		SUMAA_LOG_maxmind max_flops;
		SUMAA_LOG_maxmind min_flops;
    	if (myid == 0) {
        	fprintf(fp,"------------------------------------------------------------------------------\n");
        	fprintf(fp,"Phase           Count      Max Time    Min Time    Max Flops    Min Flops\n");
        	fprintf(fp,"------------------------------------------------------------------------------\n");
    	}
    	for (i=0;i<SUMAA_LOG_MAX_EVENTS;i++) { 
		if (SUMAA_LOG_event[i].num_calls > 0) { 
	    	event = &(SUMAA_LOG_event[i]); 
	    	num_calls = event->num_calls; 
	
	    	max_time_local = event->max_time;
        	min_time_local = event->min_time;
	    	max_flops_local = event->max_flops;
	    	min_flops_local = event->min_flops;
	
	    	SUMAA_INIT_MAX_MIN(max_time,max_time_local);
	    	SUMAA_INIT_MAX_MIN(min_time,min_time_local);
	    	SUMAA_INIT_MAX_MIN(max_flops,max_flops_local);
	    	SUMAA_INIT_MAX_MIN(min_flops,min_flops_local);
	
	    	SUMAA_MIN_MAX_AVGd(max_time,nprocs,procset);
	    	SUMAA_MIN_MAX_AVGd(min_time,nprocs,procset);
	    	SUMAA_MIN_MAX_AVGd(max_flops,nprocs,procset);
	    	SUMAA_MIN_MAX_AVGd(min_flops,nprocs,procset);
	
	    	if (myid == 0) {
  	        	fprintf(fp,"%-15s %4d       %8.2e     %8.2e   %8.2e     %8.2e\n",
                       	(event->name),num_calls,max_time.max,min_time.min,
                       	max_flops.max,min_flops.min);
            	}
    		}
    	}
	});
}
#endif

double SUMAAlogTime(void)
{
  double my_time;

#ifndef WIN_VC32
  struct timeval tp; 
  gettimeofday(&tp, NULL);
  my_time = ((double)tp.tv_sec) + (1.0e-6) * (double)(tp.tv_usec);
#else
  struct _timeb _tp;
  _ftime(&_tp);
  my_time = ((double)_tp.time) + (1.0e-3) * (_tp.millitm);
#endif
  return(my_time);
}
#endif
