/****************************************************************************
****************************************************************************/

#include "rrd_tool.h"
#include "rrd_statsl.h"
#include <sys/stat.h>

//#define DEBUG
// global constants
char DOUBLE_FORMAT[] = "%10.2e";
char LONG_FORMAT[] = "%d";
char STRING_FORMAT[] = "%s";

// i = 1 int, I = array of int
// s = 1 string, S = array of string
// d= 1 double, D = array of double

#define FMEAN 0
#define FMIN 1
#define FMAX 2
#define FINT 3

struct stats_values * new_psv ( struct stats_values * prev_psv ) {

	struct stats_values * psv;

	psv = malloc (sizeof(struct stats_values));
	if (psv != NULL) {
		if (prev_psv != NULL) prev_psv->pnext = psv;
		psv->pnext=NULL;
		psv->nbvals = 0;
		psv->datatype = 0;
		psv->format = NULL;
	} else {
		rrd_set_error("new_psv allocation failure");
	}
	return ( psv );
}

// make a new psv for storing only one integer
struct stats_values * new_int_psv ( struct stats_values * prev_psv, int i ) {

	struct stats_values * psv;

	psv = new_psv(prev_psv);
	if (psv != NULL) {
		psv->datatype = 'i';
		psv->val.i = i;
		psv->nbvals = 0;
		psv->format = LONG_FORMAT;
	}
	return ( psv );
}

// make a new psv for storing only one double
struct stats_values * new_double_psv ( struct stats_values * prev_psv, double d ) {

	struct stats_values * psv;

	psv = new_psv(prev_psv);
	if (psv != NULL) {
		psv->datatype = 'd';
		psv->val.d = d;
		psv->nbvals = 0;
		psv->format = DOUBLE_FORMAT;
	}
	return ( psv );
}

void free_psv ( struct stats_values * psv ) {
	while ( psv != NULL ) {
		struct stats_values * ptemp = psv;
		psv = psv->pnext;
		if ( (ptemp->nbvals > 0) && (ptemp->val.ptr != NULL) ) {
			if (ptemp->datatype == 'S') {				// special case: array of string
				int i;
				char ** strptr = ptemp->val.ptr;
				for (i=0; i < ptemp->nbvals; i++ ) {
					if (*strptr != NULL) free(*strptr);
					strptr++;
				}
			}
			free (ptemp->val.ptr);
		}
														// special case : string
		if ( (ptemp->datatype == 's') && (ptemp->val.ptr != NULL) ) free (ptemp->val.ptr);
		free(ptemp);
	}
}

