#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#include <sys/times.h>
#include <unistd.h>
#endif

#define DMID 13
#include "debug.h"
#include "thread.h"
#include "lstat.h"
#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE (!FALSE)
#endif
#include "lib.h"
#include "fasttime2.h"

int emsg(const char * arg_list, ...);

#include "defines.h"
#define MAX_FIGS 300
static LStat figs[MAX_FIGS];
static int lsort[MAX_FIGS];
static int max;
static int cps=CLOCKS_PER_SEC;
int lstat_lib_msec(void) ;
unsigned int lstat_my_clock(void);
int lstat_mutex_wait(void);
unsigned int lib_thread_cpu(void);

void lstat_mutex_release(void);
static int isdisable;
int lstat_disable(int x)
{
	isdisable = x;
	return TRUE;
}
int lstat_cps(void)
{
	return cps;
}
int lstat_cpms(void)
{
	return cps/1000;
}
int lstat_start(Lkeep *lk, int x)
{
	if (isdisable) return TRUE;
	if (x<0) return FALSE;
	if (x>=MAX_FIGS) return FALSE;
	lk->start_cpu = lstat_my_clock();
	lk->start_elap = lstat_lib_msec();
	figs[x].k = x;
	return TRUE;
}
int lstat_end(Lkeep *lk,int x)
{
	int cpu,elap;
	if (isdisable) return TRUE;
	if (x<0) return FALSE;
	if (x>=MAX_FIGS) return FALSE;
	cpu = lstat_my_clock() - lk->start_cpu;
	if (cpu<0) cpu = lstat_my_clock(); /* approximation if it rotated */
	elap = lstat_lib_msec() - lk->start_elap;
	lstat_mutex_wait();
	figs[x].cpu += cpu;
	if (cpu>figs[x].worstcpu) figs[x].worstcpu = cpu;
	figs[x].elap += elap;
	if (elap>figs[x].worstelap) figs[x].worstelap = elap;
	figs[x].n++;
	if (x>=max) max = x+1;
	lstat_mutex_release();
	return TRUE;
}
int lstat_n(void)
{
	return max;
}
LStat * lstat_get(int x)
{
	if (x<0) return NULL;
	if (x>=MAX_FIGS) return NULL;
	return &figs[x];
}
int lstat_reset(void)
{
	int i;
	for (i=0; i<MAX_FIGS; i++) {
		figs[i].cpu = 0;
		figs[i].elap = 0;
		figs[i].n = 0;
		figs[i].worstcpu = 0;
		figs[i].worstelap = 0;
	}
	return TRUE;
}
int lstat_cmp(const void *a, const void *b)
{
	int i = *(int *)a;
	int j = *(int *)b;
	if (i<0) emsg("lstat: i is negative\n");
	if (j<0) emsg("lstat: j is negative\n");
	if (i>=MAX_FIGS) emsg("lstat: i is too big\n");
	if (j>=MAX_FIGS) emsg("lstat: j is too big\n");
	return (int) (figs[i].cpu-figs[j].cpu);
}
int lstat_sort(void)
{
	int i;
	lstat_mutex_wait();
	for (i=0; i<MAX_FIGS; i++) {
		lsort[i] = i;
		figs[i].k = i;
	}
	qsort(lsort,max,sizeof(int),lstat_cmp);
	lstat_mutex_release();
	return TRUE;
}
LStat * lstat_get_sort(int x)
{
	return lstat_get(lsort[x]);
}
unsigned int lstat_my_clock(void)
{
#ifdef WIN32
	unsigned int lo,hi,tot,r;
	FILETIME crtime,extime,ktime,utime;

	GetThreadTimes(GetCurrentThread(),&crtime,&extime,&ktime,&utime);
	lo = ktime.dwLowDateTime >> 16;
	hi = ktime.dwHighDateTime << 16;
	tot = lo + hi;
	lo = utime.dwLowDateTime >> 16;
	hi = utime.dwHighDateTime << 16;
	tot += lo + hi;
	/*
		100 nano second intervals.
		4000000000
		10000000 per second
		CLOCKS_PER_SEC per second
	*/

	r = (int) ((double) tot * .0065536 * CLOCKS_PER_SEC);
	r = r & 0x07fffffff;
	return (int) r;
#else
//#ifndef NO_TIMES
//	struct tms bf;
//	times(&bf);
//	return (bf.tms_utime+bf.tms_stime)*  (CLOCKS_PER_SEC/60);
//#else
	return 0;
//	return lib_thread_cpu();
	//return clock();
//	return clock(); chrisp changed. 
//#endif
#endif
}
unsigned long total_now;
unsigned int lstat_process_cpu(void)
{
#ifdef WIN32
	unsigned int lo,hi,tot,r;
	FILETIME crtime,extime,ktime,utime;

	GetProcessTimes(GetCurrentProcess(),&crtime,&extime,&ktime,&utime);
	lo = ktime.dwLowDateTime >> 16;
	hi = ktime.dwHighDateTime << 16;
	tot = lo + hi;
	lo = utime.dwLowDateTime >> 16;
	hi = utime.dwHighDateTime << 16;
	tot += lo + hi;
	/*
		100 nano second intervals.
		4000000000
		10000000 per second
		CLOCKS_PER_SEC per second
	*/

	r = (int) ((double) tot * .0065536 * CLOCKS_PER_SEC);
	r = r & 0x07fffffff;
	return (int) r;
#else
//	return 0;
	return lib_thread_cpu();
#endif
}
int lstat_lib_msec(void)
{
	static int base;
	if (base==0) base = (int)time(NULL)-(24*60*60);
#ifdef WIN32
	{
		SYSTEMTIME systime;
		GetSystemTime(&systime);
		return ((int)(time(NULL)-base)*1000)+(systime.wMilliseconds);
	}
#else
#ifndef NO_TIMEOFDAY
	{
		struct timeval tsec;
		struct timezone xtzone;
		gettimeofday(&tsec,&xtzone);
		return ((tsec.tv_sec-base)*1000)+(tsec.tv_usec/1000);
	}
#else
	return (time(NULL)-base)*1000;
#endif
#endif
}




