#include "global.h"
#ifdef STATS
#include "stats.h"


#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: stats.c,v 1.16 1997/09/07 00:31:16 root Exp $";
#endif

static void updatestats (void);
static void newhour (int hour);
static void newday (int hour);
static void endmonthclear (int hour, int month);
static void endday (int hour);
static void eachcycle (time_t now);
static int doSTATclear (int argc,char *argv[],void *p);
static int doSTATload (int argc,char *argv[],void *p);
static void loadstats (const char *name,void *beginning, void *ending);


static time_t statslast = 0;


const char dailyhdr[] = "\n%s per hour in the last 24 hours (excluding current hour)\n\n";
const char weeklyhdr[] = "\n%s per day in the last 7 days (excluding current day)\n\n";
const char monthlyhdr[] = "\n%s per day in the last month (excluding current day)\n\n";
const char yearlyhdr[] = "\n%s per month in the last year (excluding current month)\n\n";

const char STAT_emptystr[] = "";
const char STAT_dailytrailer[] = 
	"------------------------           ------------------------\n"
	"0 0 0 0 0 1 1 1 1 1 2 2  Hour      0 0 0 0 0 1 1 1 1 1 2 2  Hour\n"
	"0 2 4 6 8 0 2 4 6 8 0 2            0 2 4 6 8 0 2 4 6 8 0 2\n\n";
const char STAT_weeklytrailer[] = 
	"---------------------------------       ---------------------------------\n"
	"SUN  MON  TUE  WED  THU  FRI  SAT       SUN  MON  TUE  WED  THU  FRI  SAT\n\n";
const char STAT_monthlytrailer[] = 
	"-------------------------------           -------------------------------\n"
	" 0 0 0 0 1 1 1 1 1 2 2 2 2 2 3  Days       0 0 0 0 1 1 1 1 1 2 2 2 2 2 3  Days\n"
	" 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0             2 4 6 8 0 2 4 6 8 0 2 4 6 8 0\n\n";
const char STAT_yearlytrailer[] = 
	"------------           ------------\n"
	" 0 0 0 0 1 1  Month     0 0 0 0 1 1  Month\n"
	" 2 4 6 8 0 2            2 4 6 8 0 2\n\n";
const char STAT_cannotclear[] = "Sorry, stats can only be cleared from the console\n";



static struct cmds STATScmds[] = {
#ifdef STATS_AREA
	{ "area",	doSTATarea,	0,	0,	NULLCHAR },
#endif
	{ "clear",	doSTATclear,	0,	0,	NULLCHAR },
#ifdef STATS_FWD
	{ "forwarding",	doSTATforward,	0,	0,	NULLCHAR },
#endif
#ifdef STATS_HTTP
	{ "http",	doSTAThttp,	0,	0,	NULLCHAR },
#endif
	{ "load",	doSTATload,	0, 	0,	NULLCHAR },
#ifdef STATS_MSG
	{ "messages",	doSTATmessage,	0,	0,	NULLCHAR },
#endif
	{ "save",	doSTATsave,	0, 	0,	NULLCHAR },
#ifdef STATS_TFC
	{ "traffic",	doSTATtraffic,	0,	0,	NULLCHAR },
#endif
#ifdef STATS_USE
	{ "usage",	doSTATusage,	0,	0,	NULLCHAR },
#endif
	{ NULLCHAR,	NULL,		0,	0,	NULLCHAR }
};


int
dostats (int argc, char *argv[], void *p)
{
	return subcmd(STATScmds,argc,argv,p);
}


static int
doSTATclear (int argc, char *argv[], void *p)
{
int c;
struct mbuf *bp;
int done = 0;

	if(Curproc->input != Command->input)
		tputs (STAT_cannotclear);
	else	{
		if (argc == 2)
			c = argv[1][0];
		else	{
			tputs ("\007Clear all statistics: Are you sure? ");
			tflush ();
			(void) recv_mbuf(Command->input,&bp,0,NULLCHAR,0);
			c = bp->data[0];
		}

		if(c == 'y' || c == 'Y')	{
			done = 1;
#ifdef STATS_AREA
			(void) doSTATclear_area(argc, argv, p);
#endif
#ifdef STATS_USE
			(void) doSTATclear_use(argc, argv, p);
#endif
#ifdef STATS_FWD
			(void) doSTATclear_fwd(argc, argv, p);
#endif
#ifdef STATS_MSG
			(void) doSTATclear_msg(argc, argv, p);
#endif
#ifdef STATS_TFC
			(void) doSTATclear_tfc(argc, argv, p);
#endif
#ifdef STATS_HTTP
			(void) doSTATclear_http(argc, argv, p);
#endif
		}
	}
	tprintf ("Statistics %scleared....\n", (done) ? "" : "not ");
	return 0;
}


