/** @file  psMetadata.h
*
*  @brief Contains metadata struuctures, enumerations and functions prototypes
*
*  This file defines metadata item, metadata type, metadata flags, metadata containers, and function
*  prototypes necessary creating psLib metadata APIs
*
*  @ingroup Metadata
*
*  @author Robert DeSonia, MHPCC
*  @author Ross Harman, MHPCC
*
*  @version $Revision: 1.68 $ $Name: not supported by cvs2svn $
*  @date $Date: 2005-09-30 01:34:45 $
*
*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
*/
#ifndef PS_METADATA_H
#define PS_METADATA_H

#include <stdarg.h>
#include <stdio.h>
#include <sys/types.h>
#include <regex.h>

#include "psHash.h"
#include "psList.h"
#include "psTime.h"
#include "psLookupTable.h"

/// @addtogroup Metadata
/// @{

#define PS_DATA_IS_PRIMITIVE(TYPE) \
(TYPE == PS_DATA_S32 || \
 TYPE == PS_DATA_F32 || \
 TYPE == PS_DATA_F64 || \
 TYPE == PS_DATA_BOOL)

#define PS_DATA_PRIMITIVE_TYPE(DATATYPE) ( \
        (DATATYPE==PS_DATA_S32 || DATATYPE==PS_DATA_F32 || \
         DATATYPE==PS_DATA_F64 || DATATYPE==PS_DATA_BOOL) ? DATATYPE : 0)


/** Option flags for psMetadata functions
 *
 *  Enumeration for the modification of the behaviour in psMetadataAddItem.
 *
 *  @see psMetadataAddItem
 */
typedef enum {
    PS_META_DEFAULT = 0,               ///< default behaviour (duplicate entry is an error)
    PS_META_REPLACE = 0x1000000,       ///< allow entry to be replaced
    PS_META_DUPLICATE_OK = 0x2000000,  ///< allow duplicate entries
    PS_META_NULL = 0x4000000           ///< psMetadataItem.data is a NULL value
} psMetadataFlags;

#define PS_METADATA_FLAGS_MASK 0xFF000000
#define PS_METADATA_TYPE_MASK 0x00FFFFFF

/** Metadata data structure.
 *
 *  Struct for holding metadata items. Metadata items are held in two
 *  containers. The first employs a doubly-linked list to preserve the order
 *  of the metadata. The second container employs a hash table which
 *  allows fast lookup when given a metadata keyword.
 */
typedef struct
{
    psList*  list;                     ///< Metadata in linked-list
    psHash*  hash;                     ///< Metadata in a hash table
    void *lock;                        ///< Optional lock for thread safety
}
psMetadata;

/** Metadata iterator
 *
 *  Iterator for metadata.
 */
typedef struct
{
    psListIterator* iter;              ///< iterator for the psMetadata's psList
    regex_t* regex;                    ///< the subsetting regular expression
}
psMetadataIterator;

/** Metadata item data structure.
 *
 * Struct for maintaining metadata items of varying types. It also contains
 * information about the item name, flags, comments, and other items with the same name.
 */
typedef struct
{
    const psS32 id;                    ///< Unique ID for metadata item.
    psString name;                     ///< Name of metadata item.
    psDataType type;                   ///< Type of metadata item.
    union {
        psBool B;                      ///< boolean data
        psS8 S8;                       ///< Signed 8-bit integer data.
        psS16 S16;                     ///< Signed 16-bit integer data.
        psS32 S32;                     ///< Signed 32-bit integer data.
        psU8 U8;                       ///< Unsigned 8-bit integer data.
        psU16 U16;                     ///< Unsigned 16-bit integer data.
        psU32 U32;                     ///< Unsigned 32-bit integer data.
        psF32 F32;                     ///< Single-precision float data.
        psF64 F64;                     ///< Double-precision float data.
        psList *list;                  ///< List data.
        psMetadata *md;                ///< Metadata data.
        psPtr V;                       ///< Pointer to other type of data.
    } data;                            ///< Union for data types.
    psString comment;                  ///< Optional comment ("", not NULL).
}
psMetadataItem;

