/*	PIRL Parameter Value Logic
*/
#ifndef _PPVL_
#define _PPVL_

/*
Author:

Bradford Castalia                       Castalia@Arizona.edu
Senior Systems Analyst                  (520) 621-6946
Planetary Image Research Laboratory     (520) 621-4824
Department of Planetary Sciences        FAX: (520) 621-9628
University of Arizona                   Space Sciences Bldg.
1629 E. University Blvd.                Room 429
Tucson, Arizona  85721-0092

"Build an image in your mind, fit yourself into it."
    The log of Cyradis seeress of Kell.


PIRL SCCS ID: @(#)PPVL.h	1.6 10/04/00
*/

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

Copyright (C) 2000  Bradford Castalia, University of Arizona

This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


Bradford Castalia                       Castalia@Arizona.edu
Senior Systems Analyst                  http://PIRL.LPL.Arizona.edu/~castalia
Planetary Image Research Laboratory     520-621-4824
Department of Planetary Sciences        FAX: 520-621-9628
University of Arizona                   Space Sciences Bldg.
1629 E. University Blvd.                Room 429
Tucson, Arizona  85721-0092

"Build an image in your mind, fit yourself into it."
    The Log of Cyradis, Seeress of Kell.

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

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

	Included header files:
*/
/*	Provides the FILE typedef.
*/
#include	<stdio.h>


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

	Data structures:
*/
/*	Any data object.
*/
typedef	void	PPVL_Object;


/*------------------------------------------------------------------------------
	Values
*/
typedef int		PPVL_Type;

typedef struct	PPVL_value
	{
	PPVL_Type	type;				/* data type */
	union
		{
		unsigned long		integer;
		double				real;
		char				*string;
		struct PPVL_value	**array;
		}
				data;				/* actual data value */
	int			base;				/* integer base */
	char		*units;				/* measurement units */
	}
	PPVL_Value;


/*	Value type codes:

	Note that parameter classification codes are expected to be distinct from
	value type codes.
*/
#define PPVL_LOWEST_TYPE_BIT		0

#define PPVL_TYPE_ANY				0
#define PPVL_TYPE_UNKNOWN			0

#define PPVL_TYPE_NUMERIC			(1 << PPVL_LOWEST_TYPE_BIT)
#define PPVL_TYPE_INTEGER			(PPVL_TYPE_NUMERIC | (PPVL_TYPE_NUMERIC << 1))
#define PPVL_TYPE_REAL				(PPVL_TYPE_NUMERIC | (PPVL_TYPE_NUMERIC << 2))

#define PPVL_TYPE_STRING			(1 << (PPVL_LOWEST_TYPE_BIT + 3))
#define PPVL_TYPE_IDENTIFIER		(PPVL_TYPE_STRING | (PPVL_TYPE_STRING << 1))
#define PPVL_TYPE_SYMBOL			(PPVL_TYPE_STRING | (PPVL_TYPE_STRING << 2))
#define PPVL_TYPE_TEXT				(PPVL_TYPE_STRING | (PPVL_TYPE_STRING << 3))
#define PPVL_TYPE_DATE_TIME			(PPVL_TYPE_SYMBOL | PPVL_TYPE_TEXT)

#define PPVL_TYPE_ARRAY				(1 << (PPVL_LOWEST_TYPE_BIT + 7))
#define PPVL_TYPE_SET				(PPVL_TYPE_ARRAY | (PPVL_TYPE_ARRAY << 1))
#define PPVL_TYPE_SEQUENCE			(PPVL_TYPE_ARRAY | (PPVL_TYPE_ARRAY << 2))

#define PPVL_TYPE_MASK				0x3FF
#define PPVL_HIGHEST_TYPE_BIT		(PPVL_LOWEST_TYPE_BIT + 9)

/*		Value type macros:
*/
#define PPVL_Is_Value(object)			(((PPVL_Value *)(object))->type & PPVL_TYPE_MASK)

#define PPVL_Type_Is(type, code)		(((type) & (code)) == (code))

#define PPVL_Type_Is_Numeric(type)		((type) & PPVL_TYPE_NUMERIC)
#define PPVL_Type_Is_Integer(type)		((type) == PPVL_TYPE_INTEGER)
#define PPVL_Type_Is_Real(type)			((type) == PPVL_TYPE_REAL)

