/*----------------------------------------------------------------------------*/
/* Hobbit monitor library.                                                    */
/*                                                                            */
/* This is a library module, part of libbbgen.                                */
/* It contains routines for working with RRD graphs.                          */
/*                                                                            */
/* Copyright (C) 2002-2006 Henrik Storner <henrik@storner.dk>                 */
/*                                                                            */
/* This program is released under the GNU General Public License (GPL),       */
/* version 2. See the file "COPYING" for details.                             */
/*                                                                            */
/*----------------------------------------------------------------------------*/

static char rcsid[] = "$Id: hobbitrrd.c,v 1.40 2006/07/20 16:06:41 henrik Rel $";

#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <time.h>

#include "libbbgen.h"
#include "version.h"

/* This is for mapping a status-name -> RRD file */
hobbitrrd_t *hobbitrrds = NULL;
RbtHandle hobbitrrdtree;

/* This is the information needed to generate links on the trends column page  */
hobbitgraph_t *hobbitgraphs = NULL;

static const char *hobbitlinkfmt = "<table summary=\"%s Graph\"><tr><td><A HREF=\"%s&amp;action=menu\"><IMG BORDER=0 SRC=\"%s&amp;graph=hourly&amp;action=view\" ALT=\"hobbit graph %s\"></A></td><td> <td align=\"left\" valign=\"top\"> <a href=\"%s&amp;graph=hourly&amp;action=selzoom\"> <img src=\"%s/zoom.gif\" border=0 alt=\"Zoom graph\" style='padding: 3px'> </a> </td></tr></table>\n";

static const char *metafmt = "<RRDGraph>\n  <GraphType>%s</GraphType>\n  <GraphLink><![CDATA[%s]]></GraphLink>\n  <GraphImage><![CDATA[%s&amp;graph=hourly]]></GraphImage>\n</RRDGraph>\n";


/*
 * Define the mapping between BB columns and RRD graphs.
 * Normally they are identical, but some RRD's use different names.
 */