/** Create a metadata item.
 *
 *  Returns a fill psMetadataItem ready for insertion into the psMetadata
 *  struct. The name argument specifies the name to use for this item, and
 *  may include sprintf formatting codes. The format entry specifies both
 *  the metadata type and optional flags and is created by bit-wise or of the
 *  appropriate type and flag. The comment argument is a fixed string used to
 *  comment the metadata item. The arguments to the name formatting codes and
 *  the metadata itself are passed as arguments following the comment string.
 *  The data must be a pointer for any of the elements stored in data.void.
 *  The argument list must be interpreted appropriately by the va_list
 *  operators in the function specified size and type.
 *
 * @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataItemAlloc(
    const char *name,                  ///< Name of metadata item.
    psDataType type,                   ///< Type of metadata item.
    const char *comment,               ///< Comment for metadata item.
    ...                                ///< Arguments for name formatting and metadata item data.
)
;

/** Checks the type of a particular pointer.
 *
 *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
 *
 *  @return bool:       True if the pointer matches a psMetadataItem structure, false otherwise.
 */
bool psMemCheckMetadataItem(
    psPtr ptr                          ///< the pointer whose type to check
);


/** Create a metadata item with specified string data.
 *
 *  Returns a fill psMetadataItem ready for insertion into the psMetadata
 *  struct.
 *
 * @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataItemAllocStr(
    const char* name,                  ///< Name of metadata item.
    const char* comment,               ///< Comment for metadata item.
    const char* value                  ///< the value of the metadata item.
);

/** Create a metadata item with specified psF32 data.
 *
 *  Returns a fill psMetadataItem ready for insertion into the psMetadata
 *  struct.
 *
 * @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataItemAllocF32(
    const char* name,                  ///< Name of metadata item.
    const char* comment,               ///< Comment for metadata item.
    psF32 value                        ///< the value of the metadata item.
);

/** Create a metadata item with specified psF64 data.
 *
 *  Returns a fill psMetadataItem ready for insertion into the psMetadata
 *  struct.
 *
 * @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataItemAllocF64(
    const char* name,                  ///< Name of metadata item.
    const char* comment,               ///< Comment for metadata item.
    psF64 value                        ///< the value of the metadata item.
);

/** Create a metadata item with specified psS32 data.
 *
 *  Returns a fill psMetadataItem ready for insertion into the psMetadata
 *  struct.
 *
 * @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataItemAllocS32(
    const char* name,                  ///< Name of metadata item.
    const char* comment,               ///< Comment for metadata item.
    psS32 value                        ///< the value of the metadata item.
);

/** Create a metadata item with specified psBool data.
 *
 *  Returns a fill psMetadataItem ready for insertion into the psMetadata
 *  struct.
 *
 * @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataItemAllocBool(
    const char* name,                  ///< Name of metadata item.
    const char* comment,               ///< Comment for metadata item.
    bool value                         ///< the value of the metadata item.
);

/** Create a metadata item with specified psPtr data.
 *
 *  Returns a fill psMetadataItem ready for insertion into the psMetadata
 *  struct.
 *
 * @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataItemAllocPtr(
    const char* name,                  ///< Name of metadata item.
    psDataType type,                   ///< Data type of metadata item.
    const char* comment,               ///< Comment for metadata item.
    psPtr value                        ///< the value of the metadata item.
);

#ifndef SWIG
/** Create a metadata item with va_list.
 *
 *  Returns a fill psMetadataItem ready for insertion into the psMetadata
 *  struct. The name argument specifies the name to use for this item, and
 *  may include sprintf formatting codes. The format entry specifies both
 *  the metadata type and optional flags and is created by bit-wise or of the
 *  appropriate type and flag. The comment argument is a fixed string used to
 *  comment the metadata item. The arguments to the name formatting codes and
 *  the metadata itself are passed as arguments following the comment string.
 *  The data must be a pointer for any of the elements stored in data.void.
 *  The argument list must be interpreted appropriately by the va_list
 *  operators in the function specified size and type.
 *
 * @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataItemAllocV(
    const char *name,                  ///< Name of metadata item.
    psDataType type,                   ///< Type of metadata item.
    const char *comment,               ///< Comment for metadata item.
    va_list list                       ///< Arguments for name formatting and metadata item data.
);
#endif // #ifndef SWIG

/** Create a metadata collection.
 *
 *  Returns an empty metadata container with fully allocated internal metadata
 *  containers.
 *
 *  @return psMetadata* : Pointer metadata.
 */
psMetadata* psMetadataAlloc(void);


/** Checks the type of a particular pointer.
 *
 *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
 *
 *  @return bool:       True if the pointer matches a psMetadata structure, false otherwise.
 */
bool psMemCheckMetadata(
    psPtr ptr                          ///< the pointer whose type to check
);