int
doSTATsave (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
FILE *fp;
char buffer[256];

	sprintf (buffer, "%s/last", STATSDir);
	fp = fopen (buffer, "w");
	if (fp != NULLFILE)	{
		fprintf (fp, "Last: %ld\n", statslast);
		(void) fclose (fp);
	} else
		log (-1, "Can't open stats file '%s/last'", STATSDir);

#ifdef STATS_AREA
	savestats_area();
#endif
#ifdef STATS_USE
	savestats_use();
#endif
#ifdef STATS_FWD
	savestats_fwd();
#endif
#ifdef STATS_MSG
	savestats_msg();
#endif
#ifdef STATS_TFC
	savestats_tfc();
#endif
#ifdef STATS_HTTP
	savestats_http();
#endif
	return 0;
}



static int
doSTATload (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	loadstats ("last", &statslast, &statslast);
	kwait (NULL);
#ifdef STATS_USE
	loadstats_use();
#endif
#ifdef STATS_MSG
	loadstats_msg();
#endif
#ifdef STATS_TFC
	loadstats_tfc();
#endif
#ifdef STATS_AREA
	loadstats_area();
#endif
#ifdef STATS_FWD
	loadstats_fwd();
#endif
#ifdef STATS_HTTP
	loadstats_http();
#endif
	return 0;
}



static void
loadstats (const char *name, void *beginning, void *ending)
{
char buffer[256], *cp;
long *lp;
FILE *fp;

	lp = (long *)beginning;
	sprintf (buffer, "%s/%s", STATSDir, name);
	fp = fopen (buffer, "r");
	if (fp != NULLFILE)	{
		while (!feof(fp))	{
			kwait (NULL);
			(void) fgets (buffer, 79, fp);
			if (feof(fp))
				continue;
			rip (buffer);
			cp = strchr (buffer, ' ');
			if (!cp)	{
				log (-1, "Error in loading stats file '%s/%s'", STATSDir, name);
				(void) fclose (fp);
				return;
			}
			*lp++ = atol (++cp);
			if (lp > (long *)ending)
				break;
			cp = strchr (cp, ' ');
			if (cp)	{
				*lp++ = atol (++cp);
				if (lp > (long *)ending)
					break;
			}
		}
		(void) fclose (fp);
	} else
		log (-1, "Can't open stats file '%s/%s'", STATSDir, name);
	kwait (NULL);
}



#if defined(STATS_USE) || defined(STATS_MSG) || defined(STATS_AREA) || defined(STATS_FWD) || defined(STATS_TFC) || defined(STATS_HTTP)
void
doGraph (int num, long array[][2])
{
long chart[31][2], highest[2], divisor[2];
int k, l, m;
int multiplyit[2];

	highest[0] = highest[1] = 0L;
	divisor[0] = divisor[1] = 1L;
	multiplyit[0] = multiplyit[1] = 0;
	for (k = 0; k < num; k++)
		chart[k][0] = chart[k][1] = 0L;
	for (k = 0; k < num; k++)
		for (l = 0; l < 2; l++)
			if (array[k][l] > highest[l])
				highest[l] = array[k][l];

	for (k = 0; k < 2; k++)	{
		if ((highest[k] < 10L) && highest[k])	{
			multiplyit[k] = 1;
			divisor[k] = (10 / highest[k]);
#if 0
			if (divisor[k] < 2)
				divisor[k] = 2;
#endif
		} else 	if (highest[k] >= 10L)
			divisor[k] = highest[k] / 10L;
	}

	for (k = 0; k < num; k++)
		for (l = 0; l < 2; l++)		{
			if (multiplyit[l])
				chart[k][l] = array[k][l] * divisor[l];
			else	{
				chart[k][l] = array[k][l] / divisor[l];
				if (array[k][l] % divisor[l])
					chart[k][l]++;
			}
			if (chart[k][l] > 10L)
				chart[k][l] = 10L;
		}
	for (m = 10; m ; m--)	{	/* each line */
		for (l = 0; l < 2; l ++)	{
			for (k = 0; k < num; k++)	{
				if (chart[k][l] >= m)	{	/*lint !e771 */
					if (num != 7)
						tputc ('*');
					else
						tputs ("***");
				} else {
					if (num != 7)
						tputc (' ');
					else
						tputs ("   ");
				}
				if (num == 7)
					tputs ("  ");
			}
			if (!l)	{
				if (num != 7)
					tputs ("           ");
				else
					tputs ("     ");
			} else
				tputc('\n');
		}
	}
}
#endif


static void
updatestats(void)
{
#ifdef STATS_AREA
	updatestats_area ();
#endif
#ifdef STATS_USE
	updatestats_use ();
#endif
#ifdef STATS_FWD
	updatestats_fwd ();
#endif
#ifdef STATS_MSG
	updatestats_msg ();
#endif
#ifdef STATS_TFC
	updatestats_tfc ();
#endif
}


static void
newhour (int hour)
{
#ifdef STATS_AREA
	newhour_area(hour);
#endif
#ifdef STATS_USE
	newhour_use(hour);
#endif
#ifdef STATS_FWD
	newhour_fwd(hour);
#endif
#ifdef STATS_MSG
	newhour_msg(hour);
#endif
#ifdef STATS_TFC
	newhour_tfc(hour);
#endif
#ifdef STATS_HTTP
	newhour_http(hour);
#endif
}


