/****************************************************************************/
/* Copyright 2003-2010 MBARI												*/
/****************************************************************************/
/* Summary	: Variable Handling Routines									*/
/* Filename : variable.c													*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: Respirometers													*/
/* Revision : 1.0															*/
/* Created	: 09/01/2010 from config.c										*/
/*																			*/
/* MBARI provides this documentation and code "as is", with no warranty,	*/
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium	*/
/* Research Institute to assist in its use, correction, modification, or	*/
/* enhancement. This information should not be published or distributed to	*/
/* third parties without specific written permission from MBARI.			*/
/*																			*/
/****************************************************************************/
/* Modification History:													*/
/* 01sep2010 rah - split off from config.c									*/
/* 19jul2016 rah - adapted for OASIS5 on PIC32								*/
/* $Log$
 */
/****************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions				*/
#include <mbariConst.h>			/* MBARI constants						*/
#include <oasis.h>				/* OASIS controller definitions			*/
#include <olist.h>				/* OASIS linked list definitions		*/
#include <parse.h>				/* Generic parser declarations			*/
#include <variable.h>			/* Variable definitions					*/
#include <utils.h>				/* OASIS utility functions				*/
#include <debug.h>

#include <string.h>				/* Standard string library				*/
#include <ctype.h>				/* Standard character library			*/
#include <float.h>


/********************************/
/*		Module Local Data		*/
/********************************/

MLocal LstHead	varList;


/************************************************************************/
/* Function	   : initVars												*/
/* Purpose	   : Initialize Varible module								*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
Void initVars(Void)
{
	list_init(&varList);

} /* initVars() */


/************************************************************************/
/* Function	   : internalFindVariable									*/
/* Purpose	   : Find a variable, return ptr to VarStruct with value	*/
/* Inputs	   : name ptr												*/
/* Outputs	   : VarStruct *, or NULL if not found						*/
/************************************************************************/
MLocal VarStruct *internalFindVariable(char *varName)
{
	Reg Node	*np;
	Reg VarStruct *vp;

	for (np = list_first(&varList); np != NULLNODE; np = list_next(np))
	{
		vp = (VarStruct *)np;
		if (strcasecmp(varName, vp->name) == 0)
			return(vp);
	}

	return(NULL);

} /* internalFindVariable() */


/************************************************************************/
/* Function	   : findVariable											*/
/* Purpose	   : Find a variable, return ptr to VarStruct with value	*/
/* Inputs	   : name ptr												*/
/* Outputs	   : VarStruct *, or NULL if not found						*/
/************************************************************************/
VarStruct *findVariable(char *varName)
{
	Reg VarStruct *vp;

	lockList(&varList);
	vp = internalFindVariable(varName);
	unlockList(&varList);
	return(vp);

} /* findVariable() */


/************************************************************************/
/* Function	   : longVal												*/
/* Purpose	   : Return long value of VarStruct							*/
/* Inputs	   : VarStruct ptr											*/
/* Outputs	   : Int32 value, or ERROR if not a numeric					*/
/************************************************************************/
Int32 longVal(VarStruct *vp)
{
	if ((vp->flags & VF_ISVAL) == 0)
		return(ERROR);
	return(vp->lval);

} /* longVal() */


/************************************************************************/
/* Function	   : fltVal													*/
/* Purpose	   : Return floating point value of VarStruct				*/
/* Inputs	   : VarStruct ptr											*/
/* Outputs	   : float value, or FLT_MIN if error						*/
/************************************************************************/
float fltVal(VarStruct *vp)
{
	if ((vp->flags & VF_ISVAL) == 0)
		return(FLT_MIN);
	return((vp->flags & VF_ISFLT) ? vp->fval : (float)(vp->lval));

} /* fltVal() */


/************************************************************************/
/* Function	   : checkForHex											*/
/* Purpose	   : Check if string refers to hex number					*/
/* Inputs	   : Ptr to input string, ptr to VarStruct					*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Errno checkForHex(char *tail, VarStruct *vp)
{
	Int32		val;
	int			n;
	char		*p;
	MBool		ishex = FALSE;

	p = tail + strlen(tail) - 1;

	if ((strncmp(tail, "0x", 2) == 0) || (strncmp(tail, "0X", 2) == 0))
		ishex = TRUE;
	else if ((*p == 'h') || (*p == 'H'))
	{
		ishex = TRUE;
		*p = '\0';
	}

	if (ishex && (sscanf(tail, " %x%n", &val, &n) > 0))
		if (n >= strlen(tail))
		{
			vp->lval = val;
			vp->fval = (Flt32)val;
			vp->flags = VF_ISVAL;
			return(OK);
		}

	return(ERROR);

} /* checkForHex() */


/************************************************************************/
/* Function	   : checkForDec											*/
/* Purpose	   : Check if string refers to decimal number				*/
/* Inputs	   : Ptr to input string, ptr to VarStruct					*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Errno checkForDec(char *tail, VarStruct *vp)
{
	Int32		val;

	if (sscanf(tail, " %d", &val) > 0)
	{
		vp->lval = val;
		vp->fval = (Flt32)val;
		vp->flags = VF_ISVAL;
		return(OK);
	}

	return(ERROR);

} /* checkForDec() */