/** Add existing metadata item to metadata collection.
 *
 *  Add a metadata item that has already been created to the metadata
 *  collection.
 *
 *  @return bool: True for success, false for failure.
 */
bool psMetadataAddItem(
    psMetadata*  md,                   ///< Metadata collection to insert metadat item.
    const psMetadataItem* item,        ///< Metadata item to be added.
    int location,                      ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    psS32 flags                        ///< Options flag mask, see psMetadataFlags enum
);

/** Create and add a metadata item to metadata collection.
 *
 * Creates a new metadata item add to the metadata collection.
 *
 * @return bool: True for success, false for failure.
 */
bool psMetadataAdd(
    psMetadata* md,                    ///< Metadata collection to insert metadata item.
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char *name,                  ///< Name of metadata item.
    int format,                        ///< Type of metadata item (psMetadataType) and options (psMetadataFlags)
    const char *comment,               ///< Comment for metadata item.
    ...                                ///< Arguments for name formatting and metadata item data.
);

#ifndef SWIG
/** Create and add a metadata item to metadata collection.
 *
 * Creates a new metadata item add to the metadata collection.
 *
 * @return bool: True for success, false for failure.
 */
bool psMetadataAddV(
    psMetadata* md,                    ///< Metadata collection to insert metadata item.
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char *name,                  ///< Name of metadata item.
    int format,                        ///< Type of metadata item (psMetadataType) and options (psMetadataFlags)
    const char *comment,               ///< Comment for metadata item.
    va_list list                       ///< Arguments for name formatting and metadata item data.
);
#endif // #ifndef SWIG

/** Add a psBool value to metadata collection.
 *
 *  @return bool:  True for success, False for failure.
 */
bool psMetadataAddBool(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    bool value                       ///< Value for metadata item data
);

/** Add a psS32 value to metadata collection.
 *
 *  @return bool:  True for success, False for failure.
 */
bool psMetadataAddS32(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psS32 value                        ///< Value for metadata item data
);

/** Add a psF32 value to metadata collection.
 *
 *  @return bool:  True for success, False for failure.
*/
bool psMetadataAddF32(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                    ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psF32 value                        ///< Value for metadata item data
);

/** Add a psF64 value to metadata collection.
 *
 *  @return bool:  True for success, False for failure.
*/
bool psMetadataAddF64(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psF64 value                        ///< Value for metadata item data
);

/** Add a psList to metadata collection.
 *
 *  @return psBool:  True for success, False for failure.
 */
psBool psMetadataAddList(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psList* value                      ///< psList for metadata item data
);

/** Add a string to metadata collection.
 *
 *  @return bool:  True for success, False for failure.
 */
bool psMetadataAddStr(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    const char* value                  ///< String for metadata item data
);

/** Add a vector to metadata collection.
 *
 *  @return psBool:  True for success, False for failure.
 */
psBool psMetadataAddVector(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psVector* value                    ///< Vector for metadata item data
);

/** Add a array to metadata collection.
 *
 *  @return psBool:  True for success, False for failure.
 */
psBool psMetadataAddArray(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psArray* value                     ///< Vector for metadata item data
);

/** Add an Image to metadata collection.
 *
 *  @return psBool:  True for success, False for failure.
 */
psBool psMetadataAddImage(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psImage* value                     ///< Image for metadata item data
);

/** Add a Time to metadata collection.
 *
 *  @return psBool:  True for success, False for failure.
 */
psBool psMetadataAddTime(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psTime* value                      ///< Time for metadata item data
);

/** Add a Hash to metadata collection.
 *
 *  @return psBool:  True for success, False for failure.
 */
psBool psMetadataAddHash(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psHash* value                      ///< Hash for metadata item data
);

/** Add a LookupTable to metadata collection.
 *
 *  @return psBool:  True for success, False for failure.
 */
psBool psMetadataAddLookupTable(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psLookupTable* value               ///< LookupTable for metadata item data
);

/** Add an Unknown (psPtr) to metadata collection.
 *
 *  @return psBool:  True for success, False for failure.
 */
psBool psMetadataAddUnknown(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psPtr value                        ///< Unknown for metadata item data
);

/** Add a psPtr to metadata collection.
 *
 *  @return bool:  True for success, False for failure.
 */
psBool psMetadataAddPtr(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    psDataType type,                   ///< psDataType for metadata item
    const char* comment,               ///< Comment for metadata item
    psPtr value                        ///< Unknown for metadata item data
);

/** Add Metadata to metadata collection.
 *
 *  @return psBool:  True for success, False for failure.
 */