#define PPVL_Type_Is_String(type)		((type) & PPVL_TYPE_STRING)
#define PPVL_Type_Is_Identifier(type)	((type) == PPVL_TYPE_IDENTIFIER)
#define PPVL_Type_Is_Symbol(type)		((type) == PPVL_TYPE_SYMBOL)
#define PPVL_Type_Is_Text(type)			((type) == PPVL_TYPE_TEXT)
#define PPVL_Type_Is_Date_Time(type)	((type) == PPVL_TYPE_DATE_TIME)

#define PPVL_Type_Is_Array(type)		((type) & PPVL_TYPE_ARRAY)
#define PPVL_Type_Is_Set(type)			((type) == PPVL_TYPE_SET)
#define PPVL_Type_Is_Sequence(type)		((type) == PPVL_TYPE_SEQUENCE)


/*------------------------------------------------------------------------------
	Parameters
*/
typedef int		PPVL_Class;

typedef struct	PPVL_parameter
	{
	/*	Note: The word "class" is a reserved word in C++ so it isn't
		used here to allow this module to be used in C++ applications.
	*/
	PPVL_Class	classification;		/* classification of parameter */
	char		*name;				/* parameter name (keyword) */
	union
		{
		/*	For all classes of parameters EXCEPT begin aggregate:
		*/
		struct PPVL_value		*value;

		/*	ONLY for begin aggregate class parameters:
		*/
		struct PPVL_parameter	**parameters;
		}
				content;			/* parameter content (value) */

	char		*comments;			/* leading comments */
	void		*user_data;			/* link to user-defined data */
	}
	PPVL_Parameter;


/*	Parameter classification codes:

	Note that parameter classification codes are expected to be distinct from
	value type codes.
*/
#define PPVL_LOWEST_CLASS_BIT		(PPVL_HIGHEST_TYPE_BIT + 1)

#define PPVL_CLASS_UNKNOWN			0

#define PPVL_END					(1 << PPVL_LOWEST_CLASS_BIT)

#define PPVL_CLASS_TOKEN			(1 << (PPVL_LOWEST_CLASS_BIT + 1))
#define PPVL_CLASS_END				(PPVL_CLASS_TOKEN | PPVL_END)

#define PPVL_CLASS_ASSIGNMENT		(1 << (PPVL_LOWEST_CLASS_BIT + 2))

#define PPVL_CLASS_AGGREGATE		(1 << (PPVL_LOWEST_CLASS_BIT + 3))
#define PPVL_CLASS_BEGIN_AGGREGATE	PPVL_CLASS_AGGREGATE
#define PPVL_CLASS_END_AGGREGATE	(PPVL_CLASS_AGGREGATE | PPVL_END)
#define PPVL_CLASS_GROUP			(PPVL_CLASS_AGGREGATE | (PPVL_CLASS_AGGREGATE << 1))
#define PPVL_CLASS_BEGIN_GROUP		PPVL_CLASS_GROUP
#define PPVL_CLASS_END_GROUP		(PPVL_CLASS_GROUP | PPVL_END)
#define PPVL_CLASS_OBJECT			(PPVL_CLASS_AGGREGATE | (PPVL_CLASS_AGGREGATE << 2))
#define PPVL_CLASS_BEGIN_OBJECT		PPVL_CLASS_OBJECT
#define PPVL_CLASS_END_OBJECT		(PPVL_CLASS_OBJECT | PPVL_END)

#define PPVL_HIGHEST_CLASS_BIT		(PPVL_LOWEST_CLASS_BIT + 5)
#define PPVL_CLASS_MASK				(0x3F << PPVL_LOWEST_CLASS_BIT)


/*		Parameter classification macros:
*/
#define PPVL_Is_Parameter(object)						(((PPVL_Parameter *)(object))->classification & PPVL_CLASS_MASK)

/*	>>> WARNING <<< This macro will not distinguish an end aggregate
	from a begin aggregate. Use the special macros below instead.
	Note that no end-class parameters should occur in a parameter list.
*/
#define PPVL_Class_Is(classification, code)				(((classification) & (code)) == (code))

#define PPVL_Class_Is_Token(classification)				((classification) & PPVL_CLASS_TOKEN)
#define	PPVL_Class_Is_End(classification)				((classification) ==  PPVL_CLASS_END)