int find_best_rra ( 
	rrd_t		*prrd,
	time_t		*start,
	time_t		*end,
	int			debuglevel,
	long		pstep,
	char		*cfname) {

	long			best_rra=-1;
	int i;

    /* find the rra which best matches the requirements */
	// the rules are the following
	//		- overlap_factor	: overlapping factor between the required and available range
	//		- step_diff			: difference between the required and available step
	{
		long step_diff=0;
		float ovrlp_factor = 0.0;
		time_t cal_start,cal_end;

		for(i=0;i<(int)prrd->stat_head->rra_cnt;i++){
			if(strcmp (prrd->rra_def[i].cf_nam, cfname) == 0){

				cal_end = (prrd->live_head->last_up - (prrd->live_head->last_up % (prrd->rra_def[i].pdp_cnt * prrd->stat_head->pdp_step)));
				cal_start = (cal_end - (prrd->rra_def[i].pdp_cnt * prrd->rra_def[i].row_cnt	* prrd->stat_head->pdp_step));

				if (debuglevel > 0) fprintf ( stderr,  " rra%i start (%lu) cal_start (%lu) end (%lu) cal_end (%lu)", i, *start, cal_start, *end, cal_end );
				{
					long req_range = *end -*start;
					float cur_ovrlp_factor;

					long cur_step_diff = labs(pstep - (prrd->stat_head->pdp_step * prrd->rra_def[i].pdp_cnt));
					if (debuglevel > 0) fprintf ( stderr,  " stdiff (%d)", cur_step_diff );

					// overlap factor is the factor between the required range and the available range
					if (cal_start>*start) req_range -= (cal_start-*start);
					if (cal_end<*end) req_range -= (*end-cal_end);
					cur_ovrlp_factor = (float)req_range / ((float)*end - (float)*start);
					if (debuglevel > 0) fprintf ( stderr,  " ovfact (%3.3f)", cur_ovrlp_factor );

					if ( req_range > 0 ) {
						if ( best_rra== -1 ) {
							step_diff = cur_step_diff;
							ovrlp_factor = cur_ovrlp_factor;
							best_rra = i;
						} else {
							// rule : we compare the best and current rras
							//		if the overlap factors are not 'very' different (90%), take the best step
							double magic_diff = (cur_ovrlp_factor - ovrlp_factor) / ovrlp_factor;
							if (debuglevel > 0) fprintf ( stderr,  " diff (%3.3f)", magic_diff );
							if ( magic_diff > 0.1 ) {
								step_diff = cur_step_diff;
								ovrlp_factor = cur_ovrlp_factor;
								best_rra = i;
							} else if ( magic_diff > -0.1 ) {
								if ( step_diff > cur_step_diff )  {
									step_diff = cur_step_diff;
									ovrlp_factor = cur_ovrlp_factor;
									best_rra = i;
								}
							}
						}
						if (debuglevel > 0) fprintf ( stderr,  " best_rra (%d)", best_rra );
					}

				}


				if (debuglevel > 0) fprintf ( stderr,  "\n" );
			}
		}

	}

	if (best_rra==-1)	{
		rrd_set_error("the RRD does not contain an RRA matching the chosen CF");
	}

	return(best_rra);

}