psBool psMetadataAddMetadata(
    psMetadata* md,                    ///< Metadata collection to insert metadata item
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* name,                  ///< Name of metadata item
    int format,                        ///< psMetadataFlag options/flags
    const char* comment,               ///< Comment for metadata item
    psMetadata* value                  ///< Metadata for metadata item data
);

/** Remove an item from metadata collection.
 *
 *  Items may be removed from metadata by specifing a key or location. If the
 *  name is null, the where argument is used instead. If name is not null,
 *  where is set to PS_LIST_UNKNOWN. If the item is found, it is removed from
 *  the metadata and true is returned.  If the key is not unique, then all
 *  items corresponding to it are removed.
 *
 * @return bool: True for success, false for failure.
 */
bool psMetadataRemove(
    psMetadata*  md,                   ///< Metadata collection to remove metadata item.
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char * key                   ///< Name of metadata key.
);

/** Removes an item from metadata by key name.
 *
 *  @return bool:  True for success, false for failure.
 */
bool psMetadataRemoveKey(
    psMetadata *md,                    ///< Metadata collection to remove metadata item.
    const char *key                    ///< Name of metadata key.
);

/** Removes an item from metadata by index number.
 *
 *  @return bool:  True for success, false for failure.
 */
bool psMetadataRemoveIndex(
    psMetadata *md,                    ///< Metadata collection to remove metadata item.
    int location                       ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
);


/** Find an item in the metadata collection based on key name.
 *
 *  Items may be found in the metadata by providing a key. If the key is
 *  non-unique, the first item is returned. If the item is not found, null is
 *  returned.
 *
 * @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataLookup(
    const psMetadata * md,             ///< Metadata collection to lookup metadata item.
    const char * key                   ///< Name of metadata key.
);

/** Find an item in the metadata collection based on key name and return its double precision value.
 *
 *  Items may be found in the metadata by providing a key. If the key is
 *  non-unique, the value of the first item is returned. If the item is not found, zero is
 *  returned.
 *
 * @return psF64 : Value of metadata item.
 */
psF64 psMetadataLookupF64(
    bool *status,                      ///< Status of lookup.
    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
    const char *key                    ///< Name of metadata key.
);

/** Find an item in the metadata collection based on key name and return its single precision value.
 *
 *  Items may be found in the metadata by providing a key. If the key is
 *  non-unique, the value of the first item is returned. If the item is not found, zero is
 *  returned.
 *
 * @return psF32 : Value of metadata item.
 */
psF32 psMetadataLookupF32(
    bool *status,                      ///< Status of lookup.
    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
    const char *key                    ///< Name of metadata key.
);

/** Find an item in the metadata collection based on key name and return its integer value.
 *
 *  Items may be found in the metadata by providing a key. If the key is
 *  non-unique, the value of the first item is returned. If the item is not found, zero is
 *  returned.
 *
 * @return psS32 : Value of metadata item.
 */
psS32 psMetadataLookupS32(
    bool *status,                      ///< Status of lookup.
    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
    const char *key                    ///< Name of metadata key.
);

/** Find an item in the metadata collection based on key name and return its boolean value.
 *
 *  Items may be found in the metadata by providing a key. If the key is
 *  non-unique, the value of the first item is returned. If the item is not found, zero is
 *  returned.
 *
 * @return bool : Value of metadata item.
 */
bool psMetadataLookupBool(
    bool *status,                      ///< Status of lookup.
    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
    const char *key                    ///< Name of metadata key.
);

/** Find an item in the metadata collection based on key name and return its integer value.
 *
 *  Items may be found in the metadata by providing a key. If the key is
 *  non-unique, the value of the first item is returned. If the item is not found, zero is
 *  returned.
 *
 * @return void* : Value of metadata item.
 */
psPtr psMetadataLookupPtr(
    bool *status,                      ///< Status of lookup.
    const psMetadata* md,              ///< Metadata collection to lookup metadata item.
    const char *key                    ///< Name of metadata key.
);

/** Find an item in the metadata collection based on list index.
 *
 *  Items may be found in the metadata by their entry position in the list
 *  container.
 *
 *  @return psMetadataItem* : Pointer metadata item.
 */
psMetadataItem* psMetadataGet(
    const psMetadata* md,              ///< Metadata collection to retrieve metadata item.
    int location                       ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
);

/** Creates a psMetadataIterator to iterate over the specified psMetadata.
 *
 *  Supports the subsetting of the metadata via keyword using regular
 *  expression.  If no regular expression is specified, iteration
 *  over the entire psMetadata is performed.
 *
 *  @return psMetadataIterator*        a new psMetadataIterator, of NULL if error occurred
 */
