/*
 * This is a simple test program for the Cascade Historian.
 * To compile, use:
 *     cc -o hitest hitest.c -lcogent
 *
 * To run:
 *     1) run qserve and nserve
 *     2) run histdb with no configuration (/usr/cogent/bin/histdb &)
 *     3) run hitest
 */


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

#include	<cogent.h>

FILE *Fp = NULL;

static void ShowStatus (char* func, ST_STATUS status, char* retbuf)
{
	if (status != ST_OK)
	{
		fprintf (Fp, "Error %d: %s: %s\n", status, func ? func : "unknown",
				retbuf ? retbuf : "Error");
	}
}

static void ShowValues (HI_stVALUE* values, int nvalues)
{
	int		j;
	
	for (j=0; j<nvalues; j++)
	{
		fprintf (Fp, "(%.0f, %g)", values[j].xaxis, values[j].value);
		if ((j+1) % 8 == 0)
			fprintf (Fp, "\n");
		else
			fprintf (Fp, " ");
	}
	if (j%8 != 0)
		fprintf (Fp, "\n");
}

static ST_STATUS QueryHistoryInfo (IP_Task *hist,
								   char *retbuf, int sizeretbuf,
								   int verbose, char *name,
								   double* first, double *last)
{
	ST_STATUS 	status;
	HI_stVALUE	earliest, latest;
	int			length;
	
	status = HI_Length (hist, retbuf, sizeretbuf, name, &length);
	if (status == ST_OK)
	{
		status = HI_Earliest (hist, retbuf, sizeretbuf, name, &earliest);
		ShowStatus ("  HI_Earliest", status, retbuf);
	}
	if (status == ST_OK)
	{
		status = HI_Latest (hist, retbuf, sizeretbuf, name, &latest);
		ShowStatus ("  HI_Latest", status, retbuf);
	}
	if (status == ST_OK)
	{
		if (first)
			*first = earliest.xaxis;
		if (last)
			*last = latest.xaxis;
		if (verbose > 0)
			fprintf (Fp, "%s: %d readings from (%f,%g) to (%f,%g)\n",
					name, length,
					earliest.xaxis, earliest.value,
					latest.xaxis, latest.value);
	}
	if (status == ST_OK)
	{
		status = HI_Describe (hist, retbuf, sizeretbuf, name);
		ShowStatus ("  HI_Describe", status, retbuf);
		if (status == ST_OK && verbose > 0)
			fprintf (Fp, "  Description: %s\n", retbuf);
	}
	
	return (status);
}

static ST_STATUS QueryBufferStats (int nvalues, HI_stVALUE* values)
{
	double 		min, max, mean, stddev, gap;
	int			ngaps, n2, n2gaps;
	HI_stVALUE* new_values;
	
	HI_ScaleBuffer (nvalues, values, 1.0, 0.0, .001, 1000);
	
	HI_StatBuffer (nvalues, values, &min, &max, &mean, &stddev);
	fprintf (Fp, "Y data: min = %g, max = %g, mean = %g, stddev = %g\n",
			min, max, mean, stddev);

	HI_ClipBuffer (nvalues, values, mean-stddev, mean+stddev);
	HI_StatBuffer (nvalues, values, &min, &max, &mean, &stddev);
	fprintf (Fp, "Clipped Y data: min = %g, max = %g, mean = %g, stddev = %g\n",
			min, max, mean, stddev);

	HI_ExchangeBuffer (nvalues, values);
	HI_StatBuffer (nvalues, values, &min, &max, &mean, &stddev);
	fprintf (Fp, "X data: min = %.1f, max = %.1f, mean = %.1f, stddev = %.1f\n",
			min, max, mean, stddev);
	
	HI_ExchangeBuffer (nvalues, values);
	gap = 5.0;
	ngaps = HI_GapCountBuffer (nvalues, values, gap);
	fprintf (Fp, "#data gaps exceeding %g: %d\n", gap, ngaps);
	n2 = nvalues + ngaps;
	new_values = ME_ZMalloc (n2 * sizeof(HI_stVALUE));
	HI_GapFillBuffer (nvalues, values, n2, new_values, gap);
	n2gaps = HI_GapCountBuffer (n2, new_values, gap);
	fprintf (Fp, "#data gaps exceeding %g: %d\n", gap, n2gaps);
	ME_Free(new_values);
		
	return (ST_OK);
}
					