// ----------------------------------------------------------------------
// computes the mean values of an rrd, for all ds
int rrd_stats_func (
					FILE *		in_file,
					rrd_t		*prrd,
					time_t		*start,
					time_t		*end,
					struct stats_values **psv,
					//double	**results,
					int		debuglevel,
					long		pstep,
					char		*cfname,
					int		func)
{
	long			i,ii;
	time_t			rra_start_time,rra_end_time;
	long			rra_base, rra_pointer=0;
	long			choosen_rra = -1;
	long			start_offset, end_offset;
	rrd_value_t		value;
	unsigned long		rows;
	long			ds_cnt, step;
	//long			nbresults = 0;
	long 			* nbresults;
	double			* results;

	choosen_rra = find_best_rra ( prrd, start, end, debuglevel, pstep, cfname );
	if ( choosen_rra == -1 ) return ( -1 );

	ds_cnt = prrd->stat_head->ds_cnt;

	results = malloc (ds_cnt * sizeof(rrd_value_t));
	if ( results == NULL ) {
		rrd_set_error("rrd_stats_mean : memory allocation failure");
		return(-1);
	}

	nbresults = malloc (ds_cnt * sizeof(long));
	if ( nbresults == NULL ) {
		rrd_set_error("rrd_stats_mean : memory allocation failure");
		free(results);
		return(-1);
	}

	for (i = 0; i < ds_cnt; i ++ ) {
		results[i] = DNAN;
		nbresults[i] = 0;
	}

	step = prrd->stat_head->pdp_step * prrd->rra_def[choosen_rra].pdp_cnt;
	*start -= (*start % step);
	if (*end % step) *end += (step - *end % step);
	rows = (*end - *start) / step;

	if (debuglevel > 0) fprintf ( stderr, "start %lu end %lu step %lu rows  %lu\n", *start,*end,step,rows);

	/* find base address of rra */
	rra_base=ftell(in_file);
	for(i=0;i<choosen_rra;i++)
		rra_base += ( ds_cnt
		* prrd->rra_def[i].row_cnt
		* sizeof(rrd_value_t));

	/* find start and end offset */
	rra_end_time = (prrd->live_head->last_up - (prrd->live_head->last_up % step));
	rra_start_time = (rra_end_time - ( step * (prrd->rra_def[choosen_rra].row_cnt-1)));
	start_offset = (long)(*start +step - rra_start_time) / (long)step;
	end_offset = (long)(rra_end_time - *end ) / (long)step;
	if (debuglevel > 0) fprintf ( stderr, "rra_start %lu, rra_end %lu, start_off %li, end_off %li\n", rra_start_time,rra_end_time,start_offset,end_offset);

	if (start_offset <= 0) {
		rra_pointer = prrd->rra_ptr[choosen_rra].cur_row+1;
		start_offset = 0;
	} else {
		rra_pointer = prrd->rra_ptr[choosen_rra].cur_row+1+start_offset;
	}

	if(fseek(in_file,(rra_base + (rra_pointer * ds_cnt * sizeof(rrd_value_t))),SEEK_SET) != 0){
		rrd_set_error("seek error in RRA");
		free(nbresults);
		free(results);
		return(-1);
	}
	if (debuglevel > 0) fprintf ( stderr, "First Seek: rra_base %lu rra_pointer %lu\n", rra_base, rra_pointer);
	/* step trough the array */
	for (i=start_offset;i<(long)(prrd->rra_def[choosen_rra].row_cnt-end_offset);i++){

		// end of rra reached
		if (i>=(int)prrd->rra_def[choosen_rra].row_cnt) break;

		/* OK we are inside the valid area but the pointer has to be wrapped*/
		if (rra_pointer >= (int)prrd->rra_def[choosen_rra].row_cnt) {
			rra_pointer -= prrd->rra_def[choosen_rra].row_cnt;
			if(fseek(in_file,(rra_base+rra_pointer * ds_cnt * sizeof(rrd_value_t)),SEEK_SET) != 0){
				rrd_set_error("wrap seek in RRA did fail");
				free(nbresults);
				free(results);
				return(-1);
			}
			if (debuglevel > 0) fprintf ( stderr, "wrap seek ...\n");
		}

		if (debuglevel > 1) fprintf ( stderr, "valid fetch %li -- ",i);

		for(ii=0;ii<(int)ds_cnt;ii++) {
			if(fread(&value, sizeof(rrd_value_t),1,in_file) != 1){
				rrd_set_error("fetching cdp from rra");
				free(nbresults);
				free(results);
				return(-1);
			}
			if ( !isnan(value) ) {
				if (debuglevel > 1) fprintf ( stderr, "%10.2f <",value);
				nbresults[ii]++;

				switch ( func ) {
				case FMAX:
					if (isnan((results)[ii]))	(results)[ii] = value;
					else	{
						if ( (results)[ii] < value ) {
							(results)[ii] = value;
						}
					}
					break;
				case FMIN:
					if (isnan((results)[ii]))	(results)[ii] = value;
					else	{
						if ( (results)[ii] > value ) {
							(results)[ii] = value;
						}
					}
					break;
				case FMEAN:
					if (isnan((results)[ii]))	(results)[ii] = value;
					else						(results)[ii] += value;
					break;
				case FINT:
					if (isnan((results)[ii]))	(results)[ii] =  (value*step);
					else						(results)[ii] += (value*step);
					break;
				default:
					break;
				}
			} else {
				if (debuglevel > 1) fprintf ( stderr, " NaN" );
			}
		}
		rra_pointer ++;
		if (debuglevel > 1) fprintf ( stderr, "\n");
	}

	if ( func == FMEAN ) {
		for (i = 0; i < ds_cnt; i ++ ) {
			if (nbresults[i] > 0) {
				results[i] /= nbresults[i];
			} else {
				results[i] = DNAN;
			}
			//fprintf ( stderr, "%0.10e ", results[i]);
		}
		//fprintf ( stderr, "\n");
	}

	free(nbresults);

	while (1) {
		struct stats_values *tmp_psv;
		if((*psv = new_int_psv ( NULL, step )) == NULL )	break;	// step
		if((tmp_psv = new_int_psv ( *psv, *start )) == NULL )	break;	// start time
		if((tmp_psv = new_int_psv ( tmp_psv, *end )) == NULL )	break;	// end time
		if((tmp_psv = new_psv ( tmp_psv )) == NULL )		break;	// means results
		tmp_psv->val.ptr = results;
		tmp_psv->nbvals = ds_cnt;
		tmp_psv->format = DOUBLE_FORMAT;
		tmp_psv->datatype = 'D';										// array of double

		if((tmp_psv = new_psv ( tmp_psv )) == NULL )		break;	// name of ds

		tmp_psv->val.ptr = malloc (ds_cnt * sizeof(char*));
		if ( tmp_psv->val.ptr== NULL ) {
			rrd_set_error("malloc ds ptr array");
			break;
		}
		tmp_psv->nbvals = 0;
		tmp_psv->format = STRING_FORMAT;
		tmp_psv->datatype = 'S';
		{
			char ** strptr = tmp_psv->val.ptr;

		    for(i=0;i<ds_cnt;i++){
				*strptr = malloc(sizeof(char) * DS_NAM_SIZE);
				if (*strptr == NULL) {
					rrd_set_error("malloc string array");
					break;
				}
				strncpy(*strptr,prrd->ds_def[i].ds_nam,DS_NAM_SIZE-1);
				*strptr++;
				tmp_psv->nbvals++;
			}
		}

		return(0);
	}

	// here in error case
	if ( *psv != NULL) free_psv(*psv);
	return(-1);
}