static void
newday (int day)
{
#ifdef STATS_AREA
	newday_area (day);
#endif
#ifdef STATS_USE
	newday_use (day);
#endif
#ifdef STATS_FWD
	newday_fwd (day);
#endif
#ifdef STATS_MSG
	newday_msg (day);
#endif
#ifdef STATS_TFC
	newday_tfc (day);
#endif
#ifdef STATS_HTTP
	newday_http (day);
#endif
}


static void
endmonthclear (int day, int month)
{
#ifdef STATS_AREA
	endmonthclear_area (day, month);
#endif
#ifdef STATS_USE
	endmonthclear_use (day, month);
#endif
#ifdef STATS_FWD
	endmonthclear_fwd (day, month);
#endif
#ifdef STATS_MSG
	endmonthclear_msg (day, month);
#endif
#ifdef STATS_TFC
	endmonthclear_tfc (day, month);
#endif
#ifdef STATS_HTTP
	endmonthclear_http (day, month);
#endif
}


static void
endday (int day)
{
#ifdef STATS_AREA
	endday_area (day);
#endif
#ifdef STATS_USE
	endday_use (day);
#endif
#ifdef STATS_FWD
	endday_fwd (day);
#endif
#ifdef STATS_MSG
	endday_msg (day);
#endif
#ifdef STATS_TFC
	endday_tfc (day);
#endif
#ifdef STATS_HTTP
	endday_http (day);
#endif
}


static void
eachcycle (time_t now)
{
	statslast = now;
#ifdef STATS_AREA
	eachcycle_area (now);
#endif
#ifdef STATS_USE
	eachcycle_use (now);
#endif
#ifdef STATS_FWD
	eachcycle_fwd (now);
#endif
#ifdef STATS_MSG
	eachcycle_msg (now);
#endif
#ifdef STATS_TFC
	eachcycle_tfc (now);
#endif
#ifdef STATS_HTTP
	eachcycle_http (now);
#endif
}


static void
init (time_t now)
{
	statslast = 0;
#ifdef STATS_AREA
	init_area (now);
#endif
#ifdef STATS_USE
	init_use (now);
#endif
#ifdef STATS_FWD
	init_fwd (now);
#endif
#ifdef STATS_MSG
	init_msg (now);
#endif
#ifdef STATS_TFC
	init_tfc (now);
#endif
#ifdef STATS_HTTP
	init_http (now);
#endif
}


void
statsdaemon (int i OPTIONAL, void *v1 OPTIONAL, void *v2 OPTIONAL)
{
time_t now, then;
struct tm *t;
int lastmin = -1, hour, day;

	then = now = time((time_t *)0);
	init(now);
	(void) doSTATload(0, (char **)0, (void *)0);
	t = localtime(&now);
	t->tm_min = 0;
	then = mktime (t);

	/* adjust in case previously not shut down properly */
	if (statslast && statslast < then)	{
		/* adjust for missing hour */
		hour = t->tm_hour - 1;
		if (hour < 0)
			hour = 23;
		newhour (hour);
		t->tm_hour = 0;
		then = mktime (t);

		if (statslast < then)	{
			/* adjust for missing day */
			day = t->tm_wday - 1;
			if (day < 0)
				day = 6;
			newday (day);
			day = t->tm_mday - 2;

			if (day < 0)	{	/* new month begins */
				/* let the OS tell us the # of days in last month */
				t->tm_mday = 1;
				t->tm_hour = 0;
				t->tm_sec = 0;
				then = mktime (t);
				then -= 3600;
				t = localtime (&then);
				day = t->tm_mday - 1;

				endmonthclear (day + 1, t->tm_mon);
			}
			endday (day);
		}
	}

	server_disconnect_io ();
	for(;;){
		(void) kpause(6000L);	/* Run stats 10 times a minute, every 6 seconds */
		updatestats();
		now = time((time_t *)0);
		t = localtime(&now);
		if ((!t->tm_min && lastmin != t->tm_min) || (lastmin != -1 && lastmin > t->tm_min))	{
			/* new hour begins */
			hour = t->tm_hour - 1;
			if (hour < 0)
				hour = 23;
			newhour (hour);
			if (!t->tm_hour)	{	/* new day begins */
				day = t->tm_wday - 1;
				if (day < 0)
					day = 6;
				newday(day);
				day = t->tm_mday - 2;
				if (day < 0)	{	/* new month begins */
					/* let the OS tell us the # of days in last month */
					t->tm_mday = 1;
					t->tm_hour = 0;
					t->tm_sec = 0;
					then = mktime (t);
					then -= 3600;
					t = localtime (&then);
					day = t->tm_mday - 1;

					endmonthclear (day + 1, t->tm_mon);
				}
				endday (day);
			}
		}
		eachcycle (now);

		if (!(t->tm_min % 10) && lastmin != t->tm_min)
			(void) doSTATsave(0, (char **)0, (void *)0);

		lastmin = t->tm_min;
	}
}



#endif	/* STATS */
