// Get some basic system performance monitoring counters going for performance monitoring on unix systems
// Mothods to do this found online (sourced locations noted where relevant)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>

#include "emsg.h"
#include "lprintf.h"
#include "lib.h"

#include "perflib_unix.h"

int perflib_collect();
int perflib_get_loadavg(double *val);
int perflib_get_stat(double *value);
int perflib_get_cpu_total(double *value);
float get_mb_used(void);

int CollectDataUnix(void)
{
	// Actually collect data from /proc / sysctl / etc
	perflib_collect();
	return TRUE; 
}

int UnixPerfCounter(char *type, double *numValue, char* strValue)
{
	int ret=FALSE;
	if(strcmp(type,"#loadavg")==0)
		ret=perflib_get_loadavg(numValue);
	if(strcmp(type,"#stat")==0)
		ret=perflib_get_cpu_total(numValue);
	if(strcmp(type,"#vmsize")==0){
		*numValue = (double) get_mb_used();
		return TRUE;
	}

	dmsg("perflib: %s value=%2.1f valid=%s",type,*numValue,(ret?"true":"false"));

	return ret;
}

// ***************************************************************************
// ****  LINUX  **************************************************************
// ***************************************************************************
#if defined is_linux

int perflib_collect()
{
	// Collect /proc/loadavg if it exists
	perflib_collect_loadavg();
	
	// Collect /proc/stat for total cpu loading
	perflib_collect_stat();

	return TRUE;
}

float perflib_loadavg;

// Simple method of getting most recent "Load Average" (kernel 60 second averaged)
// Read from: /proc/loadavg
int perflib_collect_loadavg()
{
	FILE *f;
	f=fopen("/proc/loadavg","r");
	if(!f) return FALSE;
	fscanf(f,"%f",&perflib_loadavg);
	fclose(f);
	return TRUE;
}
int perflib_get_loadavg(double *value)
{
	//Setup on a scale we can actually graph
	*value=perflib_loadavg*100;	
	return TRUE;
}

// Linux CPU and disk io constructs sourced from iostat 5.0.5
// part of sysstat from: http://pagesperso-orange.fr/sebastien.godard/download.html

#include <sys/param.h>	/* for HZ */

#define STAT		"/proc/stat"

#define S_VALUE(m,n,p)	(((double) ((n) - (m))) / (p) * HZ)
/* new define to normalize to %; HZ is 1024 on IA64 and % should be normalized to 100 */
#define SP_VALUE(m,n,p)	(((double) ((n) - (m))) / (p) * 100)

// Handle overflow conditions properly...
double ll_sp_value(unsigned long long value1, unsigned long long value2, unsigned long long itv)
{
   if ((value2 < value1) && (value1 <= 0xffffffff))
      /* Counter's type was unsigned long and has overflown */
      return ((double) ((value2 - value1) & 0xffffffff)) / itv * 100;
   else
      return SP_VALUE(value1, value2, itv);
}
double ll_s_value(unsigned long long value1, unsigned long long value2, unsigned long long itv)
{
   if ((value2 < value1) && (value1 <= 0xffffffff))
      /* Counter's type was unsigned long and has overflown */
      return ((double) ((value2 - value1) & 0xffffffff)) / itv * HZ;
   else
      return S_VALUE(value1, value2, itv);
}


// Read stats from /proc/stat file for total CPU usage

struct comm_stats {
   unsigned long long uptime;
   unsigned long long uptime0;
   unsigned long long cpu_iowait;
   unsigned long long cpu_idle;
   unsigned long long cpu_user;
   unsigned long long cpu_nice;
   unsigned long long cpu_system;
};

int curr_stat=1;	//with ^= below starts on zero
struct comm_stats comm_stats[2];