#define	PPVL_Class_Is_Assignment(classification)		((classification) == PPVL_CLASS_ASSIGNMENT)

#define PPVL_Class_Is_Aggregate(classification)			((classification) & PPVL_CLASS_AGGREGATE)
#define PPVL_Class_Is_Begin_Aggregate(classification)	(((classification) & PPVL_CLASS_AGGREGATE) && ! ((classification) & PPVL_END))
#define PPVL_Class_Is_End_Aggregate(classification)		(((classification) & PPVL_CLASS_AGGREGATE) &&   ((classification) & PPVL_END))
#define PPVL_Class_Is_Object(classification)			((classification) == PPVL_CLASS_OBJECT)
#define PPVL_Class_Is_Begin_Object(classification)		((classification) == PPVL_CLASS_BEGIN_OBJECT)
#define PPVL_Class_Is_End_Object(classification)		((classification) == PPVL_CLASS_END_OBJECT)
#define PPVL_Class_Is_Group(classification)				((classification) == PPVL_CLASS_GROUP)
#define PPVL_Class_Is_Begin_Group(classification)		((classification) == PPVL_CLASS_BEGIN_GROUP)
#define PPVL_Class_Is_End_Group(classification)			((classification) == PPVL_CLASS_END_GROUP)


/*------------------------------------------------------------------------------
	Parameter symbols:
*/
/*  The name of the aggregate parameter that contains all of the
	parameters read from a file or string (PPVL_{read,scan}_aggregate).
*/
#define PPVL_CONTAINER_NAME						"The Container"

/*	No file read limit.
*/
#define PPVL_NO_READ_LIMIT						-1

/*	Parameter selection to find.
*/
typedef enum	PPVL_select
	{
	PPVL_SELECT_ANY,
	PPVL_SELECT_NAME,
	PPVL_SELECT_CLASS,
	PPVL_SELECT_PARAMETER,
	PPVL_SELECT_USER_DATA
	}
	PPVL_Select;

/*	Search from the beginning of the list
	(no Last_{Parameter,Value} to find first).
*/
#define PPVL_SEARCH_FROM_THE_TOP				NULL


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

	Global variables
*/
/*------------------------------------------------------------------------------
	Tunable values:
*/
/*  The format to use for output of real values.

	>>> CAUTION <<<
	The precision (number of places after the decimal point)
	is very dependent on the host system's ability. The default
	format string is "%#G". Another possibility might be "%#.15E".
*/
extern char		*PPVL_real_number_format;


/*  Verbatim quoted strings.

    Normally multi-line quoted strings in PVL statements have white
    space surrounding the line breaks compressed to a single space
    character (except when the last non-white space character on the
    line is a dash ("-"), in which case no space is included). This
    is because output formatting is expected to be controlled by
    embedded format characters which are processed on output:

        \n - line break.
        \t - horizontal tab.
        \f - form feed (page break).
        \\ - backslash character.
        \v - verbatim (no formatting) till next \v.

    If quoted strings in PVL statements are not to be compressed,
    set the following variable to TRUE.
*/
extern int		PPVL_verbatim_strings;


/*	Parameter pathname delimiter.

	Parameters that must be located within a specific aggregate should 
	have the name of the parent aggregate prefixed to the parameter 
	name and separated by the PPVL_PARAMETER_PATH_DELIMITER (a forward 
	slash - "/" - by default). As many parent aggregate names as 
	necessary can be prefixed in this way. If a parameter must be at 
	the top level (not in any nested aggregate), just prefix the 
	delimiter with no parent name. For parameters that may occur at 
	any level, no prefix should be used. This syntax is similar to 
	that used in Unix filesystem pathnames, with absoulte pathnames 
	starting with a "/", and relative pathnames not (a relative 
	parameter pathname is a "find anywhere" name).

	The delimiter is defined as a string, rather than a single
	character, so it can be used in functions that require a string.
*/
extern char		*PPVL_parameter_path_delimiter;


/*------------------------------------------------------------------------------
	Error conditions:

	Every PPVL function sets PPVL_errno. Initially it is set to 
	PPVL_ERROR_NO_ERROR on entering the function. If an error 
	condition is detected, it is set to an appropriate value before 
	the function cleans up and returns an error status value. The 
	condition, however, may be considered to be only a warning (this 
	may depend on PPVL_strict being FALSE) in which case PPVL_errno is 
	set accordingly but no error return is taken. Note that only the 
	first warning condition in a function is registered. If an error
	condition occurs after a warning condition has been encountered
	the error condition takes precedence over the warning.
*/
extern int		PPVL_strict;