psMetadataIterator* psMetadataIteratorAlloc(
    psMetadata* md,                    ///< the psMetadata to iterate with
    long location,                     ///< Index number, PS_LIST_HEAD, or PS_LIST_TAIL
    const char* regex
    ///< A regular expression for subsetting the psMetadata.  If NULL, no
    ///< subsetting is performed.
);

/** Set the iterator of the psMetadat to a given position.  If location is
 *  invalid the iterator position is not changed.
 *
 *  @return bool        TRUE if iterator successfully set, otherwise FALSE.
*/
bool psMetadataIteratorSet(
    psMetadataIterator* iterator,      ///< psMetadata iterator
    long location                      ///< index number, PS_LIST_HEAD, or PS_LIST_TAIL
);

/** Position the specified iterator to the next matching item in psMetadata,
 *  given the regular expression of the iterator
 *
 *  @return psPtr       the psMetadataItem at the original iterator position
 *                      or NULL if the iterator went past the end of the list.
 */
psMetadataItem* psMetadataGetAndIncrement(
    psMetadataIterator* iterator       ///< iterator to move
);

/** Position the specified iterator to the previous matching item in psMetadata,
 *  given the regular expression of the iterator
 *
 *  @return psPtr       the psMetadataItem at the original iterator position
 *                      or NULL if the iterator went past the beginning of the
 *                      list.
 */
psMetadataItem* psMetadataGetAndDecrement(
    psMetadataIterator* iterator       ///< iterator to move
);

/** Find an item in the metadata collection based on key name and return its metadata value.
 *
 *  Items may be found in the metadata by providing a key. If the key is
 *  non-unique, the value of the first item is returned. If the item is not found, zero is
 *  returned.
 *
 *  @return psMetadata*:        Value of metadata item.
 */
psMetadata *psMetadataLookupMD(
    bool *status,                      ///< Status of lookup.
    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
    const char *key                    ///< Name of metadata key.
);

/** Find an item in the metadata collection based on key name and return its string value.
 *
 *  Items may be found in the metadata by providing a key. If the key is
 *  non-unique, the value of the first item is returned. If the item is not found, zero is
 *  returned.
 *
 *  @return char*:           Value of metadata item.
 */
char *psMetadataLookupStr(
    bool *status,                      ///< Status of lookup.
    const psMetadata *md,              ///< Metadata collection to lookup metadata item.
    const char *key                    ///< Name of metadata key.
);

/** Print metadata collection to screen      */
void psMetadataPrint(
    psMetadata *md,                    ///< Metadata collection to print.
    int level                          ///< the level of metadata items.
);

/** Implements the various verbosity controls.
 *
 *  Arguments shall be removed from the argument list as they are processed.
 *
 *  @return int:        The resultant logging level.
 */
int psArgumentVerbosity(
    int *argc,                         ///< number or arguments
    char **argv                        ///< the argument list
);

/** Checks for an argument and returns its index position if found.
 *
 *  @return int:        The index of the element in the argument list, otherwise 0.
 */
int psArgumentGet(
    int argc,                          ///< number or arguments
    char **argv,                       ///< the argument list
    const char *arg                    ///< the specified argument to match
);

/** Removes from the argument list the argument whose index is argnum.
 *
 *  The number of entries in the argument list shall be decremented.
 *
 *  @return bool:       True if the argnum is in the argument list, otherwise false.
 */
bool psArgumentRemove(
    int argnum,                        ///< the argument to remove
    int *argc,                         ///< number or arguments
    char **argv                        ///< the argument list
);

/** Parses the command line arguments into a metadata container of arguments.
 *
 *  The input arguments shall contain the list of possible arguments as the keywords providing
 *  the default values.  As matching arguments are found on the command line, the values shall be
 *  read into the arguments metadata, with the appropriate type.  The arguments and their values
 *  shall be removed from the list of command line arguments as they are processed.
 *
 *  @return bool:       False if any argument was encountered that is not present in arguments.
 */
bool psArgumentParse(
    psMetadata *arguments,             ///< metadata container for arguments
    int *argc,                         ///< number or arguments
    char **argv                        ///< the argument list
);

/** Prints to stdout a guide to the command-line arguments.      */
void psArgumentHelp(
    psMetadata *arguments              ///< metadata container for arguments
);

/// @}

#endif // #ifndef PS_METADATA_H