/************************************************************************/
/* Function	   : checkForFloat											*/
/* Purpose	   : Check if string refers to floating point number		*/
/* Inputs	   : Ptr to input string, ptr to VarStruct					*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Errno checkForFloat(char *tail, VarStruct *vp)
{
	Flt32		val;
	int			n;

	if (sscanf(tail, " %f%n", &val, &n) > 0)
		if (n >= strlen(tail))
		{
			vp->fval = val;
			vp->lval = (Int32)val;
			vp->flags = VF_ISVAL | VF_ISFLT;
			return(OK);
		}

	return(ERROR);

} /* checkForFloat() */


/************************************************************************/
/* Function	   : parseVariable											*/
/* Purpose	   : Parse a config line for a variable						*/
/* Inputs	   : Ptrs to name and value									*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
Errno parseVariable(char *name, char *tail)
{
	VarStruct	*vp, *varp;

#ifdef DEBUG_VAR
	printf("parseVariable(): %s = %s\n", name, tail);
#endif

	if ((name == NULL) || (*name != '$') || (strlen(name) < 2))
		return(ERROR);

	lockList(&varList);

	if ((vp = findVariable(name)) != NULL)
	{
		list_get(&varList, (Node *)vp);
		free(vp);
	}

	if (*tail == '$')
	{
		if ((varp = findVariable(tail)) != NULL)
		{
			if ((vp = malloc(sizeof(VarStruct) + strlen(name) + strlen(varp->str) + 2)) == NULL)
				return(ERROR);
			vp->name = (char *)(vp + 1);
			strcpy(vp->name, name);
			vp->str = vp->name + strlen(name) + 1;
			strcpy(vp->str, varp->str);
			vp->lval = varp->lval;
			vp->fval = varp->fval;
			vp->flags = varp->flags;
		}
		else
		{
			unlockList(&varList);
			return(ERROR);
		}
	}
	else
	{
		if ((vp = malloc(sizeof(VarStruct) + strlen(name) + strlen(tail) + 2)) == NULL)
		{
			unlockList(&varList);
			return(ERROR);
		}

		vp->name = (char *)(vp + 1);
		strcpy(vp->name, name);
		vp->str = vp->name + strlen(name) + 1;
		strcpy(vp->str, tail);
		vp->flags = 0;

		if (checkForHex(tail, vp) != OK)
			if (checkForDec(tail, vp) != OK)
				checkForFloat(tail, vp);
	}

	list_add(&varList, (Node *)vp);
	unlockList(&varList);
	return(OK);

} /* parseVariable() */


/************************************************************************/
/* Function	   : showVars												*/
/* Purpose	   : Display Variables to screen							*/
/* Inputs	   : None													*/
/* Outputs	   : OK														*/
/************************************************************************/
Int16 showVars(Void)
{
	Reg Node	*np;
	Reg VarStruct *vp;

	xprintf("%d variables:\n", list_count(&varList));

	lockList(&varList);

	for (np = list_first(&varList); np != NULLNODE; np = list_next(np))
	{
		vp = (VarStruct *)np;

		xprintf("%10s ", vp->name);

		if ((vp->flags & VF_ISVAL) == 0)
			xprintf(" STRING %s\n", vp->str);
		else if (vp->flags & VF_ISFLT)
			xprintf(" FLOAT %8.4g\n", vp->fval);
		else
			xprintf(" LONG %8ld\n", vp->lval);
	}

	unlockList(&varList);
	return(OK);

} /* showVars() */


/************************************************************************/
/* Function	   : getStringVal											*/
/* Purpose	   : Get string using substitution if string represents a var*/
/* Inputs	   : String													*/
/* Outputs	   : String													*/
/************************************************************************/
char *getStringVal(char *s)
{
	VarStruct *vp;

	if ((*s == '$') && ((vp = findVariable(s)) != NULL))
		return(vp->str);

	return(s);

} /* getStringVal() */


/************************************************************************/
/* Function	   : getLongVal												*/
/* Purpose	   : Get Int32 value corresponding to a string				*/
/* Inputs	   : String, Int32 ptr to return							*/
/* Outputs	   : TRUE if conversion OK, else FALSE						*/
/* Comment	   : Essentially atoi() with variable substitution			*/
/************************************************************************/
MBool getLongVal(char *s, Int32 *val)
{
	VarStruct *vp, varstr;

	if ((*s == '$') && ((vp = findVariable(s)) != NULL))
	{
		*val = vp->lval;
		return((vp->flags & VF_ISVAL) != 0);
	}

	if ((checkForHex(s, &varstr) == OK) || (checkForDec(s, &varstr) == OK))
	{
		*val = varstr.lval;
		return(TRUE);
	}

	*val = 0;
	sscanf(s, " %ld", val);
	return(FALSE);

} /* getLongVal() */


/************************************************************************/
/* Function	   : getFltVal												*/
/* Purpose	   : Get Flt32 value corresponding to a string				*/
/* Inputs	   : String, Flt32 ptr to return							*/
/* Outputs	   : TRUE if conversion OK, else FALSE						*/
/* Comment	   : Essentially atof() with variable substitution			*/
/************************************************************************/
MBool getFltVal(char *s, Flt32 *val)
{
	VarStruct *vp, varstr;

	if ((*s == '$') && ((vp = findVariable(s)) != NULL))
	{
		*val = vp->fval;
		return((vp->flags & VF_ISVAL) != 0);
	}

	if (checkForFloat(s, &varstr) == OK)
	{
		*val = varstr.fval;
		return(TRUE);
	}

	*val = 0.0;
	sscanf(s, " %f", val);
	return(FALSE);

} /* getFltVal() */