extern int		PPVL_errno;

typedef enum	PPVL_error_code
	{
	PPVL_ERROR_NO_ERROR,

	/*	Hard errors:
	*/
	PPVL_ERROR_NO_MEMORY,
	PPVL_ERROR_BAD_ARGUMENT,
	PPVL_ERROR_READING_FILE,

	/*	Syntax errors:
	*/
	PPVL_ERROR_MISSING_QUOTE_END,
	PPVL_ERROR_MISSING_COMMENT_END,
	PPVL_ERROR_MISSING_UNITS_END,
	PPVL_ERROR_RESERVED_CHARACTER,
	PPVL_ERROR_ILLEGAL_SYNTAX,
	PPVL_ERROR_EMPTY_STATEMENT,
	PPVL_ERROR_VALUE_OVERFLOW,
	PPVL_ERROR_ARRAY_CLOSURE_MISMATCH,

	/*	Abstract (high level) usage problems:
	*/
	PPVL_ERROR_GROUP_VALUE,
	PPVL_ERROR_MISSING_AGGREGATE_END,
	PPVL_ERROR_AGGREGATE_CLOSURE_MISMATCH,
	PPVL_ERROR_SIZED_RECORDS,

	PPVL_ILLEGAL_ERROR_CODE
	}
	PPVL_Error_Code;

#define PPVL_SUCCESS		PPVL_ERROR_NO_ERROR
#define PPVL_FATAL_ERROR	PPVL_ERROR_READING_FILE
#define PPVL_SYNTAX_ERROR	PPVL_ERROR_ARRAY_CLOSURE_MISMATCH


/*	Provides a brief error message string
	using the current value of PPVL_errno.
*/
#define PPVL_ERROR_MESSAGE	\
	 PPVL_errmsg \
		[(PPVL_errno < 0 || \
		  PPVL_errno >= PPVL_ILLEGAL_ERROR_CODE) ? \
			PPVL_ILLEGAL_ERROR_CODE : PPVL_errno]

extern char	*PPVL_errmsg[];


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

	Function prototypes:
*/
PPVL_Parameter *
PPVL_read_aggregate
	(
	FILE			*File,
	unsigned long	Read_Limit,
	unsigned long	*Total_Scanned
	);

PPVL_Parameter *
PPVL_scan_aggregate
	(
	char			*The_String,
	char			**Next_Character
	);

PPVL_Parameter *
PPVL_scan_parameter
	(
	char			*The_String,
	char			**Next_Character
	);

char *
PPVL_scan_comments
	(
	char			*The_String,
	char			**Next_Character
	);

PPVL_Value *
PPVL_scan_value
	(
	char			*The_String,
	char			**Next_Character
	);

PPVL_Error_Code
PPVL_scan_datum
	(
	PPVL_Value		*The_Value,
	char			*The_String,
	char			**Next_Character
	);

char *
PPVL_scan_quoted_string
	(
	char			*The_String,
	char			**Next_Character
	);

char *
PPVL_scan_units
	(
	char			*The_String,
	char			**Next_Character
	);

char *
PPVL_skip_white_space_and_comments
	(
	char			*The_String
	);

char *
PPVL_comb_symbol
	(
	char			*The_String
	);

#define PPVL_new(Type, Content, Name, Comments, User_Data) \
	( \
	((Type) & PPVL_CLASS_MASK) ? \
		PPVL_new_parameter (Name, Type, Content, Comments, User_Data) : \
		PPVL_new_value (Type, Content, Name, Comments) \
	)

PPVL_Parameter *
PPVL_new_parameter
	(
	char			*name,
	PPVL_Class		classification,
	PPVL_Object		*content,
	char			*comments,
	void			*user_data
	);

PPVL_Value *
PPVL_new_value
	(
	PPVL_Type		type,
	PPVL_Object		*data,
	int				base,
	char			*units
	);

#define PPVL_add(The_Container, The_Object)	\
	( \
	PPVL_Is_Parameter (The_Container) ? \
		PPVL_add_parameter (The_Container, The_Object) : \
		PPVL_add_value (The_Container, The_Object) \
	)