#ifdef DO_THREADS
//Mutex *th_mutex_new(char *name);

static Mutex *m;
#endif

void lstat_init(void)
{
#ifdef DO_THREADS
	m = th_mutex_new("lstat");
#endif
}
#ifndef MUTEX_TIME
#define MUTEX_TIME 600000
#endif

void lib_abort_real(char *src, int line);
#define lib_abort() lib_abort_real(__FILE__,__LINE__)

static int mutex_i;
static int (*mutex_cb)(int vm, int isget, int seconds);
int lstat_set_mutex(int (*cb)(int vm, int isget, int seconds), int m)
{
		mutex_i = m;
		mutex_cb = cb;
		return TRUE;
}


int lstat_mutex_wait(void)
{
	if (mutex_cb) return mutex_cb(mutex_i,TRUE,600);
#ifdef DO_THREADS
	if (m==NULL) return TRUE;
	if (!th_mutex_wait(m,MUTEX_TIME)) { /* 10 minutes */
		printf("lstat_mutex_failed\n");
		lib_abort();
	}
	return TRUE; 
#endif
}
void lstat_mutex_release(void)
{
	if (mutex_cb) { mutex_cb(mutex_i,FALSE,600); return; }
#ifdef DO_THREADS
	if (m==NULL) return;
	th_mutex_release(m);
#endif
}
int lstat_save_total(void)
{
#ifndef WIN32
#ifndef NO_TIMES
	struct tms bf;
	times(&bf);
	total_now = (bf.tms_utime+bf.tms_stime+bf.tms_cutime+bf.tms_cstime) *  (CLOCKS_PER_SEC/60);	
#else
//	total_now = clock();
	total_now = 0;
#endif
#endif
	return TRUE;
}