static void rrd_setup(void)
{
	static int setup_done = 0;
	char *lenv, *ldef, *p, *tcptests, *services;
	int count;
	hobbitrrd_t *lrec;
	hobbitgraph_t *grec;

	/* Do nothing if we have been called within the past 5 minutes */
	if ((setup_done + 300) >= time(NULL)) return;


	/* 
	 * Must free any old data first.
	 * NB: These lists are NOT null-terminated ! 
	 *     Stop when bbsvcname becomes a NULL.
	 */
	lrec = hobbitrrds;
	while (lrec && lrec->bbsvcname) {
		if (lrec->hobbitrrdname != lrec->bbsvcname) xfree(lrec->hobbitrrdname);
		xfree(lrec->bbsvcname);
		lrec++;
	}
	if (hobbitrrds) {
		xfree(hobbitrrds);
		rbtDelete(hobbitrrdtree);
	}

	grec = hobbitgraphs;
	while (grec && grec->hobbitrrdname) {
		if (grec->hobbitpartname) xfree(grec->hobbitpartname);
		xfree(grec->hobbitrrdname);
		grec++;
	}
	if (hobbitgraphs) xfree(hobbitgraphs);


	/* Get the tcp services, and count how many there are */
	services = strdup(init_tcp_services());
	tcptests = strdup(services);
	count = 0; p = strtok(tcptests, " "); while (p) { count++; p = strtok(NULL, " "); }
	strcpy(tcptests, services);

	/* Setup the hobbitrrds table, mapping test-names to RRD files */
	lenv = (char *)malloc(strlen(xgetenv("TEST2RRD")) + strlen(tcptests) + count*strlen(",=tcp") + 1);
	strcpy(lenv, xgetenv("TEST2RRD")); 
	p = lenv+strlen(lenv)-1; if (*p == ',') *p = '\0';	/* Drop a trailing comma */
	p = strtok(tcptests, " "); while (p) { sprintf(lenv+strlen(lenv), ",%s=tcp", p); p = strtok(NULL, " "); }
	xfree(tcptests);
	xfree(services);

	count = 0; p = lenv; do { count++; p = strchr(p+1, ','); } while (p);
	hobbitrrds = (hobbitrrd_t *)calloc(sizeof(hobbitrrd_t), (count+1));

	hobbitrrdtree = rbtNew(name_compare);
	lrec = hobbitrrds; ldef = strtok(lenv, ",");
	while (ldef) {
		p = strchr(ldef, '=');
		if (p) {
			*p = '\0'; 
			lrec->bbsvcname = strdup(ldef);
			lrec->hobbitrrdname = strdup(p+1);
		}
		else {
			lrec->bbsvcname = lrec->hobbitrrdname = strdup(ldef);
		}
		rbtInsert(hobbitrrdtree, lrec->bbsvcname, lrec);

		ldef = strtok(NULL, ",");
		lrec++;
	}
	xfree(lenv);

	/* Setup the hobbitgraphs table, describing how to make graphs from an RRD */
	lenv = strdup(xgetenv("GRAPHS"));
	p = lenv+strlen(lenv)-1; if (*p == ',') *p = '\0';	/* Drop a trailing comma */
	count = 0; p = lenv; do { count++; p = strchr(p+1, ','); } while (p);
	hobbitgraphs = (hobbitgraph_t *)calloc(sizeof(hobbitgraph_t), (count+1));

	grec = hobbitgraphs; ldef = strtok(lenv, ",");
	while (ldef) {
		p = strchr(ldef, ':');
		if (p) {
			*p = '\0'; 
			grec->hobbitrrdname = strdup(ldef);
			grec->hobbitpartname = strdup(p+1);
			p = strchr(grec->hobbitpartname, ':');
			if (p) {
				*p = '\0';
				grec->maxgraphs = atoi(p+1);
				if (strlen(grec->hobbitpartname) == 0) {
					xfree(grec->hobbitpartname);
					grec->hobbitpartname = NULL;
				}
			}
		}
		else {
			grec->hobbitrrdname = strdup(ldef);
		}

		ldef = strtok(NULL, ",");
		grec++;
	}
	xfree(lenv);

	setup_done = time(NULL);
}


hobbitrrd_t *find_hobbit_rrd(char *service, char *flags)
{
	/* Lookup an entry in the hobbitrrds table */
	RbtHandle handle;

	rrd_setup();

	if (flags && (strchr(flags, 'R') != NULL)) {
		/* Dont do RRD's for reverse tests, since they have no data */
		return NULL;
	}

	handle = rbtFind(hobbitrrdtree, service);
	if (handle == rbtEnd(hobbitrrdtree)) 
		return NULL;
	else {
		void *k1, *k2;
		rbtKeyValue(hobbitrrdtree, handle, &k1, &k2);
		return (hobbitrrd_t *)k2;
	}
}

hobbitgraph_t *find_hobbit_graph(char *rrdname)
{
	/* Lookup an entry in the hobbitgraphs table */
	hobbitgraph_t *grec;
	int found = 0;
	char *dchar;

	rrd_setup();
	grec = hobbitgraphs; 
	while (!found && (grec->hobbitrrdname != NULL)) {
		found = (strncmp(grec->hobbitrrdname, rrdname, strlen(grec->hobbitrrdname)) == 0);
		if (found) {
			/* Check that it's not a partial match, e.g. "ftp" matches "ftps" */
			dchar = rrdname + strlen(grec->hobbitrrdname);
			if ( (*dchar != '.') && (*dchar != ',') && (*dchar != '\0') ) found = 0;
		}

		if (!found) grec++;
	}

	return (found ? grec : NULL);
}


