#include "stdinc.h"
#include "dat.h"
#include "fns.h"

int collectstats = 1;

/* keep in sync with dat.h:/NStat */
Statdesc statdesc[NStat] =
{
	{ "rpc total", },
	{ "rpc reads", },
	{ "rpc reads ok", },
	{ "rpc reads failed", },
	{ "rpc read bytes", },
	{ "rpc read time", },
	{ "rpc read cached", },
	{ "rpc read cached time", },
	{ "rpc read uncached", },
	{ "rpc read uncached time "},
	
	{ "rpc writes", },
	{ "rpc writes new", },
	{ "rpc writes old", },
	{ "rpc writes failed", },
	{ "rpc write bytes", },
	{ "rpc write time", },
	{ "rpc write new time", },
	{ "rpc write old time", },

	{ "lump cache hits", },
	{ "lump cache misses", },
	{ "lump cache reads", },
	{ "lump cache writes", },
	{ "lump cache size", },
	{ "lump cache stall", },
	{ "lump cache read time", },

	{ "disk cache hits", },
	{ "disk cache misses", },
	{ "disk cache lookups", },
	{ "disk cache reads", },
	{ "disk cache writes", },
	{ "disk cache dirty", },
	{ "disk cache size", },
	{ "disk cache flushes", },
	{ "disk cache stalls", },
	{ "disk cache lookup time", },

	{ "disk block stalls", },
	{ "lump stalls", },

	{ "index cache hits", },
	{ "index cache misses", },
	{ "index cache reads", },
	{ "index cache writes", },
	{ "index cache fills", },
	{ "index cache prefetches", },
	{ "index cache dirty", },
	{ "index cache size", },
	{ "index cache flushes", },
	{ "index cache stalls", },
	{ "index cache read time", },
	{ "index cache lookups" },
	{ "index cache summary hits" },
	{ "index cache summary prefetches" },

	{ "bloom filter hits", },
	{ "bloom filter misses", },
	{ "bloom filter false misses", },
	{ "bloom filter lookups", },
	{ "bloom filter ones", },
	{ "bloom filter bits", },

	{ "arena block reads", },
	{ "arena block read bytes", },
	{ "arena block writes", },
	{ "arena block write bytes", },

	{ "isect block reads", },
	{ "isect block read bytes", },
	{ "isect block writes", },
	{ "isect block write bytes", },

	{ "sum reads", },
	{ "sum read bytes", },
	
	{ "cig loads" },
	{ "cig load time" },
};

QLock statslock;
Stats stats;
Stats *stathist;
int nstathist;
ulong statind;
ulong stattime;

void
statsproc(void *v)
{
	USED(v);

	for(;;){
		stats.now = time(0);
		stathist[stattime%nstathist] = stats;
		stattime++;
		sleep(1000);
	}
}

void
statsinit(void)
{
	nstathist = 90000;
	stathist = MKNZ(Stats, nstathist);
	vtproc(statsproc, nil);
}

void
setstat(int index, long val)
{
	qlock(&statslock);
	stats.n[index] = val;
	qunlock(&statslock);
}

void
addstat(int index, int inc)
{
	if(!collectstats)
		return;
	qlock(&statslock);
	stats.n[index] += inc;
	qunlock(&statslock);
}

void
addstat2(int index, int inc, int index1, int inc1)
{
	if(!collectstats)
		return;
	qlock(&statslock);
	stats.n[index] += inc;
	stats.n[index1] += inc1;
	qunlock(&statslock);
}

void
printstats(void)
{
}

void
binstats(long (*fn)(Stats *s0, Stats *s1, void *arg), void *arg,
	long t0, long t1, Statbin *bin, int nbin)
{
	long xt0, t, te, v;
	int i, j, lo, hi, m;
	vlong tot;
	Statbin *b;
	
	t = stats.now;
	
	/* negative times mean relative to now. */
	if(t0 <= 0)
		t0 += t;
	if(t1 <= 0)
		t1 += t;
	/* ten minute range if none given */
	if(t1 <= t0)
		t0 = t1 - 60*10;
	if(0) fprint(2, "stats %ld-%ld\n", t0, t1);
	
	/* binary search to find t0-1 or close */
	lo = stattime;
	hi = stattime+nstathist;
	while(lo+1 < hi){
		m = (lo+hi)/2;
		if(stathist[m%nstathist].now >= t0)
			hi = m;
		else
			lo = m;
	}
	xt0 = stathist[lo%nstathist].now;
	if(xt0 >= t1){
		/* no samples */
		memset(bin, 0, nbin*sizeof bin[0]);
		return;
	}

	hi = stattime+nstathist;
	j = lo+1;
	for(i=0; i<nbin; i++){
		te = t0 + (t1-t0)*i/nbin;
		b = &bin[i];
		memset(b, 0, sizeof *b);
		tot = 0;
		for(; j<hi && stathist[j%nstathist].now<te; j++){
			v = fn(&stathist[(j-1)%nstathist], &stathist[j%nstathist], arg);
			if(b->nsamp==0 || v < b->min)
				b->min = v;
			if(b->nsamp==0 || v > b->max)
				b->max = v;
			tot += v;
			b->nsamp++;
		}
		if(b->nsamp)
			b->avg = tot / b->nsamp;
		if(b->nsamp==0 && i>0)
			*b = bin[i-1];
	}	
}