int main (int argc, char** argv)
{
	ST_STATUS		status;
	IP_Task			*myself, *hist;
	char			retbuf[512];
	int				i, opt, nvalues, verbose=0, bufsize=100;
	int				inmemory=0, incount=10000, outcount=100, interpcount=1;
	int				starttime=1, adddata = 0, count;
	double			interpperiod=10.0;
	HI_stVALUE		*values;
	double			first, last, adead=0, pdead=0;
	char			*hiname = NULL;
	char			*taskname = "/hi/default";

	Fp = stdout;
	
	while ((opt = getopt (argc, argv, "a:q:mn:N:i:v:hp:b:d:D:t:")) != -1)
	{
		switch (opt)
		{
		  case 'a':
			hiname = optarg;
			adddata = 1;
			break;
		  case 'q':
			hiname = optarg;
			break;
		  case 'd':
			adead = strtod(optarg, NULL);
			break;
		  case 'D':
			pdead = strtod(optarg, NULL);
			break;
		  case 't':
			starttime = strtol(optarg, NULL, 0);
			if (starttime == 0)
				starttime = time(NULL);
			break;
		  case 'b':
			bufsize=strtol(optarg, NULL, 0);
			break;
		  case 'm':
			inmemory=1;
			break;
		  case 'i':
			interpcount=strtol(optarg, NULL, 0);
			break;
		  case 'p':
			interpperiod=strtod(optarg, NULL);
			break;
		  case 'n':
			incount=strtol(optarg, NULL, 0);
			break;
		  case 'v':
			verbose=strtol(optarg, NULL, 0);
			break;
		  case 'N':
			outcount=strtol(optarg, NULL, 0);
			break;
		  case 'h':
		  default:
			printf ("Usage: %s [-a history] [-b #] [-d #] [-D #] [-i #] [-m] [-n #] [-N #] [-p period] [-q history] [-t time][-v #]\n",
					argv[0]);
			printf ("    -a  add data to this history (create if required)\n");
			printf ("    -b  set number of values to buffer in historian\n");
			printf ("    -d  set absolute deadband\n");
			printf ("    -D  set percent deadband\n");
			printf ("    -i  iterate queries this many times\n");
			printf ("    -m  keep history in memory only, do not create files\n");
			printf ("    -n  specify number of values to inject\n");
			printf ("    -N  specify number of values to query\n");
			printf ("    -p  period for periodic queries\n");
			printf ("    -q  query specified history\n");
			printf ("    -t  specify start time for values, 0->now\n");
			printf ("    -v  specify verbosity level\n");
			printf ("        0 = print only errors (default)\n");
			printf ("        1 = print query summary information\n");
			printf ("        2 = print query data\n");
			exit (-1);
			break;
		}
	}
	
	myself = IP_NserveInit ("hitest", NULL, "hitest", 0, 0);
	hist = IP_NserveLookupName (taskname);

	if (!hist)
	{
		printf ("Could not locate historian %s\n", taskname);
		exit (0);
	}

	if (hiname)
	{
		/* test if specified history already exists - if not, create it */
		status = HI_Count (hist, retbuf, sizeof(retbuf),
								  hiname, &count);
		ShowStatus ("HI_Count", status, retbuf);
		if (status == ST_OK && count == 0)
		{
			status = HI_History (hist, retbuf, sizeof(retbuf), hiname);
			ShowStatus ("HI_History", status, retbuf);

			if (!inmemory)
			{
				status = HI_FileBase (hist, retbuf, sizeof(retbuf), hiname,
									  ".", hiname, ".hist", 0);
				ShowStatus ("HI_FileBase", status, retbuf);
			}
		}
		if (adddata)
		{
			status = HI_Bufsize (hist, retbuf, sizeof(retbuf),
								 hiname, bufsize, NULL);
			ShowStatus ("HI_Bufsize", status, retbuf);
			
			if (adead != 0)
			{
				status = HI_Deadband (hist, retbuf, sizeof(retbuf), hiname,
									  "absolute", adead);
				ShowStatus ("HI_Deadband(absolute)", status, retbuf);
			}
			if (pdead != 0)
			{
				status = HI_Deadband (hist, retbuf, sizeof(retbuf), hiname,
									  "percent", pdead);
				ShowStatus ("HI_Deadband(percent)", status, retbuf);
			}
			
			for (i=0; i<incount; i++, starttime++)
			{
				status = HI_Add (hist, retbuf, sizeof(retbuf), hiname, i,
								 starttime, 0);
				ShowStatus ("HI_Add", status, retbuf);
			}

			status = HI_Flush (hist, retbuf, sizeof(retbuf), "*");
			ShowStatus ("HI_Flush", status, retbuf);
		}

		/* query history stats */
		status = QueryHistoryInfo (hist, retbuf, sizeof(retbuf), verbose,
								   hiname, &first, &last);

		if (status == ST_OK)
		{
			/* query history interpolator */
			char	*xargs[10];
			
			xargs[0] = malloc (32);
			sprintf (xargs[0], "%f", interpperiod);
			
			for (i=0; i<interpcount; i++)
			{
				if (status == ST_OK)
				{
					status = HI_InterpolateData (hist, hiname,
												 "NoInterpolator",
												 first, last-first,
												 0, NULL, &values, &nvalues);
					ShowStatus ("HI_InterpolateData (NoInterpolator)",
								status, NULL);
				}
				if (status == ST_OK && values)
				{
					if (verbose)
					{
						fprintf (Fp, "NoInterpolator returned %d values\n",
								nvalues);
						if (verbose > 1)
							ShowValues (values, nvalues);
					}
					ME_Free (values);
				}
				if (status == ST_OK)
				{
					status = HI_InterpolateData (hist, hiname,
												 "PeriodicInterpolator",
												 first, last-first,
												 1, xargs, &values, &nvalues);
					ShowStatus ("HI_InterpolateData (PeriodicInterpolator)",
								status, NULL);
				}
				if (status == ST_OK && values)
				{
					if (verbose)
					{
						fprintf (Fp, "PeriodicInterpolator (%.2f s) returned %d values\n",
								interpperiod, nvalues);
						QueryBufferStats (nvalues, values);
					
						if (verbose > 1)
							ShowValues (values, nvalues);
					}
					ME_Free (values);
				}
			}
		}			
	}
	else
	{
		/* query all points available */
		int			count, countC, i;
		char 		*historylist[20];
		char		historybuf[256];
				
		status = HI_Count (hist, retbuf, sizeof(retbuf),
								  "*", &count);
		if (status == ST_OK)
		{
			fprintf (Fp, "%d histor%s available: \n",
					 count, (count==1?"y":"ies"));

			/* note: using reserved buffer to hold history names */
			status = HI_List (hist, historybuf, sizeof(historybuf),
							  "*", 0, 20, historylist, &countC);
		}
		if (status == ST_OK)
		{
			for (i=0; i<countC; i++)
			{
				hiname = historylist[i];
				
				QueryHistoryInfo (hist, retbuf, sizeof(retbuf), 1, hiname,
								  NULL, NULL);
			}
		}
		else
			ShowStatus ("HI_List", status, retbuf);
	}


	return (0);
}