static char *hobbit_graph_text(char *hostname, char *dispname, char *service, 
			      hobbitgraph_t *graphdef, int itemcount, hg_stale_rrds_t nostale, const char *fmt)
{
	static char *rrdurl = NULL;
	static int rrdurlsize = 0;
	static int gwidth = 0, gheight = 0;
	char *svcurl;
	int svcurllen, rrdparturlsize;
	char rrdservicename[100];

	MEMDEFINE(rrdservicename);

	if (!gwidth) {
		gwidth = atoi(xgetenv("RRDWIDTH"));
		gheight = atoi(xgetenv("RRDHEIGHT"));
	}

	dbgprintf("rrdlink_url: host %s, rrd %s (partname:%s, maxgraphs:%d, count=%d)\n", 
		hostname, 
		graphdef->hobbitrrdname, textornull(graphdef->hobbitpartname), graphdef->maxgraphs, itemcount);

	if ((service != NULL) && (strcmp(graphdef->hobbitrrdname, "tcp") == 0)) {
		sprintf(rrdservicename, "tcp:%s", service);
	}
	else if ((service != NULL) && (strcmp(graphdef->hobbitrrdname, "ncv") == 0)) {
		sprintf(rrdservicename, "ncv:%s", service);
	}
	else {
		strcpy(rrdservicename, graphdef->hobbitrrdname);
	}

	svcurllen = 2048                        + 
		    strlen(xgetenv("CGIBINURL")) + 
		    strlen(hostname)            + 
		    strlen(rrdservicename)  + 
		    strlen(urlencode(dispname ? dispname : hostname));
	svcurl = (char *) malloc(svcurllen);

	rrdparturlsize = 2048 +
			 strlen(fmt)        +
			 3*svcurllen        +
			 strlen(rrdservicename) +
			 strlen(xgetenv("BBSKIN"));

	if (rrdurl == NULL) {
		rrdurlsize = rrdparturlsize;
		rrdurl = (char *) malloc(rrdurlsize);
	}
	*rrdurl = '\0';

	{
		char *rrdparturl;
		int first = 1;
		int step;

		step = (graphdef->maxgraphs ? graphdef->maxgraphs : 5);
		if (itemcount) {
			int gcount = (itemcount / step); if ((gcount*step) != itemcount) gcount++;
			step = (itemcount / gcount);
		}

		rrdparturl = (char *) malloc(rrdparturlsize);
		do {
			if (itemcount > 0) {
				sprintf(svcurl, "%s/hobbitgraph.sh?host=%s&amp;service=%s&amp;graph_width=%d&amp;graph_height=%d&amp;first=%d&amp;count=%d", 
					xgetenv("CGIBINURL"), hostname, rrdservicename, 
					gwidth, gheight,
					first, step);
			}
			else {
				sprintf(svcurl, "%s/hobbitgraph.sh?host=%s&amp;service=%s&amp;graph_width=%d&amp;graph_height=%d", 
					xgetenv("CGIBINURL"), hostname, rrdservicename,
					gwidth, gheight);
			}

			strcat(svcurl, "&amp;disp=");
			strcat(svcurl, urlencode(dispname ? dispname : hostname));

			if (nostale == HG_WITHOUT_STALE_RRDS) strcat(svcurl, "&amp;nostale");

			sprintf(rrdparturl, fmt, rrdservicename, svcurl, svcurl, rrdservicename, svcurl, xgetenv("BBSKIN"));
			if ((strlen(rrdparturl) + strlen(rrdurl) + 1) >= rrdurlsize) {
				rrdurlsize += (4096 + rrdparturlsize);
				rrdurl = (char *) realloc(rrdurl, rrdurlsize);
			}
			strcat(rrdurl, rrdparturl);
			first += step;
		} while (first <= itemcount);
		xfree(rrdparturl);
	}

	dbgprintf("URLtext: %s\n", rrdurl);

	xfree(svcurl);

	MEMUNDEFINE(rrdservicename);

	return rrdurl;
}

char *hobbit_graph_data(char *hostname, char *dispname, char *service, 
			hobbitgraph_t *graphdef, int itemcount,
			hg_stale_rrds_t nostale, hg_link_t wantmeta)
{
	return hobbit_graph_text(hostname, dispname, 
				 service, graphdef, 
				 itemcount, nostale,
				 ((wantmeta == HG_META_LINK) ? metafmt : hobbitlinkfmt));
}