PPVL_Parameter *
PPVL_add_parameter
	(
	PPVL_Parameter	*The_Aggregate,
	PPVL_Parameter	*The_Parameter
	);

PPVL_Value *
PPVL_add_value
	(
	PPVL_Value		*The_Array,
	PPVL_Value		*The_Value
	);

#define PPVL_duplicate(The_Object)	\
	( \
	PPVL_Is_Parameter (The_Object) ? \
		PPVL_duplicate_parameter (The_Object) : \
		PPVL_duplicate_value (The_Object) \
	)

PPVL_Parameter *
PPVL_duplicate_parameter
	(
	PPVL_Parameter	*The_Parameter
	);

PPVL_Value *
PPVL_duplicate_value
	(
	PPVL_Value		*The_Value
	);

#define PPVL_remove(The_Container, The_Object)	\
	( \
	PPVL_Is_Parameter (The_Container) ? \
		PPVL_remove_parameter (The_Container, The_Object) : \
		PPVL_remove_value (The_Container, The_Object) \
	)

PPVL_Parameter *
PPVL_remove_parameter
	(
	PPVL_Parameter	*The_Aggregate,
	PPVL_Parameter	*The_Parameter
	);

PPVL_Value *
PPVL_remove_value
	(
	PPVL_Value		*The_Array,
	PPVL_Value		*The_Value
	);

#define PPVL_free(The_Object)	\
	( \
	PPVL_Is_Parameter (The_Object) ? \
		PPVL_free_parameter (The_Object) : \
		PPVL_free_value (The_Object) \
	)

void
PPVL_free_parameter
	(
	PPVL_Parameter	*The_Parameter
	);

void
PPVL_free_value
	(
	PPVL_Value		*The_Value
	);

#define PPVL_find(The_Container, Last_Object, Select, The_Selection, The_Parent)	\
	( \
	PPVL_Is_Parameter (The_Container) ? \
		PPVL_find_parameter (The_Container, Last_Object, Select, The_Selection, The_Parent) : \
		PPVL_find_value (The_Container, Last_Object, Select, The_Selection, The_Parent) \
	)

PPVL_Parameter *
PPVL_find_parameter
	(
	PPVL_Parameter	*The_Aggregate,
	PPVL_Parameter	*Last_Parameter,
	PPVL_Select		Select,
	PPVL_Object		*The_Selection,
	PPVL_Parameter	**The_Parent
	);

PPVL_Value *
PPVL_find_value
	(
	PPVL_Value		*The_Array,
	PPVL_Value		*Last_Value,
	PPVL_Type		Type,
	PPVL_Object		*Data,
	PPVL_Value		**The_Parent
	);

#define PPVL_count_all(The_Container)	\
	( \
	PPVL_Is_Parameter (The_Container) ? \
		PPVL_count_all_parameters (The_Container) : \
		PPVL_count_all_values (The_Container) \
	)

int
PPVL_count_all_parameters
	(
	PPVL_Parameter	*The_Aggregate
	);

int
PPVL_count_all_values
	(
	PPVL_Value		*The_Array
	);

#define PPVL_count(The_Container)	\
	( \
	PPVL_Is_Parameter (The_Container) ? \
		PPVL_count_parameter (The_Container) : \
		PPVL_count_value (The_Container) \
	)

int
PPVL_count_parameters
	(
	PPVL_Parameter	*The_Aggregate
	);

int
PPVL_count_values
	(
	PPVL_Value		*The_Array
	);

#define PPVL_index(The_Container, The_Object)	\
	( \
	PPVL_Is_Parameter (The_Container) ? \
		PPVL_index_parameter (The_Container, The_Object) : \
		PPVL_index_value (The_Container, The_Object) \
	)

int
PPVL_index_parameter
	(
	PPVL_Parameter	*The_Aggregate,
	PPVL_Parameter	*The_Parameter
	);

int
PPVL_index_value
	(
	PPVL_Value		*The_Array,
	PPVL_Value		*The_Value
	);

PPVL_Error_Code
PPVL_write_parameter
	(
	PPVL_Parameter	*The_Parameter,
	FILE			*The_File,
	int				Indent_Level
	);

PPVL_Error_Code
PPVL_write_value
	(
	PPVL_Value		*The_Value,
	FILE			*The_File
	);

#endif