// ----------------------------------------------------------------------
// computes distribution for 1 rrd, 1 ds, auto rra, in distn intervals
int rrd_stats_dist (
					FILE *		in_file,
					rrd_t		*prrd,
					time_t		*start,
					time_t		*end,
					double		min,
					double		max,
					long		distn,
					int			ds,
					struct stats_values **psv,
					int			debuglevel,
					long		pstep,
					char		*cfname)
{
	long			i;
	time_t			rra_start_time,rra_end_time;
	long			rra_base, choosen_rra=0, rra_pointer=0;
	long			start_offset, end_offset;
	int			first_full = 1;
	int			first_part = 1;
	unsigned long		rows;
	long			ds_cnt, step;
	long			nbresults = 0;
	rrd_value_t		*dataptr, before=0.0, after=0.0;
	double			*results;

	choosen_rra = find_best_rra ( prrd, start, end, debuglevel, pstep, cfname );
	if ( choosen_rra == -1 ) return ( -1 );

	results = malloc (distn * sizeof(rrd_value_t));
	if (results == NULL) {
		rrd_set_error("rrd_stats : memory allocation failure");
		return(-1);
	}

	ds_cnt = prrd->stat_head->ds_cnt;
	for (i = 0; i < distn; i ++ ) results[i] = 0.0;

	dataptr = malloc (ds_cnt * sizeof(rrd_value_t));
	if ( dataptr == NULL ) {
		rrd_set_error("rrd_stats_dist : memory allocation failure");
		free(results);
		return(-1);
	}

	step = prrd->stat_head->pdp_step * prrd->rra_def[choosen_rra].pdp_cnt;
	*start -= (*start % step);
	if (*end % step) *end += (step - *end % step);
	rows = (*end - *start) / step +1;

	if (debuglevel > 0) {
		fprintf ( stderr, "start %lu end %lu step %lu rows  %lu\n", *start,*end,step,rows);
	}

	/* find base address of rra */
	rra_base=ftell(in_file);
	for(i=0;i<choosen_rra;i++) rra_base += ( ds_cnt * prrd->rra_def[i].row_cnt	* sizeof(rrd_value_t));

	/* find start and end offset */
	rra_end_time = (prrd->live_head->last_up - (prrd->live_head->last_up % step));
	rra_start_time = (rra_end_time - ( step * (prrd->rra_def[choosen_rra].row_cnt-1)));
	start_offset = (*start - rra_start_time) / (long)step;
	end_offset = (rra_end_time - *end ) / (long)step;
	if (debuglevel > 0) {
		fprintf ( stderr, "rra_start %lu, rra_end %lu, start_off %li, end_off %li\n", rra_start_time,rra_end_time,start_offset,end_offset);
	}

	if (start_offset <= 0) {
		rra_pointer = prrd->rra_ptr[choosen_rra].cur_row+1;
		start_offset = 0;
	} else {
		rra_pointer = prrd->rra_ptr[choosen_rra].cur_row+1+start_offset;
	}

	if(fseek(in_file,(rra_base + (rra_pointer * ds_cnt * sizeof(rrd_value_t))),SEEK_SET) != 0){
		rrd_set_error("seek error in RRA");
		free(dataptr);
		free(results);
		return(-1);
	}
	if (debuglevel > 0) fprintf ( stderr, "First Seek: rra_base %lu rra_pointer %lu\n", rra_base, rra_pointer);
	/* step trough the array */
	for (i=start_offset;i<(long)(prrd->rra_def[choosen_rra].row_cnt-end_offset);i++){

		// end of rra reached
		if (i>=(int)prrd->rra_def[choosen_rra].row_cnt) break;

		/* OK we are inside the valid area but the pointer has to be wrapped*/
		if (rra_pointer >= (int)prrd->rra_def[choosen_rra].row_cnt) {
			rra_pointer -= prrd->rra_def[choosen_rra].row_cnt;
			if(fseek(in_file,(rra_base+rra_pointer * ds_cnt * sizeof(rrd_value_t)),SEEK_SET) != 0){
				rrd_set_error("wrap seek in RRA did fail");
				free(dataptr);
				free(results);
				return(-1);
			}
			if (debuglevel > 0) fprintf ( stderr, "wrap seek ...\n");
		}

		if (debuglevel > 1) fprintf ( stderr, "valid fetch %li -- ",i);
		nbresults++;

		if((long)fread(dataptr, sizeof(rrd_value_t),ds_cnt,in_file) != ds_cnt){
			rrd_set_error("fetching cdp from rra");
			free(dataptr);
			free(results);
			return(-1);
		}

		if (debuglevel > 1) fprintf ( stderr, "%10.2f ", dataptr[ds]);

		if ( !isnan(dataptr[ds]) ) {
			int ndx = (int)((dataptr[ds]-min)/(max-min) * distn);
			if		(ndx < 0)		{ before	  ++; }	// values smaller that min
			else if (ndx >= distn)	{ after		  ++; }	// values greater than max
			else					{ results[ndx]++; };
		}
		rra_pointer ++;
		if (debuglevel > 1) fprintf ( stderr, "\n");
	}

	before /= nbresults; after /= nbresults;
	for (i = 0; i < distn; i ++ ) {
		results[i] /= nbresults;
		//fprintf ( stderr, "%0.10e ", results[i]);
	}
	//fprintf ( stderr, "\n");


	free(dataptr);

	while (1) {
		struct stats_values *tmp_psv;
		if((*psv = new_int_psv ( NULL, step )) == NULL )		break;	// step
		if((tmp_psv = new_int_psv ( *psv, *start )) == NULL )	break;	// start time
		if((tmp_psv = new_int_psv ( tmp_psv, *end )) == NULL )	break;	// end time
		if((tmp_psv = new_double_psv ( tmp_psv, before )) == NULL )	break;
		if((tmp_psv = new_double_psv ( tmp_psv, after )) == NULL )	break;
		if((tmp_psv = new_psv ( tmp_psv )) == NULL )			break;	// means results
		tmp_psv->val.ptr = results;
		tmp_psv->nbvals = distn;
		tmp_psv->format = DOUBLE_FORMAT;
		tmp_psv->datatype = 'D';										// array of double
		return(0);
	}

	// here in error case
	if ( *psv != NULL) free_psv(*psv);
	return(-1);

}