int perflib_collect_stat()
{
   FILE *statfp;
   char line[BFSZ];
   int pos, i;
   unsigned long v_tmp[3];
   unsigned int v_major, v_index;
   struct io_stats *st_iodev_tmp[4], *st_iodev_i;
   struct io_hdr_stats *st_hdr_iodev_i;
   unsigned long long cc_idle, cc_iowait;
   unsigned long long cc_user, cc_nice, cc_system, cc_hardirq, cc_softirq;

   curr_stat ^= 1;

   /* Open stat file */
   if ((statfp = fopen(STAT, "r")) == NULL) {
	   return FALSE;
   }

   while (fgets(line, BFSZ, statfp) != NULL) {
      if (!strncmp(line, "cpu ", 4)) {
		 /*
		  * Read the number of jiffies spent in the different modes,
		  * and compute system uptime in jiffies (1/100ths of a second if HZ=100).
		  * Some fields are only presnt in 2.6 kernels.
		  */
		 comm_stats[curr_stat].cpu_iowait = 0;	/* For pre 2.6 kernels */
		 cc_hardirq = cc_softirq = 0;

		 /* CPU counters became unsigned long long with kernel 2.6.5 */
		 sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu",
				&(comm_stats[curr_stat].cpu_user), &(comm_stats[curr_stat].cpu_nice),&(comm_stats[curr_stat].cpu_system),
				&(comm_stats[curr_stat].cpu_idle), &(comm_stats[curr_stat].cpu_iowait), &cc_hardirq, &cc_softirq);

		 /*
		  * Time spent in system mode also includes time spent servicing
		  * interrupts and softirqs.
		  */
		 comm_stats[curr_stat].cpu_system += cc_hardirq + cc_softirq;
		
		 /*
		  * Compute system uptime in jiffies.
		  * Uptime is multiplied by the number of processors.
		  */
		 comm_stats[curr_stat].uptime =	comm_stats[curr_stat].cpu_user + comm_stats[curr_stat].cpu_nice + comm_stats[curr_stat].cpu_system +
										comm_stats[curr_stat].cpu_idle + comm_stats[curr_stat].cpu_iowait;
      }
      // MW: Older kernel stuff (particularly related to diskio) removed
   }

   /* Close stat file */
   fclose(statfp);
   return TRUE;
}

int perflib_get_cpu_total(double *value)
{
	unsigned long long itv;

	itv = (comm_stats[curr_stat].uptime - comm_stats[!curr_stat].uptime) & 0xffffffff;
	if (!itv) itv = 1;

	// Could show various types but just give out one total for now based on idle time :-) (or could sum all the other values!)
	*value=100-ll_sp_value(comm_stats[!curr_stat].cpu_idle,   comm_stats[curr_stat].cpu_idle,   itv);

/*
	printf(_("avg-cpu:  %%user   %%nice    %%sys %%iowait   %%idle\n"));
	ll_sp_value(comm_stats[!curr].cpu_user,   comm_stats[curr].cpu_user,   itv);
	ll_sp_value(comm_stats[!curr].cpu_nice,   comm_stats[curr].cpu_nice,   itv);
	ll_sp_value(comm_stats[!curr].cpu_system, comm_stats[curr].cpu_system, itv);
	ll_sp_value(comm_stats[!curr].cpu_iowait, comm_stats[curr].cpu_iowait, itv);
	ll_sp_value(comm_stats[!curr].cpu_idle, comm_stats[curr].cpu_idle, itv));	
*/
	return TRUE;
}




// ***************************************************************************
// ****  FREEBSD / OSX  ******************************************************
// ***************************************************************************

#elif defined FREEBSD || OSX

//#include <stdlib.h>
//#include <stdio.h>
//#include <string.h>
//#include <sys/types.h>

//#include "perflib_unix.h"

//#include <sys/sysctl.h>

int perflib_collect()
{
	return FALSE;
}

int perflib_get_loadavg(double *val)
{
	return FALSE;
}
int perflib_get_cpu_total(double *value)
{
	return FALSE;
}

// ***************************************************************************
// ****  SOLARIS  ************************************************************
// ***************************************************************************

#elif defined SOLARIS

int perflib_collect()
{
	return FALSE;
}

// Solaris version of sysctl I believeis the best way...
int perflib_get_loadavg(double *val)
{
	return FALSE;
}
int perflib_get_cpu_total(double *value)
{
	return FALSE;
}

#else

int perflib_collect()
{
	return FALSE;
}

// Some warning??
int perflib_get_loadavg(double *val)
{
	return FALSE;
}
int perflib_get_cpu_total(double *value)
{
	return FALSE;
}
#endif