/* main entry point for all stats functions.
	rules :
		- if this function returns 1, the result array must be freed (!!!)
*/
int rrd_stats (		int argc,
			char **argv,
			struct stats_values **psv
		)
{

	FILE		*rrd_file;
	rrd_t		rrd;
	int		i;
	time_t		start_tmp=0,end_tmp=0;
	int		linepass = 0; /* stack can only follow directly after LINE* AREA or STACK */
	struct		rrd_time_value  start_tv, end_tv;
	char		*parsetime_error = NULL;
	int		retval = -1;
	int		debuglevel = 0;

	optind = 0; opterr = 0;  /* initialize getopt */

	parsetime("end-24h", &start_tv);
	parsetime("now", &end_tv);

	while (1){
		static struct option long_options[] = {
			{"start",	required_argument, 0,  's'},
			{"end",		required_argument, 0,  'e'},
			//{"max",		required_argument, 0,  'a'},
			//{"min",		required_argument, 0,  'i'},
			//{"ds",		required_argument, 0,  'd'},
			//{"distn",	required_argument, 0,  'n'},
			{"debug",	required_argument, 0,  'l'},
			{0,0,0,0}
		};
		int option_index = 0;
		int opt;


		opt = getopt_long(argc, argv, "s:e:l:", long_options, &option_index);

		if (opt == EOF)	break;

		switch(opt) {
		case 's':
			if ((parsetime_error = parsetime(optarg, &start_tv))) {
				rrd_set_error( "start time: %s", parsetime_error );
				return retval;
			}
			break;
		case 'l':
			debuglevel= atoi(optarg);
			break;
		case 'e':
			if ((parsetime_error = parsetime(optarg, &end_tv))) {
				rrd_set_error( "end time: %s", parsetime_error );
				return retval;
			}
			break;

		case '?':
			rrd_set_error("unknown option '%s'",argv[optind-1]);
			return retval;
			break;
		}
	}


	if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
		return retval;
	}

	if (start_tmp < 3600*24*365*10){
		rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
		return retval;
	}

	if (end_tmp < start_tmp) {
		rrd_set_error("start (%ld) should be less than end (%ld)",
			start_tmp, end_tmp);
		return retval;
	}

	/* need at least 2 arguments: filename, and the rest */
	if (argc-optind < 2) {
		rrd_set_error("not enough arguments");
		return retval;
	}

	if(rrd_open(argv[optind],&rrd_file,&rrd, RRD_READONLY)==-1){
		return retval;
	}

	if (debuglevel > 0) {
		fprintf ( stderr, " stats end (%ld) start (%ld)\n", end_tmp, start_tmp);

		fprintf ( stderr, "rrd (%s)\n", argv[optind] );
		fprintf ( stderr, " version: (%s)\n",rrd.stat_head->version);
		fprintf ( stderr, " step: (%lu)\n",rrd.stat_head->pdp_step);
		fprintf ( stderr, " lastupdate (%ld)\n", rrd.live_head->last_up );

		fprintf ( stderr, " ds (%lu)\n", rrd.stat_head->ds_cnt);

		for(i=0;i<(int)(rrd.stat_head->ds_cnt);i++){
			fprintf ( stderr, "  ds%lu name (%s)", i, rrd.ds_def[i].ds_nam);
			fprintf ( stderr, " type (%s)",rrd.ds_def[i].dst);
			fprintf ( stderr, " minimal_heartbeat (%lu)",rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
			fprintf ( stderr, " min (%0.10e)",rrd.ds_def[i].par[DS_min_val].u_val);
			fprintf ( stderr, " max (%0.10e)",rrd.ds_def[i].par[DS_max_val].u_val);
			fprintf ( stderr, " last_ds (%s)",rrd.pdp_prep[i].last_ds);
			fprintf ( stderr, " value (%0.10e)",rrd.pdp_prep[i].scratch[PDP_val].u_val);
			fprintf ( stderr, " unknown_sec (%lu)\n", rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
		}

		fprintf ( stderr, " rra (%lu)\n", rrd.stat_head->rra_cnt);
		for(i=0;i<(int)rrd.stat_head->rra_cnt;i++){
			long ii;

			fprintf ( stderr, "  rra%lu cf (%s)",i, rrd.rra_def[i].cf_nam);
			fprintf ( stderr, " pdp_per_row (%lu)", rrd.rra_def[i].pdp_cnt);
			fprintf ( stderr, " row_cnt (%lu)", rrd.rra_def[i].row_cnt);
			fprintf ( stderr, " cdp_prep" );
			for(ii=0;ii<(int)rrd.stat_head->ds_cnt;ii++){
				double value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
				fprintf ( stderr, " (%0.10e)", value);
				fprintf ( stderr, " u.d.(%lu)", rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt);
			}
			fprintf ( stderr, "\n");
		}
	}


	//for(i=optind+1;i<argc;i++){
		i = optind+1;

		if ( strnicmp (argv[i], "mean:",5) ==  0 ) {
			char dummy[128], cfname[128];
			int step;
			if(sscanf(argv[i], "%4[A-Za-z]:%d:" CF_NAM_FMT, dummy, &step, &cfname) ==  3) {
				if ( rrd_stats_func ( rrd_file, &rrd, &start_tmp, &end_tmp, psv, debuglevel, step, cfname, FMEAN) == 0 ) {
					retval = 1;
				}
			} else {
				rrd_set_error("rrd_stats : cannot parse mean argument");
			}
		} else if (strnicmp (argv[i], "dist:", 5) == 0 ) {
			// hope that this comes with "dist:min:max:ds:distn:step"
			char dsname[128], dummy[128], cfname[128];
			float min, max;
			int distn, ds, step;
			if(sscanf(argv[i], "%4[A-Za-z]:%e:%e:" DS_NAM_FMT ":%d:%d:" CF_NAM_FMT, dummy, &min, &max, dsname, &distn, &step, &cfname) ==  7) {
				ds = ds_match(&rrd,dsname);
				if (ds != -1) {
					if ( rrd_stats_dist ( rrd_file, &rrd, &start_tmp, &end_tmp, min, max, distn, ds, psv, debuglevel, step, cfname) == 0 ) {
						retval = 1;
					}
				}
			} else {
				rrd_set_error("rrd_stats : cannot parse dist argument");
			}
		} else if ( strnicmp (argv[i], "max:",4) ==  0 ) {
			char dummy[128], cfname[128];
			int step;
			if(sscanf(argv[i], "%4[A-Za-z]:%d:" CF_NAM_FMT, dummy, &step, &cfname) ==  3) {
				if ( rrd_stats_func ( rrd_file, &rrd, &start_tmp, &end_tmp, psv, debuglevel, step, cfname, FMAX) == 0 ) {
					retval = 1;
				}
			} else {
				rrd_set_error("rrd_stats : cannot parse mean argument");
			}
		} else if ( strnicmp (argv[i], "min:",4) ==  0 ) {
			char dummy[128], cfname[128];
			int step;
			if(sscanf(argv[i], "%4[A-Za-z]:%d:" CF_NAM_FMT, dummy, &step, &cfname) ==  3) {
				if ( rrd_stats_func ( rrd_file, &rrd, &start_tmp, &end_tmp, psv, debuglevel, step, cfname, FMIN) == 0 ) {
					retval = 1;
				}
			} else {
				rrd_set_error("rrd_stats : cannot parse mean argument");
			}
		} else if ( strnicmp (argv[i], "int:",4) ==  0 ) {
			char dummy[128], cfname[128];
			int step;
			if(sscanf(argv[i], "%4[A-Za-z]:%d:" CF_NAM_FMT, dummy, &step, &cfname) ==  3) {
				if ( rrd_stats_func ( rrd_file, &rrd, &start_tmp, &end_tmp, psv, debuglevel, step, cfname, FINT) == 0 ) {
					retval = 1;
				}
			} else {
				rrd_set_error("rrd_stats : cannot parse mean argument");
			}
		} else {
			rrd_set_error("rrd_stats : unknown function");
		}
	//}


	rrd_free(&rrd);
	fclose(rrd_file);

	return retval;
}


int main_test (int argc, char **argv) {

	struct stats_values *psv;

	if ( rrd_stats (argc, argv, &psv) == 1 ) {
										// in this case, result must be freed
		free_psv ( psv );
	}

	if (rrd_test_error()) {
		fprintf (stderr, "ERROR: %s\n",rrd_get_error());
		rrd_clear_error();
	}

	return(0);
}
