/* $Id: uves_propertylist.c,v 1.20 2013-02-19 13:24:28 amodigli Exp $
 *
 * This file is part of the ESO Common Pipeline Library
 * Copyright (C) 2001-2005 European Southern Observatory
 *
 * 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
 */

/*
 * $Author: amodigli $
 * $Date: 2013-02-19 13:24:28 $
 * $Revision: 1.20 $
 * $Name: not supported by cvs2svn $
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <uves_msg.h>

#include <uves_propertylist.h>
#include <uves_utils_wrappers.h>
#include <uves_error.h>

#ifdef USE_CPL
#else

#include <uves_utils_wrappers.h>
#include <uves_deque.h>
#include <uves_dump.h>
#include <cpl.h>

//#include <cxmacros.h>
#include <cxmemory.h>
#include <cxmessages.h>
#include <cxstrutils.h>
#include <cxutils.h>
//#include "cpl_error_impl.h"
//#include "cpl_propertylist_impl.h"
#include <qfits.h>

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <regex.h>
#include <assert.h>


#include <cxdeque.h>

/**
 * @defgroup uves_propertylist Property Lists
 *
 * This module is the same as CPL-3.1 property lists but implemented
 * using a deque (a vector that can grow in both ends in amortized constant time)
 * rather than a linked list.
 *
 * This was a necessary change in order for property lists to be usable with MIDAS
 * generated FLAMES calibration data
 * (a simple loop is now O(n), not O(n^2), for FLAMES data we have n ~= 10^5)
 *
 * This module still uses the cpl_property module
 */

/**@{*/

enum {
    FITS_STDKEY_MAX = 8,
    FITS_SVALUE_MAX = 68
};


/*
 * The property list type.
 */

struct _uves_propertylist_ {
    uves_deque *properties;
};


/*
 * Regular expresion filter type
 */

struct _uves_regexp_ {
    regex_t re;
    cxbool invert;
};

typedef struct _uves_regexp_ uves_regexp;


static void
propertylist_append_property(uves_propertylist *plist, const cpl_property *p)
{
    switch(cpl_property_get_type(p)) {
    case CPL_TYPE_CHAR:
        uves_propertylist_append_char(plist, cpl_property_get_name(p), cpl_property_get_char(p));
        break;
    case CPL_TYPE_BOOL:
        uves_propertylist_append_bool(plist, cpl_property_get_name(p), cpl_property_get_bool(p));
        break;
    case CPL_TYPE_INT:
        uves_propertylist_append_int(plist, cpl_property_get_name(p), cpl_property_get_int(p));
        break;
    case CPL_TYPE_LONG:
        uves_propertylist_append_long(plist, cpl_property_get_name(p), cpl_property_get_long(p));
        break;
    case CPL_TYPE_FLOAT:
        uves_propertylist_append_float(plist, cpl_property_get_name(p), cpl_property_get_float(p));
        break;
    case CPL_TYPE_DOUBLE:
        uves_propertylist_append_double(plist, cpl_property_get_name(p), cpl_property_get_double(p));
        break;
    case CPL_TYPE_STRING:
        uves_propertylist_append_string(plist, cpl_property_get_name(p), cpl_property_get_string(p));
        break;
    default:
        cpl_msg_error(__func__,"Unknown property type: %s", uves_tostring_cpl_type(cpl_property_get_type(p)));
        cpl_error_set(__func__, CPL_ERROR_UNSUPPORTED_MODE);
        break;
    }
    /* This is constant time!! */
    cpl_property_set_comment(uves_propertylist_get(plist, uves_propertylist_get_size(plist) - 1),
                             cpl_property_get_comment(p));
    return;
}

static void
propertylist_prepend_property_cpl(cpl_propertylist *plist, const cpl_property *p)
{
    switch(cpl_property_get_type(p)) {
    case CPL_TYPE_CHAR:
        cpl_propertylist_prepend_char(plist, cpl_property_get_name(p), cpl_property_get_char(p));
        break;
    case CPL_TYPE_BOOL:
        cpl_propertylist_prepend_bool(plist, cpl_property_get_name(p), cpl_property_get_bool(p));
        break;
    case CPL_TYPE_INT:
        cpl_propertylist_prepend_int(plist, cpl_property_get_name(p), cpl_property_get_int(p));
        break;
    case CPL_TYPE_LONG:
        cpl_propertylist_prepend_long(plist, cpl_property_get_name(p), cpl_property_get_long(p));
        break;
    case CPL_TYPE_FLOAT:
        cpl_propertylist_prepend_float(plist, cpl_property_get_name(p), cpl_property_get_float(p));
        break;
    case CPL_TYPE_DOUBLE:
        cpl_propertylist_prepend_double(plist, cpl_property_get_name(p), cpl_property_get_double(p));
        break;
    case CPL_TYPE_STRING:
        cpl_propertylist_prepend_string(plist, cpl_property_get_name(p), cpl_property_get_string(p));
        break;
    default:
        cpl_msg_error(__func__,"Unknown property type: %s", uves_tostring_cpl_type(cpl_property_get_type(p)));
        cpl_error_set(__func__, CPL_ERROR_UNSUPPORTED_MODE);
        break;
    }
    cpl_propertylist_set_comment(plist, cpl_property_get_name(p), cpl_property_get_comment(p));
    return;
}

static cpl_propertylist *
uves_propertylist_to_cpl(const uves_propertylist *self)
{
    cpl_propertylist *result;
    long i;

    if (self == NULL) {
        result = NULL;
    }
    else {
        result = cpl_propertylist_new();
        
        for (i = uves_propertylist_get_size(self)-1; i >= 0; i--) {
            propertylist_prepend_property_cpl(
                result, 
                uves_propertylist_get_const(self, i));
        }
    }
    return result;
}

static void
uves_propertylist_from_cpl(uves_propertylist *self, const cpl_propertylist *list_cpl)
{
    long N = cpl_propertylist_get_size(list_cpl); /* O(n) */
    cpl_propertylist *copy = cpl_propertylist_duplicate(list_cpl); /* O(n) */
    long i;

    assert( uves_propertylist_is_empty(self));

    for (i = 0; i < N; i++) {
        const cpl_property *p = cpl_propertylist_get(copy, 0); /* O(1) */
        propertylist_append_property(self, p); /* O(1) */
        cpl_propertylist_erase(copy, cpl_property_get_name(p)); /* O(1),
                                                                 erases only first match */
    }
    assert( cpl_propertylist_is_empty(copy));
    cpl_propertylist_delete(copy);

    return;
}

/* Wrappers for often used functions which have cpl_propertylists in their interface */
cpl_error_code uves_vector_save(const cpl_vector *v, const char *f, cpl_type_bpp bpp,
                                const uves_propertylist *header, unsigned mode)
{
    cpl_propertylist *header_cpl = uves_propertylist_to_cpl(header);
    cpl_vector_save(v, f, bpp, header_cpl, mode);
    cpl_propertylist_delete(header_cpl);

    return cpl_error_get_code();
}
cpl_error_code 
uves_image_save(const cpl_image *image, const char *f, cpl_type_bpp bpp,
                               const uves_propertylist *header, unsigned mode)
{
  cpl_propertylist *header_cpl = NULL;
  check_nomsg(header_cpl=uves_propertylist_to_cpl(header));
  check_nomsg(cpl_image_save(image, f, bpp, header_cpl, mode));
 cleanup:
    cpl_propertylist_delete(header_cpl);

    return cpl_error_get_code();
}
cpl_error_code uves_imagelist_save(const cpl_imagelist *imagelist, const char *f, cpl_type_bpp bpp,
                               const uves_propertylist *header, unsigned mode)
{
    cpl_propertylist *header_cpl = uves_propertylist_to_cpl(header);
    cpl_imagelist_save(imagelist, f, bpp, header_cpl, mode);
    cpl_propertylist_delete(header_cpl);

    return cpl_error_get_code();
}
cpl_error_code uves_table_save(const cpl_table *table, const uves_propertylist *header,
                               const uves_propertylist *ext_header, const char *filename,
                               unsigned mode)
{
    cpl_propertylist *header_cpl = uves_propertylist_to_cpl(header);
    cpl_propertylist *ext_header_cpl = uves_propertylist_to_cpl(ext_header);
    cpl_table_save(table, header_cpl, ext_header_cpl, filename, mode);
    cpl_propertylist_delete(header_cpl);
    cpl_propertylist_delete(ext_header_cpl);

    return cpl_error_get_code();
}

cpl_error_code uves_dfs_setup_product_header(uves_propertylist *header,
                                             const cpl_frame *product_frame,
                                             const cpl_frameset *framelist,
                                             const cpl_parameterlist *parlist,
                                             const char *recid,
                                             const char *pipeline_id,
                                             const char *dictionary_id)
{
    cpl_propertylist *header_cpl = uves_propertylist_to_cpl(header);

    cpl_dfs_setup_product_header(header_cpl,
                                 product_frame,
                                 framelist,
                                 parlist,
                                 recid,
                                 pipeline_id,
                                 dictionary_id,NULL);

    uves_propertylist_empty(header);
    uves_propertylist_from_cpl(header, header_cpl);
    cpl_propertylist_delete(header_cpl);

    return cpl_error_get_code();
}

cpl_error_code uves_table_sort(cpl_table *t, const uves_propertylist *list)
{
    /* Just use this workaround ... */
   cpl_table_sort(t, (const cpl_propertylist*) list);
    
    /* ... instead of this one */
#if 0
    cpl_propertylist *list_cpl = uves_propertylist_to_cpl(list);
    cpl_table_sort(t, list_cpl);
    cpl_propertylist_delete(list_cpl);
#endif    
    return cpl_error_get_code();
}


/*
 * Private methods
 */


/* Workarounds for cpl_error_push/pop which are not exported */
static cpl_error_code push_pop_error;

static void error_push(void)
{
    push_pop_error = cpl_error_get_code();
    /* Don't track location */
    cpl_error_reset();
    return;
}
static void error_pop(void)
{
    if (push_pop_error != CPL_ERROR_NONE)
        {
            cpl_error_set(__func__, push_pop_error);
        }
    return;
}








static cxint
_uves_propertylist_filter_regexp(cxcptr key, cxcptr filter)
{

    const uves_regexp *_filter = (const uves_regexp *)filter;

    if (regexec(&_filter->re, key, (size_t)0, NULL, 0) == REG_NOMATCH)
        return _filter->invert == TRUE ? TRUE : FALSE;

    return _filter->invert == TRUE ? FALSE : TRUE;

}


static cxbool
_uves_propertylist_compare(const cpl_property *property, const char *name)
{
    const cxchar *key = cpl_property_get_name(property);

    return strcmp(key, name) == 0 ? TRUE : FALSE;

}

/* The following function is not used
static cxbool
_uves_propertylist_compare_start(const cpl_property *property,
                                const char *part_name)
{

    const cxchar *key = cpl_property_get_name(property);

    if (strstr(key, part_name) == key)
        return TRUE;

    return FALSE;

}
*/

static cxbool
_uves_propertylist_compare_regexp(const cpl_property *property, uves_regexp *re)
{

    const cxchar *key = cpl_property_get_name(property);

    return _uves_propertylist_filter_regexp(key, re);

}


static uves_deque_iterator
_uves_propertylist_find(const uves_propertylist *self, const char *name)
{

    uves_deque_iterator first, last;
    cpl_property *p;

    first = uves_deque_begin(self->properties);
    last = uves_deque_end(self->properties);

    while (first != last) {
        p = uves_deque_get(self->properties, first);

        if (_uves_propertylist_compare(p, name))
            break;

        first = uves_deque_next(self->properties, first);
    }

    return first;

}

static cpl_property *
_uves_propertylist_get(const uves_propertylist *self, const char *name)
{
    uves_deque_iterator pos = _uves_propertylist_find(self, name);

    if (pos == uves_deque_end(self->properties))
        return NULL;

    return uves_deque_get(self->properties, pos);

}


static int
_uves_propertylist_insert(uves_propertylist *self, const cxchar *where,
                         cxbool after, const cxchar *name, cpl_type type,
                         cxptr value)
{

    uves_deque_iterator pos;
    cpl_property *property;


    /*
     * Find the position where value should be inserted.
     */

    pos = _uves_propertylist_find(self, where);

    if (pos == uves_deque_end(self->properties)) {
        return 1;
    }

    if (after) {
        pos = uves_deque_next(self->properties, pos);
    }


    /*
     * Create the property for value and fill it.
     */

    property = cpl_property_new(name, type);
    if (!property) {
        return 1;
    }


    /*
     * Map property type to the driver function's argument type.
     */

    switch (type) {
        case CPL_TYPE_CHAR:
            cpl_property_set_char(property, *((cxchar *)value));
            break;

        case CPL_TYPE_BOOL:
            cpl_property_set_bool(property, *((cxint *)value));
            break;

        case CPL_TYPE_INT:
            cpl_property_set_int(property, *((cxint *)value));
            break;

        case CPL_TYPE_LONG:
            cpl_property_set_long(property, *((cxlong *)value));
            break;

        case CPL_TYPE_FLOAT:
            cpl_property_set_float(property, *((cxfloat *)value));
            break;

        case CPL_TYPE_DOUBLE:
            cpl_property_set_double(property, *((cxdouble *)value));
            break;

        case CPL_TYPE_STRING:
            cpl_property_set_string(property, ((const cxchar *)value));
            break;

        default:
            return 1;
            break;
    }


    /*
     * Insert it into the list
     */

    uves_deque_insert(self->properties, pos, property);

    return 0;

}

/*
 * Parser for FITS cards. Returns 0 if the card was successfully parsed
 * into its components, 1 if the card should be ignored and a negative
 * number if an error occurred. If the parsing was successful the function
 * fills the buffers pointed to by key, value, comment and type.
 *
 * The buffers key, value and comment must, at least, have the size
 * FITS_LINESZ + 1!
 *
 * Errors:
 *  -1  Invalid pointer to the input FITS header.
 *  -2  Invalid keyword name detected (cf. qfits_header_getitem())
 */

static cxint
_uves_propertylist_decode_fits(const qfits_header *header, cxint i,
                              cxchar *key, cxint *type, cxchar *value,
                              cxchar *comment)
{

    cxchar _key[FITS_LINESZ + 1];
    cxchar _value[FITS_LINESZ + 1];
    cxchar _comment[FITS_LINESZ + 1];
    cxchar *s;

    if (!header)
        return -1;

    /*
     * Get a FITS card, decode it into its components and determine
     * its type.
     */

    if (qfits_header_getitem((qfits_header *)header, i, _key, _value,
                             _comment, NULL) != 0) {
        return -2;
    }


    /*
     * Skip the END record. Qfits ignores empty header cards. Therefore
     * we do not need to take care of them here, but who knows.
     */

    s = _key;

    if (strncmp(s, "END", 3) == 0 || strncmp(s, "        ", 8) == 0 ||
        strlen(s) == 0) {
        return 1;
    }


    /*
     * strip the HIERARCH prefix from the keyword name if it
     * is present.
     */

    if (strncmp(s, "HIERARCH ", 9) == 0)
        s += 9;

    strncpy(key, s, FITS_LINESZ);
    key[FITS_LINESZ] = '\0';


    /*
     * Parse the value string and determine its type. Comment and
     * history records are forced to be strings. The type check
     * has to be done before cleaning the value, so that strings
     * like '1.0' are still recognized as such.
     */

    s = _value;

    if (strncmp(_key, "COMMENT", 7) == 0 || strncmp(_key, "HISTORY", 7) == 0) {
        *type = QFITS_STRING;

        /*
         * qfits returns an empty string if there is no value for a keyword
         */

        if (strlen(s) == 0) {
            value[0] = '\0';
        }
        else {
            strncpy(value, s, FITS_LINESZ);
        }
    }
    else {
        *type = qfits_get_type(s);
        strncpy(value, qfits_pretty_string(s), FITS_LINESZ);
        value[FITS_LINESZ] = '\0';
    }


    /*
     * Parse the comment
     */

    s = _comment;

    /*
     * qfits returns an empty string if there is no value for a keyword
     */

    if (strlen(s) == 0)
        comment[0] = '\0';
    else {
        strncpy(comment, s, FITS_LINESZ);
        comment[FITS_LINESZ] = '\0';
    }

    return 0;

}




/*
 * The function converts a FITS header to a property list ignoring
 * keywords for which _uves_propertylist_decode_fits() returns 1. If
 * parsing a FITS keyword fails the function returns the status from
 * _uves_propertylist_decode_fits(). If the type of a FITS keyword is not
 * supported -1 is returned.
 */

static cxint
_uves_propertylist_from_fits(uves_propertylist *self,
                            const qfits_header *header,
                            cx_compare_func filter,
                            cxcptr data)
{

    register cxint i;


    cx_assert(self != NULL);
    cx_assert(header != NULL);


    /*
     * Build the property list from the header.
     */

    for (i = 0; i < header->n; i++) {
        register cxint status;

        cxint type;
        cxint ival;

        cxdouble dval;

        cxchar key[FITS_LINESZ + 1];
        cxchar value[FITS_LINESZ + 1];
        cxchar comment[FITS_LINESZ + 1];

        cpl_property *property;


        status = _uves_propertylist_decode_fits(header, i, key, &type,
                                               value, comment);

        if (status) {
            switch (status) {
            case 1:
                continue;
                break;

            default:
                return status;
                break;
            }
        }


        if (filter != NULL && filter(key, data) == FALSE) {
            continue;
        }


        /*
         * Create the property from the parsed FITS card.
         */

        switch (type) {
            case QFITS_BOOLEAN:
                property = cpl_property_new(key, CPL_TYPE_BOOL);

                ival = *value == 'T' ? 1 : 0;
                cpl_property_set_bool(property, ival);
                cpl_property_set_comment(property, comment);
                break;

            case QFITS_INT:
                property = cpl_property_new(key, CPL_TYPE_INT);

                sscanf(value, "%d", &ival);
                cpl_property_set_int(property, ival);
                cpl_property_set_comment(property, comment);
                break;

            case QFITS_FLOAT:
                property = cpl_property_new(key, CPL_TYPE_DOUBLE);

                sscanf(value, "%lf", &dval);
                cpl_property_set_double(property, dval);
                cpl_property_set_comment(property, comment);
                break;

                /* FIXME: qfits does not correctly report the type for
                 *        string values if the single quotes have been
                 *        stripped. In this case it reports QFITS_UNKNOWN
                 *        for the value's type. As a temporary (!) work
                 *        around we accept everything unknown as a string.
                 */

            case QFITS_UNKNOWN:
            case QFITS_STRING:
                property = cpl_property_new(key, CPL_TYPE_STRING);

                cpl_property_set_string(property, value);
                cpl_property_set_comment(property, comment);
                break;

            case QFITS_COMPLEX:

                /* FIXME: Add support for complex keywords. */

            default:
                return 1;
                break;
        }

        uves_deque_push_back(self->properties, property);
    }

    return 0;

}



/*
 * Public methods
 */

/**
 * @brief
 *    Create an empty property list.
 *
 * @return
 *   The newly created property list.
 *
 * The function creates a new property list and returns a handle for it.
 * To destroy the returned property list object use the property list
 * destructor @b uves_propertylist_delete().
 *
 * @see uves_propertylist_delete()
 */

uves_propertylist *
uves_propertylist_new(void)
{

    uves_propertylist *self = cx_malloc(sizeof *self);

    self->properties = uves_deque_new();
    return self;

}


/**
 * @brief
 *   Create a copy of the given property list.
 *
 * @param self  The property list to be copied.
 *
 * @return
 *   The created copy or @c NULL in case an error occurred.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> is a <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a deep copy of the given property list @em self,
 * i.e the created copy and the original property list do not share any
 * resources.
 */

uves_propertylist *
uves_propertylist_duplicate(const uves_propertylist *self)
{

    const cxchar *const _id = "uves_propertylist_duplicate";

    uves_deque_iterator first, last;

    uves_propertylist *copy = NULL;


    if (self == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return NULL;
    }

    cx_assert(self->properties != NULL);


    copy = uves_propertylist_new();

    first = uves_deque_begin(self->properties);
    last = uves_deque_end(self->properties);

    while (first != last) {
        cpl_property *tmp = uves_deque_get(self->properties, first);

        uves_deque_push_back(copy->properties, cpl_property_duplicate(tmp));
        first = uves_deque_next(self->properties, first);
    }

    return copy;

}


/**
 * @brief
 *    Destroy a property list.
 *
 * @param self  The property list to destroy.
 *
 * @return
 *   Nothing.
 *
 * The function destroys the property list @em self and its whole
 * contents.
 */

void
uves_propertylist_delete(const uves_propertylist *self)
{

    if (self) {
        uves_deque_destroy(self->properties, (cx_free_func)cpl_property_delete);
        cx_free((void *)self);
    }

    return;

}


/**
 * @brief
 *    Get the current size of a property list.
 *
 * @param self  A property list.
 *
 * @return
 *   The property list's current size, or 0 if the list is empty. If an
 *   error occurs the function returns 0 and sets an appropriate error
 *   code.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> is a <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function reports the current number of elements stored in the property
 * list @em self.
 */

long
uves_propertylist_get_size(const uves_propertylist *self)
{

    const cxchar *const _id = "uves_propertylist_get_size";


    if (self == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0L;
    }

    return (long) uves_deque_size(self->properties);

}


/**
 * @brief
 *   Check whether a property list is empty.
 *
 * @param self  A property list.
 *
 * @return
 *   The function returns 1 if the list is empty, and 0 otherwise.
 *   In case an error occurs the function returns -1 and sets an
 *   appropriate error code.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> is a <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function checks if @em self contains any properties.
 */

int
uves_propertylist_is_empty(const uves_propertylist *self)
{

    const cxchar *const _id = "uves_propertylist_is_empty";


    if (self == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return -1;
    }

    return uves_deque_empty(self->properties);

}


/**
 * @brief
 *   Get the the type of a property list entry.
 *
 * @param self  A property list.
 * @param name   The property name to look up.
 *
 * @return
 *   The type of the stored value. If an error occurs the function returns
 *   @c CPL_TYPE_INVALID and sets an appropriate error code.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function returns the type of the value stored in @em self with the
 * name @em name.
 */

cpl_type
uves_propertylist_get_type(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_get_type";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_TYPE_INVALID;
    }

    property = _uves_propertylist_get(self, name);

    if (property == NULL) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_TYPE_INVALID;
    }

    return cpl_property_get_type(property);

}


/**
 * @brief
 *   Check whether a property is present in a property list.
 *
 * @param self  A property list.
 * @param name  The property name to look up.
 *
 * @return
 *   The function returns 1 if the property is present, or 0 otherwise.
 *   If an error occurs the function returns 0 and sets an appropriate
 *   error code.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property with
 * the name @em name and reports whether it was found or not.
 */

int
uves_propertylist_contains(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_contains";


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }

    return _uves_propertylist_get(self, name) != NULL ? 1 : 0;

}





/**
 * @brief
 *   Check whether a property is present in a property list.
 *
 * @param self  A property list.
 * @param name  The property name to look up.
 *
 * @return
 *   The function returns 1 if the property is present, or 0 otherwise.
 *   If an error occurs the function returns 0 and sets an appropriate
 *   error code.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property with
 * the name @em name and reports whether it was found or not.
 */

int
my_uves_propertylist_contains(const cpl_propertylist *self, const char *name)
{

    const cxchar *const _id = "my_uves_propertylist_contains";


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }

    return cpl_propertylist_has(self, name);

}








/**
 * @brief
 *   Modify the comment field of the given property list entry.
 *
 * @param self    A property list.
 * @param name     The property name to look up.
 * @param comment  New comment string.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error code
 *   otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its comment is replaced by
 * the string @em comment. The provided comment string may be @c NULL.
 * In this case an already existing comment is deleted.
 */

cpl_error_code
uves_propertylist_set_comment(uves_propertylist *self, const char *name,
                             const char *comment)
{

    const cxchar *const _id = "uves_propertylist_set_comment";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = _uves_propertylist_get(self, name);

    if (property == NULL) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_ERROR_DATA_NOT_FOUND;
    }

    cpl_property_set_comment(property, comment);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Set the value of the given character property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 * @param value  New character value.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error code
 *   otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its character value is replaced
 * with the character @em value.
 */

cpl_error_code
uves_propertylist_set_char(uves_propertylist *self, const char *name,
                          char value)
{

    const cxchar *const _id = "uves_propertylist_set_char";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = _uves_propertylist_get(self, name);

    if (property == NULL) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_ERROR_DATA_NOT_FOUND;
    }

    return cpl_property_set_char(property, value);

}


/**
 * @brief
 *   Set the value of the given boolean property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 * @param value  New boolean value.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error code
 *   otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its boolean value is replaced
 * with the boolean @em value.
 */

cpl_error_code
uves_propertylist_set_bool(uves_propertylist *self, const char *name, int value)
{

    const cxchar *const _id = "uves_propertylist_set_bool";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = _uves_propertylist_get(self, name);

    if (property == NULL) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_ERROR_DATA_NOT_FOUND;
    }

    return cpl_property_set_bool(property, value);

}


/**
 * @brief
 *   Set the value of the given integer property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 * @param value  New integer value.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error code
 *   otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its integer value is replaced
 * with the integer @em value.
 */

cpl_error_code
uves_propertylist_set_int(uves_propertylist *self, const char *name, int value)
{

    const cxchar *const _id = "uves_propertylist_set_int";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = _uves_propertylist_get(self, name);

    if (property == NULL) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_ERROR_DATA_NOT_FOUND;
    }

    return cpl_property_set_int(property, value);

}


/**
 * @brief
 *   Set the value of the given long property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 * @param value  New long value.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error code
 *   otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its long value is replaced with
 * the long @em value.
 */

cpl_error_code
uves_propertylist_set_long(uves_propertylist *self, const char *name,
                          long value)
{

    const cxchar *const _id = "uves_propertylist_set_long";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = _uves_propertylist_get(self, name);

    if (property == NULL) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_ERROR_DATA_NOT_FOUND;
    }

    return cpl_property_set_long(property, value);

}


/**
 * @brief
 *   Set the value of the given float property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 * @param value  New float value.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error code
 *   otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its float value is replaced with
 * the float @em value.
 */

cpl_error_code
uves_propertylist_set_float(uves_propertylist *self, const char *name,
                           float value)
{

    const cxchar *const _id = "uves_propertylist_set_float";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = _uves_propertylist_get(self, name);

    if (property == NULL) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_ERROR_DATA_NOT_FOUND;
    }

    return cpl_property_set_float(property, value);

}


/**
 * @brief
 *   Set the value of the given double property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 * @param value  New double value.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error code
 *   otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its double value is replaced with
 * the double @em value.
 */

cpl_error_code
uves_propertylist_set_double(uves_propertylist *self, const char *name,
                            double value)
{

    const cxchar *const _id = "uves_propertylist_set_double";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = _uves_propertylist_get(self, name);

    if (property == NULL) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_ERROR_DATA_NOT_FOUND;
    }

    return cpl_property_set_double(property, value);

}


/**
 * @brief
 *   Set the value of the given string property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 * @param value  New string value.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error code
 *   otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its string value is replaced with
 * the string @em value.
 */

cpl_error_code
uves_propertylist_set_string(uves_propertylist *self, const char *name,
                            const char *value)
{

    const cxchar *const _id = "uves_propertylist_set_string";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = _uves_propertylist_get(self, name);

    if (property == NULL) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_ERROR_DATA_NOT_FOUND;
    }

    return cpl_property_set_string(property, value);

}


/**
 * @brief
 *   Access property list elements by index.
 *
 * @param self      The property list to query.
 * @param position  Index of the element to retrieve.
 *
 * @return
 *   The function returns the property with index @em position, or @c NULL
 *   if @em position is out of range.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> is a <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function returns a handle for the property list element, the property,
 * with the index @em position. Numbering of property list elements extends from
 * 0 to @b uves_propertylist_get_size() - 1. If @em position is less than 0 or
 * greater equal than @b uves_propertylist_get_size() the function returns
 * @c NULL.
 */

const cpl_property *
uves_propertylist_get_const(const uves_propertylist *self, long position)
{

    const cxchar *const _id = "uves_propertylist_get";

//    register cxsize i = 0;

    uves_deque_iterator first, last;


    if (self == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return NULL;
    }

    if (position < 0) {
        return NULL;
    }

    first = uves_deque_begin(self->properties);
    last = uves_deque_end(self->properties);

//    while (i < (cxsize)position && first != last) {
//        first = uves_deque_next(self->properties, first);
//        i++;
//    }

    if (first == last) {
        return NULL;
    }

//    return uves_deque_get(self->properties, first);
    return uves_deque_get(self->properties, position);

}

cpl_property *
uves_propertylist_get(uves_propertylist *self, long position)
{
    return (cpl_property *)uves_propertylist_get_const(self, position);
}


/**
 * @brief
 *   Get the comment of the given property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 *
 * @return
 *   The comment of the property list entry, or @c NULL.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its comment string is returned.
 * If an entry with the name @em name is not found, or if the entry has
 * no comment the function returns @c NULL.
 */

const char *
uves_propertylist_get_comment(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_get_comment";

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return NULL;
    }

    property = _uves_propertylist_get(self, name);

    if (!property) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return NULL;
    }

    return cpl_property_get_comment(property);

}


/**
 * @brief
 *   Get the character value of the given property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 *
 * @return
 *   The character value stored in the list entry. The function returns '\\0'
 *   if an error occurs and an appropriate error code is set.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The sought-after property <i>name</i> is not of type
 *         @c CPL_TYPE_CHAR.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its character value is returned.
 */

char
uves_propertylist_get_char(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_get_char";

    cxchar result;

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return '\0';
    }

    property = _uves_propertylist_get(self, name);

    if (!property) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return '\0';
    }

    error_push();
//jmlarsen: this is not exported    cpl_error_push();

    result = cpl_property_get_char(property);

    /*
     * If an error occurred change any possibly set location to this
     * function.
     */

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
        cpl_error_set_where(_id);
        return '\0';
    }

//jmlarsen: this is not exported    cpl_error_pop();
    error_pop();

    return result;

}


/**
 * @brief
 *   Get the boolean value of the given property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 *
 * @return
 *   The integer representation of the boolean value stored in
 *   the list entry. @c TRUE is represented as non-zero value while
 *   0 indicates @c FALSE. The function returns 0 if an error occurs
 *   and an appropriate error code is set.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The sought-after property <i>name</i> is not of type
 *         @c CPL_TYPE_BOOL.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its boolean value is returned.
 */

int
uves_propertylist_get_bool(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_get_bool";

    cxbool result;

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }

    property = _uves_propertylist_get(self, name);

    if (!property) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return 0;
    }

    error_push();
//jmlarsen: this is not exported    cpl_error_push();

    result = cpl_property_get_bool(property);

    /*
     * If an error occurred change any possibly set location to this
     * function.
     */

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
        cpl_error_set_where(_id);
        return 0;
    }

//jmlarsen: this is not exported    cpl_error_pop();
    error_pop();

    return result == TRUE ? 1 : 0;

}


/**
 * @brief
 *   Get the integer value of the given property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 *
 * @return
 *   The integer value stored in the list entry. The function returns 0 if
 *   an error occurs and an appropriate error code is set.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The sought-after property <i>name</i> is not of type
 *         @c CPL_TYPE_INT.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its integer value is returned.
 */

int
uves_propertylist_get_int(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_get_int";

    cxint result;

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }

    property = _uves_propertylist_get(self, name);

    if (!property) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return 0;
    }

    error_push();
//jmlarsen: this is not exported    cpl_error_push();

    result = cpl_property_get_int(property);

    /*
     * If an error occurred change any possibly set location to this
     * function.
     */

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
        cpl_error_set_where(_id);
        return 0;
    }

//jmlarsen: this is not exported    cpl_error_pop();
    error_pop();

    return result;

}


/**
 * @brief
 *   Get the long value of the given property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 *
 * @return
 *   The long value stored in the list entry. The function returns 0 if
 *   an error occurs and an appropriate error code is set.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The sought-after property <i>name</i> is not of type
 *         @c CPL_TYPE_LONG.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its long value is returned.
 */

long
uves_propertylist_get_long(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_get_long";

    cxlong result;

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }

    property = _uves_propertylist_get(self, name);

    if (!property) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return 0;
    }

    error_push();
//jmlarsen: this is not exported    cpl_error_push();

    result = cpl_property_get_long(property);

    /*
     * If an error occurred change any possibly set location to this
     * function.
     */

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
        cpl_error_set_where(_id);
        return 0;
    }

//jmlarsen: this is not exported    cpl_error_pop();
    error_pop();

    return result;

}


/**
 * @brief
 *   Get the float value of the given property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 *
 * @return
 *   The float value stored in the list entry. The function returns 0 if
 *   an error occurs and an appropriate error code is set.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The sought-after property <i>name</i> is not of type
 *         @c CPL_TYPE_FLOAT.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its float value is returned.
 */

float
uves_propertylist_get_float(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_get_float";

    cxfloat result;

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }

    property = _uves_propertylist_get(self, name);

    if (!property) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return 0;
    }

    error_push();
//jmlarsen: this is not exported    cpl_error_push();

    result = cpl_property_get_float(property);

    /*
     * If an error occurred change any possibly set location to this
     * function.
     */

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
        cpl_error_set_where(_id);
        return 0;
    }

//jmlarsen: this is not exported    cpl_error_pop();
    error_pop();

    return result;

}


/**
 * @brief
 *   Get the double value of the given property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 *
 * @return
 *   The double value stored in the list entry. The function returns 0 if
 *   an error occurs and an appropriate error code is set.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The sought-after property <i>name</i> is not of type
 *         @c CPL_TYPE_DOUBLE.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, its double value is returned.
 */

double
uves_propertylist_get_double(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_get_double";

    cxdouble result;

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }

    property = _uves_propertylist_get(self, name);

    if (!property) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return 0;
    }

    error_push();
//jmlarsen: this is not exported    cpl_error_push();

    result = cpl_property_get_double(property);

    /*
     * If an error occurred change any possibly set location to this
     * function.
     */

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
        cpl_error_set_where(_id);
        return 0;
    }

//jmlarsen: this is not exported    cpl_error_pop();
    error_pop();

    return result;

}


/**
 * @brief
 *   Get the string value of the given property list entry.
 *
 * @param self   A property list.
 * @param name   The property name to look up.
 *
 * @return
 *   A handle to the string value stored in the list entry. The
 *   function returns @c NULL if an error occurs and an appropriate
 *   error code is set.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>self</i> does not contain a property with
 *         the name <i>name</i>.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The sought-after property <i>name</i> is not of type
 *         @c CPL_TYPE_STRING.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property named
 * @em name. If it is present in the list, a handle to its string value
 * is returned.
 */

const char *
uves_propertylist_get_string(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_get_string";

    const cxchar *result;

    cpl_property *property;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return NULL;
    }

    property = _uves_propertylist_get(self, name);

    if (!property) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return NULL;
    }

    error_push();
//jmlarsen: this is not exported    cpl_error_push();

    result = cpl_property_get_string(property);

    /*
     * If an error occurred change any possibly set location to this
     * function.
     */

    if (cpl_error_get_code() != CPL_ERROR_NONE) {
        cpl_error_set_where(_id);
        return NULL;
    }

//jmlarsen: this is not exported    cpl_error_pop();
    error_pop();

    return result;

}


/**
 * @brief
 *   Insert a character value into a property list at the given position.
 *
 * @param self   A property list.
 * @param here   Name indicating the position at which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The character value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>here</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new character property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self at the position of the property named @em here.
 */

cpl_error_code
uves_propertylist_insert_char(uves_propertylist *self, const char *here,
                             const char *name, char value)
{

    const cxchar *const _id = "uves_propertylist_insert_char";

    cxint status = 0;


    if (self == NULL || here == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, here, FALSE, name,
                                      CPL_TYPE_CHAR, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a boolean value into a property list at the given position.
 *
 * @param self   A property list.
 * @param here   Name indicating the position at which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The boolean value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>here</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new boolean property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self at the position of the property named @em here.
 */

cpl_error_code
uves_propertylist_insert_bool(uves_propertylist *self, const char *here,
                             const char *name, int value)
{

    const cxchar *const _id = "uves_propertylist_insert_bool";

    cxint status = 0;


    if (self == NULL || here == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status =  _uves_propertylist_insert(self, here, FALSE, name,
                                       CPL_TYPE_BOOL, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a integer value into a property list at the given position.
 *
 * @param self   A property list.
 * @param here   Name indicating the position at which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The integer value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>here</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new integer property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self at the position of the property named @em here.
 */

cpl_error_code
uves_propertylist_insert_int(uves_propertylist *self, const char *here,
                            const char *name, int value)
{

    const cxchar *const _id = "uves_propertylist_insert_int";

    cxint status = 0;


    if (self == NULL || here == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, here, FALSE, name,
                                      CPL_TYPE_INT, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}
/**
 * @brief
 *   Check whether a property is present in a property list.
 *
 * @param self  A property list.
 * @param name  The property name to look up.
 *
 * @return
 *   The function returns 1 if the property is present, or 0 otherwise.
 *   If an error occurs the function returns 0 and sets an appropriate
 *   error code.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property list @em self for a property with
 * the name @em name and reports whether it was found or not.
 */

int
uves_propertylist_has(const uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "cpl_propertylist_has";


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }

    return _uves_propertylist_get(self, name) != NULL ? 1 : 0;

}


/**
 * @brief
 *   Insert a long value into a property list at the given position.
 *
 * @param self   A property list.
 * @param here   Name indicating the position at which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The long value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>here</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new long property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self at the position of the property named @em here.
 */

cpl_error_code
uves_propertylist_insert_long(uves_propertylist *self, const char *here,
                             const char *name, long value)
{

    const cxchar *const _id = "uves_propertylist_insert_long";

    cxint status = 0;


    if (self == NULL || here == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, here, FALSE, name,
                                      CPL_TYPE_LONG, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a float value into a property list at the given position.
 *
 * @param self   A property list.
 * @param here   Name indicating the position at which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The float value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>here</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new float property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self at the position of the property named @em here.
 */

cpl_error_code
uves_propertylist_insert_float(uves_propertylist *self, const char *here,
                              const char *name, float value)
{

    const cxchar *const _id = "uves_propertylist_insert_float";

    cxint status = 0;


    if (self == NULL || here == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, here, FALSE, name,
                                      CPL_TYPE_FLOAT, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a double value into a property list at the given position.
 *
 * @param self   A property list.
 * @param here   Name indicating the position at which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The double value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>here</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new double property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self at the position of the property named @em here.
 */

cpl_error_code
uves_propertylist_insert_double(uves_propertylist *self, const char *here,
                               const char *name, double value)
{

    const cxchar *const _id = "uves_propertylist_insert_char";

    cxint status = 0;


    if (self == NULL || here == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, here, FALSE, name,
                                      CPL_TYPE_DOUBLE, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a string value into a property list at the given position.
 *
 * @param self   A property list.
 * @param here   Name indicating the position at which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The string value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>here</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new string property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self at the position of the property named @em here.
 */

cpl_error_code
uves_propertylist_insert_string(uves_propertylist *self, const char *here,
                               const char *name, const char *value)
{

    const cxchar *const _id = "uves_propertylist_insert_string";

    cxint status = 0;


    if (self == NULL || here == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, here, FALSE, name,
                                      CPL_TYPE_STRING, (cxptr)value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a character value into a property list after the given position.
 *
 * @param self   A property list.
 * @param after  Name of the property after which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The character value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>after</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new character property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self after the property named @em after.
 */

cpl_error_code
uves_propertylist_insert_after_char(uves_propertylist *self, const char *after,
                                   const char *name, char value)
{

    const cxchar *const _id = "uves_propertylist_insert_after_char";

    cxint status = 0;


    if (self == NULL || after == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, after, TRUE, name,
                                      CPL_TYPE_CHAR, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a boolean value into a property list after the given position.
 *
 * @param self   A property list.
 * @param after  Name of the property after which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The boolean value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>after</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new boolean property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self after the property named @em after.
 */

cpl_error_code
uves_propertylist_insert_after_bool(uves_propertylist *self, const char *after,
                                   const char *name, int value)
{

    const cxchar *const _id = "uves_propertylist_insert_after_bool";

    cxint status = 0;


    if (self == NULL || after == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, after, TRUE, name,
                                      CPL_TYPE_BOOL, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a integer value into a property list after the given position.
 *
 * @param self   A property list.
 * @param after  Name of the property after which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The integer value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>after</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new integer property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self after the property named @em after.
 */

cpl_error_code
uves_propertylist_insert_after_int(uves_propertylist *self, const char *after,
                                  const char *name, int value)
{

    const cxchar *const _id = "uves_propertylist_insert_after_int";

    cxint status = 0;


    if (self == NULL || after == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, after, TRUE, name,
                                      CPL_TYPE_INT, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a long value into a property list after the given position.
 *
 * @param self   A property list.
 * @param after  Name of the property after which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The long value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>after</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new long property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self after the property named @em after.
 */

cpl_error_code
uves_propertylist_insert_after_long(uves_propertylist *self, const char *after,
                                   const char *name, long value)
{

    const cxchar *const _id = "uves_propertylist_insert_after_long";

    cxint status = 0;


    if (self == NULL || after == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, after, TRUE, name,
                                      CPL_TYPE_LONG, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a float value into a property list after the given position.
 *
 * @param self   A property list.
 * @param after  Name of the property after which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The float value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>after</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new float property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self after the property named @em after.
 */

cpl_error_code
uves_propertylist_insert_after_float(uves_propertylist *self, const char *after,
                                    const char *name, float value)
{

    const cxchar *const _id = "uves_propertylist_insert_after_float";

    cxint status = 0;


    if (self == NULL || after == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, after, TRUE, name,
                                      CPL_TYPE_FLOAT, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a double value into a property list after the given position.
 *
 * @param self   A property list.
 * @param after  Name of the property after which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The double value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>after</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new double property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self after the property named @em after.
 */

cpl_error_code
uves_propertylist_insert_after_double(uves_propertylist *self,
                                     const char *after, const char *name,
                                     double value)
{

    const cxchar *const _id = "uves_propertylist_insert_after_double";

    cxint status = 0;


    if (self == NULL || after == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = _uves_propertylist_insert(self, after, TRUE, name,
                                      CPL_TYPE_DOUBLE, &value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Insert a string value into a property list after the given position.
 *
 * @param self   A property list.
 * @param after  Name of the property after which the value is inserted.
 * @param name   The property name to be assigned to the value.
 * @param value  The string value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>after</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_UNSPECIFIED</td>
 *       <td class="ecr">
 *         A property with the name <i>name</i> could not be inserted
 *         into <i>self</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new string property with name @em name and
 * value @em value. The property is inserted into the property list
 * @em self after the property named @em after.
 */

cpl_error_code
uves_propertylist_insert_after_string(uves_propertylist *self,
                                     const char *after, const char *name,
                                     const char *value)
{

    const cxchar *const _id = "uves_propertylist_insert_after_string";

    cxint status = 0;


    if (self == NULL || after == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status =  _uves_propertylist_insert(self, after, TRUE, name,
                                       CPL_TYPE_STRING, (cxptr)value);

    if (status) {
        cpl_error_set(_id, CPL_ERROR_UNSPECIFIED);
        return CPL_ERROR_UNSPECIFIED;
    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Prepend a character value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The character value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new character property with name @em name and
 * value @em value. The property is prepended to the property list @em self.
 */

cpl_error_code
uves_propertylist_prepend_char(uves_propertylist *self, const char *name,
                              char value)
{

    const cxchar *const _id = "uves_propertylist_prepend_char";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_CHAR);
    cx_assert(property != NULL);

    cpl_property_set_char(property, value);
    uves_deque_push_front(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Prepend a boolean value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The boolean value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new boolean property with name @em name and
 * value @em value. The property is prepended to the property list @em self.
 */

cpl_error_code
uves_propertylist_prepend_bool(uves_propertylist *self, const char *name,
                              int value)
{

    const cxchar *const _id = "uves_propertylist_prepend_bool";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_BOOL);
    cx_assert(property != NULL);

    cpl_property_set_bool(property, value);
    uves_deque_push_front(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Prepend a integer value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The integer value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new integer property with name @em name and
 * value @em value. The property is prepended to the property list @em self.
 */

cpl_error_code
uves_propertylist_prepend_int(uves_propertylist *self, const char *name,
                             int value)
{

    const cxchar *const _id = "uves_propertylist_prepend_int";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_INT);
    cx_assert(property != NULL);

    cpl_property_set_int(property, value);
    uves_deque_push_front(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Prepend a long value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The long value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new long property with name @em name and
 * value @em value. The property is prepended to the property list @em self.
 */

cpl_error_code
uves_propertylist_prepend_long(uves_propertylist *self, const char *name,
                              long value)
{

    const cxchar *const _id = "uves_propertylist_prepend_long";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_LONG);
    cx_assert(property != NULL);

    cpl_property_set_long(property, value);
    uves_deque_push_front(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Prepend a float value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The float value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new float property with name @em name and
 * value @em value. The property is prepended to the property list @em self.
 */

cpl_error_code
uves_propertylist_prepend_float(uves_propertylist *self, const char *name,
                               float value)
{

    const cxchar *const _id = "uves_propertylist_prepend_float";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_FLOAT);
    cx_assert(property != NULL);

    cpl_property_set_float(property, value);
    uves_deque_push_front(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Prepend a double value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The double value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new double property with name @em name and
 * value @em value. The property is prepended to the property list @em self.
 */

cpl_error_code
uves_propertylist_prepend_double(uves_propertylist *self, const char *name,
                                double value)
{

    const cxchar *const _id = "uves_propertylist_prepend_double";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_DOUBLE);
    cx_assert(property != NULL);

    cpl_property_set_double(property, value);
    uves_deque_push_front(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Prepend a string value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The string value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new string property with name @em name and
 * value @em value. The property is prepended to the property list @em self.
 */

cpl_error_code
uves_propertylist_prepend_string(uves_propertylist *self, const char *name,
                                const char *value)
{

    const cxchar *const _id = "uves_propertylist_prepend_string";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_STRING);
    cx_assert(property != NULL);

    cpl_property_set_string(property, value);
    uves_deque_push_front(self->properties, property);

    return CPL_ERROR_NONE;

}



cpl_error_code
uves_propertylist_append_char(uves_propertylist *self, const char *name,
                             char value)
{
    return uves_propertylist_append_c_char(self, name, value, NULL);
}
cpl_error_code
uves_propertylist_append_bool(uves_propertylist *self, const char *name,
                             int value)
{
    return uves_propertylist_append_c_bool(self, name, value, NULL);
}

cpl_error_code
uves_propertylist_append_int(uves_propertylist *self, const char *name,
                            int value)
{
    return uves_propertylist_append_c_int(self, name, value, NULL);
}

cpl_error_code
uves_propertylist_append_long(uves_propertylist *self, const char *name,
                             long value)
{
    return uves_propertylist_append_c_long(self, name, value, NULL);
}

cpl_error_code
uves_propertylist_append_float(uves_propertylist *self, const char *name,
                              float value)
{
    return uves_propertylist_append_c_float(self, name, value, NULL);
}

cpl_error_code
uves_propertylist_append_double(uves_propertylist *self, const char *name,
                               double value)
{
    return uves_propertylist_append_c_double(self, name, value, NULL);
}

cpl_error_code
uves_propertylist_append_string(uves_propertylist *self, const char *name,
                               const char *value)
{
    return uves_propertylist_append_c_string(self, name, value, NULL);
}


















/**
 * @brief
 *   Append a character value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The character value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new character property with name @em name and
 * value @em value. The property is appended to the property list @em self.
 */

cpl_error_code
uves_propertylist_append_c_char(uves_propertylist *self, const char *name,
                             char value, const char *comment)
{

    const cxchar *const _id = "uves_propertylist_append_char";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_CHAR);
    cx_assert(property != NULL);

    if (comment != NULL) cpl_property_set_comment(property, comment);

    cpl_property_set_char(property, value);
    uves_deque_push_back(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Append a boolean value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The boolean value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new boolean property with name @em name and
 * value @em value. The property is appended to the property list @em self.
 */

cpl_error_code
uves_propertylist_append_c_bool(uves_propertylist *self, const char *name,
                             int value, const char *comment)
{

    const cxchar *const _id = "uves_propertylist_append_bool";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_BOOL);
    cx_assert(property != NULL);

    if (comment != NULL) cpl_property_set_comment(property, comment);

    cpl_property_set_bool(property, value);
    uves_deque_push_back(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Append an integer value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The integer value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new integer property with name @em name and
 * value @em value. The property is appended to the property list @em self.
 */

cpl_error_code
uves_propertylist_append_c_int(uves_propertylist *self, const char *name,
                            int value, const char *comment)
{

    const cxchar *const _id = "uves_propertylist_append_int";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_INT);
    cx_assert(property != NULL);

    if (comment != NULL) cpl_property_set_comment(property, comment);

    cpl_property_set_int(property, value);
    uves_deque_push_back(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Append a long value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The long value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new long property with name @em name and
 * value @em value. The property is appended to the property list @em self.
 */

cpl_error_code
uves_propertylist_append_c_long(uves_propertylist *self, const char *name,
                             long value, const char *comment)
{

    const cxchar *const _id = "uves_propertylist_append_long";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_LONG);
    cx_assert(property != NULL);

    if (comment != NULL) cpl_property_set_comment(property, comment);

    cpl_property_set_long(property, value);
    uves_deque_push_back(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Append a float value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The float value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new float property with name @em name and
 * value @em value. The property is appended to the property list @em self.
 */

cpl_error_code
uves_propertylist_append_c_float(uves_propertylist *self, const char *name,
                              float value, const char *comment)
{

    const cxchar *const _id = "uves_propertylist_append_float";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_FLOAT);
    cx_assert(property != NULL);

    if (comment != NULL) cpl_property_set_comment(property, comment);

    cpl_property_set_float(property, value);
    uves_deque_push_back(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Append a double value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The double value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new double property with name @em name and
 * value @em value. The property is appended to the property list @em self.
 */

cpl_error_code
uves_propertylist_append_c_double(uves_propertylist *self, const char *name,
                               double value, const char *comment)
{

    const cxchar *const _id = "uves_propertylist_append_double";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_DOUBLE);
    cx_assert(property != NULL);

    if (comment != NULL) cpl_property_set_comment(property, comment);

    cpl_property_set_double(property, value);
    uves_deque_push_back(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Append a string value to a property list.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The string value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function creates a new string property with name @em name and
 * value @em value. The property is appended to the property list @em self.
 */

cpl_error_code
uves_propertylist_append_c_string(uves_propertylist *self, const char *name,
                               const char *value, const char *comment)
{

    const cxchar *const _id = "uves_propertylist_append_string";

    cpl_property *property = NULL;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    property = cpl_property_new(name, CPL_TYPE_STRING);
    cx_assert(property != NULL);

    if (comment != NULL) cpl_property_set_comment(property, comment);

    cpl_property_set_string(property, value);
    uves_deque_push_back(self->properties, property);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Append a property list..
 *
 * @param self   A property list.
 * @param other  The property to append.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> is a <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function appends the property list @em other to the property list
 * @em self.
 */

cpl_error_code
uves_propertylist_append(uves_propertylist *self,
                        const uves_propertylist *other)
{

    const cxchar *const _id = "uves_propertylist_append";

    if (self == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    if (other != NULL) {

        uves_deque_const_iterator pos = uves_deque_begin(other->properties);

        while (pos != uves_deque_end(other->properties)) {

            const cpl_property *p = uves_deque_get(other->properties, pos);

            uves_deque_push_back(self->properties, cpl_property_duplicate(p));
            pos = uves_deque_next(other->properties, pos);

        }

    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Erase the given property from a property list.
 *
 * @param self   A property list.
 * @param name   Name of the property to erase.
 *
 * @return
 *   On success the function returns the number of erased entries. If
 *   an error occurs the function returns 0 and an appropriate error
 *   code is set.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches the property with the name @em name in the property
 * list @em self and removes it. The property is destroyed. If @em self
 * contains multiple duplicates of a property named @em name, only the
 * first one is erased.
 */

int
uves_propertylist_erase(uves_propertylist *self, const char *name)
{

    const cxchar *const _id = "uves_propertylist_erase";

    uves_deque_iterator pos;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }

    pos = _uves_propertylist_find(self, name);
    if (pos == uves_deque_end(self->properties)) {
        return 0;
    }
//        fprintf(stderr, "%d\n", __LINE__);

    uves_deque_erase(self->properties, pos, (cx_free_func)cpl_property_delete);

    return 1;

}

/**
 * @brief
 *   Erase all properties with name matching a given regular expression.
 *
 * @param self    A property list.
 * @param regexp  Regular expression.
 * @param invert  Flag inverting the sense of matching.
 *
 * @return
 *   On success the function returns the number of erased entries. If
 *   an error occurs the function returns 0 and an appropriate error
 *   code is set.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>regexp</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function searches for all the properties matching in the list
 * @em self and removes them. Whether a property matches or not depends on
 * the given regular expression @em regexp, and the flag @em invert. If
 * @em invert is @c 0, all properties matching @em regexp are removed from
 * the list. If @em invert is set to a non-zero value, all properties which
 * do not match @em regexp are erased. The removed properties are destroyed.
 *
 * The function expects POSIX 1003.2 compliant extended regular expressions.
 */

int
uves_propertylist_erase_regexp(uves_propertylist *self, const char *regexp,
                              int invert)
{

    const cxchar *const _id = "uves_propertylist_erase_regexp";

    cxint status = 0;
    cxint count = 0;

    uves_deque_iterator first, last;



    uves_regexp filter;


    if (self == NULL || regexp == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return 0;
    }


    status = regcomp(&filter.re, regexp, REG_EXTENDED | REG_NOSUB);
    if (status) {
        cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
        return 0;
    }

    filter.invert = invert == 0 ? FALSE : TRUE;

    first = uves_deque_begin(self->properties);
    last  = uves_deque_end(self->properties);

    while (first < uves_deque_end(self->properties)) {
        uves_deque_iterator pos = first;
//        first = uves_deque_next(self->properties, first);

        cpl_property* p = uves_deque_get(self->properties, pos);
        if (_uves_propertylist_compare_regexp(p, &filter) == TRUE) {

//            fprintf(stderr, "%d\n", __LINE__);

            uves_deque_erase(self->properties, pos,
                          (cx_free_func)cpl_property_delete);
            count++;
        }
        else
            first = uves_deque_next(self->properties, first);
    }

    regfree(&filter.re);

    return count;

}


/**
 * @brief
 *   Remove all properties from a property list.
 *
 * @param self  A property list.
 *
 * @return Nothing.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> is a <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function removes all properties from @em self. Each property
 * is properly deallocated. After calling this function @em self is
 * empty.
 */

void
uves_propertylist_empty(uves_propertylist *self)
{

    const cxchar *const _id = "uves_propertylist_empty";

    uves_deque_iterator first, last;


    if (self == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return;
    }

    first = uves_deque_begin(self->properties);
    last = uves_deque_end(self->properties);

    while (first < uves_deque_end(self->properties)) {
        uves_deque_iterator pos = first;
        
//        first = uves_deque_next(self->properties, first);
//        fprintf(stderr, "%d  %d %d %d\n", __LINE__, first, last, pos);
        uves_deque_erase(self->properties, pos,
                      (cx_free_func)cpl_property_delete);

//        first = uves_deque_next(self->properties, first);
    }

    return;

}


/**
 * @brief
 *   Update a property list with a character value.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The character value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The property list <i>self</i> contains a property with the
 *         name <i>name</i> which is not of type <tt>CPL_TYPE_CHAR</tt>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function updates the property list @em self with the character value
 * @em value. This means, if a property with the name @em name exists already
 * its value is updated, otherwise a property with the name @em name is
 * created and added to @em self. The update will fail if a property with
 * the name @em name exists already which is not of type @c CPL_TYPE_CHAR.
 */

cpl_error_code
uves_propertylist_update_char(uves_propertylist *self, const char *name,
                             char value)
{

    const cxchar *const _id = "uves_propertylist_update_char";

    uves_deque_iterator pos;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    pos = _uves_propertylist_find(self, name);

    if (pos == uves_deque_end(self->properties)) {

        cpl_property *property = cpl_property_new(name, CPL_TYPE_CHAR);


        cx_assert(property != NULL);

        cpl_property_set_char(property, value);
        uves_deque_push_back(self->properties, property);
    }
    else {

        cpl_property *property = uves_deque_get(self->properties, pos);


        cx_assert(property != NULL);

        if (cpl_property_get_type(property) != CPL_TYPE_CHAR) {
            cpl_error_set(_id, CPL_ERROR_TYPE_MISMATCH);
            return CPL_ERROR_TYPE_MISMATCH;
        }

        cpl_property_set_char(property, value);

    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Update a property list with a boolean value.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The boolean value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The property list <i>self</i> contains a property with the
 *         name <i>name</i> which is not of type <tt>CPL_TYPE_BOOL</tt>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function updates the property list @em self with the boolean value
 * @em value. This means, if a property with the name @em name exists already
 * its value is updated, otherwise a property with the name @em name is
 * created and added to @em self. The update will fail if a property with
 * the name @em name exists already which is not of type @c CPL_TYPE_BOOL.
 */

cpl_error_code
uves_propertylist_update_bool(uves_propertylist *self, const char *name,
                             int value)
{

    const cxchar *const _id = "uves_propertylist_update_bool";

    uves_deque_iterator pos;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    pos = _uves_propertylist_find(self, name);

    if (pos == uves_deque_end(self->properties)) {

        cpl_property *property = cpl_property_new(name, CPL_TYPE_BOOL);


        cx_assert(property != NULL);

        cpl_property_set_bool(property, value);
        uves_deque_push_back(self->properties, property);
    }
    else {

        cpl_property *property = uves_deque_get(self->properties, pos);


        cx_assert(property != NULL);

        if (cpl_property_get_type(property) != CPL_TYPE_BOOL) {
            cpl_error_set(_id, CPL_ERROR_TYPE_MISMATCH);
            return CPL_ERROR_TYPE_MISMATCH;
        }

        cpl_property_set_bool(property, value);

    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Update a property list with a integer value.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The integer value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The property list <i>self</i> contains a property with the
 *         name <i>name</i> which is not of type <tt>CPL_TYPE_INT</tt>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function updates the property list @em self with the integer value
 * @em value. This means, if a property with the name @em name exists already
 * its value is updated, otherwise a property with the name @em name is
 * created and added to @em self. The update will fail if a property with
 * the name @em name exists already which is not of type @c CPL_TYPE_INT.
 */

cpl_error_code
uves_propertylist_update_int(uves_propertylist *self, const char *name,
                            int value)
{

    const cxchar *const _id = "uves_propertylist_update_int";

    uves_deque_iterator pos;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    pos = _uves_propertylist_find(self, name);

    if (pos == uves_deque_end(self->properties)) {

        cpl_property *property = cpl_property_new(name, CPL_TYPE_INT);


        cx_assert(property != NULL);

        cpl_property_set_int(property, value);
        uves_deque_push_back(self->properties, property);
    }
    else {

        cpl_property *property = uves_deque_get(self->properties, pos);


        cx_assert(property != NULL);

        if (cpl_property_get_type(property) != CPL_TYPE_INT) {
            cpl_error_set(_id, CPL_ERROR_TYPE_MISMATCH);
            return CPL_ERROR_TYPE_MISMATCH;
        }

        cpl_property_set_int(property, value);

    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Update a property list with a long value.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The long value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The property list <i>self</i> contains a property with the
 *         name <i>name</i> which is not of type <tt>CPL_TYPE_LONG</tt>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function updates the property list @em self with the long value
 * @em value. This means, if a property with the name @em name exists already
 * its value is updated, otherwise a property with the name @em name is
 * created and added to @em self. The update will fail if a property with
 * the name @em name exists already which is not of type @c CPL_TYPE_LONG.
 */

cpl_error_code
uves_propertylist_update_long(uves_propertylist *self, const char *name,
                             long value)
{

    const cxchar *const _id = "uves_propertylist_update_long";

    uves_deque_iterator pos;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    pos = _uves_propertylist_find(self, name);

    if (pos == uves_deque_end(self->properties)) {

        cpl_property *property = cpl_property_new(name, CPL_TYPE_LONG);


        cx_assert(property != NULL);

        cpl_property_set_long(property, value);
        uves_deque_push_back(self->properties, property);
    }
    else {

        cpl_property *property = uves_deque_get(self->properties, pos);


        cx_assert(property != NULL);

        if (cpl_property_get_type(property) != CPL_TYPE_LONG) {
            cpl_error_set(_id, CPL_ERROR_TYPE_MISMATCH);
            return CPL_ERROR_TYPE_MISMATCH;
        }

        cpl_property_set_long(property, value);

    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Update a property list with a float value.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The float value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The property list <i>self</i> contains a property with the
 *         name <i>name</i> which is not of type <tt>CPL_TYPE_FLOAT</tt>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function updates the property list @em self with the float value
 * @em value. This means, if a property with the name @em name exists already
 * its value is updated, otherwise a property with the name @em name is
 * created and added to @em self. The update will fail if a property with
 * the name @em name exists already which is not of type @c CPL_TYPE_FLOAT.
 */

cpl_error_code
uves_propertylist_update_float(uves_propertylist *self, const char *name,
                              float value)
{

    const cxchar *const _id = "uves_propertylist_update_float";

    uves_deque_iterator pos;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    pos = _uves_propertylist_find(self, name);

    if (pos == uves_deque_end(self->properties)) {

        cpl_property *property = cpl_property_new(name, CPL_TYPE_FLOAT);


        cx_assert(property != NULL);

        cpl_property_set_float(property, value);
        uves_deque_push_back(self->properties, property);
    }
    else {

        cpl_property *property = uves_deque_get(self->properties, pos);


        cx_assert(property != NULL);

        if (cpl_property_get_type(property) != CPL_TYPE_FLOAT) {
            cpl_error_set(_id, CPL_ERROR_TYPE_MISMATCH);
            return CPL_ERROR_TYPE_MISMATCH;
        }

        cpl_property_set_float(property, value);

    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Update a property list with a double value.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The double value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The property list <i>self</i> contains a property with the
 *         name <i>name</i> which is not of type <tt>CPL_TYPE_DOUBLE</tt>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function updates the property list @em self with the double value
 * @em value. This means, if a property with the name @em name exists already
 * its value is updated, otherwise a property with the name @em name is
 * created and added to @em self. The update will fail if a property with
 * the name @em name exists already which is not of type @c CPL_TYPE_DOUBLE.
 */

cpl_error_code
uves_propertylist_update_double(uves_propertylist *self, const char *name,
                               double value)
{

    const cxchar *const _id = "uves_propertylist_update_double";

    uves_deque_iterator pos;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    pos = _uves_propertylist_find(self, name);

    if (pos == uves_deque_end(self->properties)) {

        cpl_property *property = cpl_property_new(name, CPL_TYPE_DOUBLE);


        cx_assert(property != NULL);

        cpl_property_set_double(property, value);
        uves_deque_push_back(self->properties, property);
    }
    else {

        cpl_property *property = uves_deque_get(self->properties, pos);


        cx_assert(property != NULL);

        if (cpl_property_get_type(property) != CPL_TYPE_DOUBLE) {
            cpl_error_set(_id, CPL_ERROR_TYPE_MISMATCH);
            return CPL_ERROR_TYPE_MISMATCH;
        }

        cpl_property_set_double(property, value);

    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Update a property list with a string value.
 *
 * @param self   A property list.
 * @param name   The property name to be assigned to the value.
 * @param value  The string value to store.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i> or <i>name</i> is a <tt>NULL</tt>
 *         pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The property list <i>self</i> contains a property with the
 *         name <i>name</i> which is not of type <tt>CPL_TYPE_STRING</tt>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function updates the property list @em self with the string value
 * @em value. This means, if a property with the name @em name exists already
 * its value is updated, otherwise a property with the name @em name is
 * created and added to @em self. The update will fail if a property with
 * the name @em name exists already which is not of type @c CPL_TYPE_STRING.
 */

cpl_error_code
uves_propertylist_update_string(uves_propertylist *self, const char *name,
                               const char *value)
{

    const cxchar *const _id = "uves_propertylist_update_string";

    uves_deque_iterator pos;


    if (self == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    pos = _uves_propertylist_find(self, name);

    if (pos == uves_deque_end(self->properties)) {

        cpl_property *property = cpl_property_new(name, CPL_TYPE_STRING);


        cx_assert(property != NULL);

        cpl_property_set_string(property, value);
        uves_deque_push_back(self->properties, property);
    }
    else {

        cpl_property *property = uves_deque_get(self->properties, pos);


        cx_assert(property != NULL);

        if (cpl_property_get_type(property) != CPL_TYPE_STRING) {
            cpl_error_set(_id, CPL_ERROR_TYPE_MISMATCH);
            return CPL_ERROR_TYPE_MISMATCH;
        }

        cpl_property_set_string(property, value);

    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Copy a property from another property list.
 *
 * @param self   A property list.
 * @param other  The property list from which a property is copied.
 * @param name   The name of the property to copy.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>other</i> or <i>name</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The property list <i>other</i> does not contain a property with the
 *         name <i>name</i>.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The property list <i>self</i> contains a property with the
 *         name <i>name</i> which is not of the same type as the property
 *         which should be copied from <i>other</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function copies the property @em name from the property list
 * @em other to the property list @em self. If the property list @em self
 * does not already contain a property @em name the property is appended
 * to @em self. If a property @em name exists already in @em self the
 * function overwrites the contents of this property if and only if this
 * property is of the same type as the property to be copied from @em other.
 */

cpl_error_code
uves_propertylist_copy_property(uves_propertylist *self,
                               const uves_propertylist *other,
                               const char *name)
{

    const cxchar *const _id = "uves_propertylist_copy_property";

    uves_deque_iterator spos;
    uves_deque_iterator tpos;


    if (self == NULL || other == NULL || name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    spos = _uves_propertylist_find(other, name);

    if (spos == uves_deque_end(other->properties)) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return CPL_ERROR_DATA_NOT_FOUND;
    }

    tpos = _uves_propertylist_find(self, name);

    if (tpos == uves_deque_end(self->properties)) {

        cpl_property *p = cpl_property_duplicate(uves_deque_get(other->properties,
                                                             spos));
        uves_deque_push_back(self->properties, p);

    }
    else {

        cpl_property *p = uves_deque_get(self->properties, tpos);
        cpl_property *_p = uves_deque_get(self->properties, spos);


        if (cpl_property_get_type(p) != cpl_property_get_type(_p)) {
            cpl_error_set(_id, CPL_ERROR_TYPE_MISMATCH);
            return CPL_ERROR_TYPE_MISMATCH;
        }

        switch (cpl_property_get_type(_p)) {

        case CPL_TYPE_CHAR:
            cpl_property_set_char(p, cpl_property_get_char(_p));
            break;

        case CPL_TYPE_BOOL:
            cpl_property_set_bool(p, cpl_property_get_bool(_p));
            break;

        case CPL_TYPE_INT:
            cpl_property_set_int(p, cpl_property_get_int(_p));
            break;

        case CPL_TYPE_LONG:
            cpl_property_set_long(p, cpl_property_get_long(_p));
            break;

        case CPL_TYPE_FLOAT:
            cpl_property_set_float(p, cpl_property_get_float(_p));
            break;

        case CPL_TYPE_DOUBLE:
            cpl_property_set_double(p, cpl_property_get_double(_p));
            break;

        case CPL_TYPE_STRING:
            cpl_property_set_string(p, cpl_property_get_string(_p));
            break;

        default:
            /* This point should never be reached */
            cx_error("%s: Unsupported type encountered!", CX_CODE_POS);
            break;

        }

        cpl_property_set_comment(p, cpl_property_get_comment(_p));

    }

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Copy matching properties from another property list.
 *
 * @param self    A property list.
 * @param other   The property list from which a property is copied.
 * @param regexp  The regular expression used to select properties.
 * @param invert  Flag inverting the sense of matching.
 *
 * @return
 *   The function returns @c CPL_ERROR_NONE on success or a CPL error
 *   code otherwise.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>self</i>, <i>other</i> or <i>regexp</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_ILLEGAL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>regexp</i> is an invalid regular expression.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_TYPE_MISMATCH</td>
 *       <td class="ecr">
 *         The property list <i>self</i> contains a property with the
 *         name <i>name</i> which is not of the same type as the property
 *         which should be copied from <i>other</i>.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function copies all properties with matching names from the property
 * list @em other to the property list @em self. If the flag @em invert is
 * zero, all properties whose names match the regular expression @em regexp
 * are copied. If @em invert is set to a non-zero value, all properties with
 * names not matching @em regexp are copied rather. The function expects
 * POSIX 1003.2 compliant extended regular expressions.
 *
 * If the property list @em self does not already contain one of the
 * properties to be copied this property is appended to @em self. If a
 * property to be copied exists already in @em self the function overwrites
 * the contents of this property.
 *
 * Before properties are copied from the property list @em other to @em self
 * the types of the properties are checked and if any type mismatch is
 * detected the function stops processing immediately. The property list
 * @em self is not at all modified in this case.
 *
 * @see uves_propertylist_copy_property()
 */

cpl_error_code
uves_propertylist_copy_property_regexp(uves_propertylist *self,
                                      const uves_propertylist *other,
                                      const char *regexp,
                                      int invert)
{

    const cxchar *const _id = "uves_propertylist_copy_property_regexp";

    cxint status;

    cxsize i;
    cxsize count = 0;

    uves_deque_const_iterator first, last;

    typedef struct _property_pair_ {
        cpl_property *s;
        cpl_property *t;
    } property_pair;

    property_pair *pairs = NULL;

    uves_regexp filter;


    if (self == NULL || other == NULL || regexp == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return CPL_ERROR_NULL_INPUT;
    }

    status = regcomp(&filter.re, regexp, REG_EXTENDED | REG_NOSUB);
    if (status) {
        cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
        return CPL_ERROR_ILLEGAL_INPUT;
    }

    filter.invert = invert == 0 ? FALSE : TRUE;


    count = uves_deque_size(other->properties);

    if (count == 0) {
        regfree(&filter.re);
        return CPL_ERROR_NONE;
    }

    pairs = cx_malloc(count * sizeof(property_pair));
    cx_assert(pairs != NULL);

    count = 0;


    first = uves_deque_begin(other->properties);
    last  = uves_deque_end(other->properties);

    while (first != last) {

        cpl_property *p = uves_deque_get(other->properties, first);


        if (_uves_propertylist_compare_regexp(p, &filter) == TRUE) {

            const cxchar *name = cpl_property_get_name(p);

            uves_deque_const_iterator pos = _uves_propertylist_find(self, name);

            cpl_property *_p = NULL;


            if (pos != uves_deque_end(self->properties)) {

                _p = uves_deque_get(self->properties, pos);

                if (cpl_property_get_type(p) != cpl_property_get_type(_p)) {

                    regfree(&filter.re);

                    cx_free(pairs);
                    pairs = NULL;

                    cpl_error_set(_id, CPL_ERROR_TYPE_MISMATCH);

                    return CPL_ERROR_TYPE_MISMATCH;

                }

            }

            pairs[count].s = p;
            pairs[count].t = _p;
            ++count;

        }

        first = uves_deque_next(other->properties, first);

    }

    regfree(&filter.re);


    for (i = 0; i < count; i++) {

        if (pairs[i].t == NULL) {

            cpl_property *p = cpl_property_duplicate(pairs[i].s);
            uves_deque_push_back(self->properties, p);

        }
        else {

            switch (cpl_property_get_type(pairs[i].s)) {

            case CPL_TYPE_CHAR:
                cpl_property_set_char(pairs[i].t,
                                      cpl_property_get_char(pairs[i].s));
                break;

            case CPL_TYPE_BOOL:
                cpl_property_set_bool(pairs[i].t,
                                      cpl_property_get_bool(pairs[i].s));
                break;

            case CPL_TYPE_INT:
                cpl_property_set_int(pairs[i].t,
                                     cpl_property_get_int(pairs[i].s));
                break;

            case CPL_TYPE_LONG:
                cpl_property_set_long(pairs[i].t,
                                      cpl_property_get_long(pairs[i].s));
                break;

            case CPL_TYPE_FLOAT:
                cpl_property_set_float(pairs[i].t,
                                       cpl_property_get_float(pairs[i].s));
                break;

            case CPL_TYPE_DOUBLE:
                cpl_property_set_double(pairs[i].t,
                                        cpl_property_get_double(pairs[i].s));
                break;

            case CPL_TYPE_STRING:
                cpl_property_set_string(pairs[i].t,
                                        cpl_property_get_string(pairs[i].s));
                break;

            default:
                /* This point should never be reached */
                cx_free(pairs);
                cx_error("%s: Unsupported type encountered!", CX_CODE_POS);
                break;

            }

        }

    }

    cx_free(pairs);

    return CPL_ERROR_NONE;

}


/**
 * @brief
 *   Create a property list from a file.
 *
 * @param name      Name of the input file.
 * @param position  Index of the data set to read.
 *
 * @return
 *   The function returns the newly created property list or @c NULL if an
 *   error occurred.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>name</i> is a <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_ILLEGAL_INPUT</td>
 *       <td class="ecr">
 *         The <i>position</i> is less than 0 or the properties cannot be
 *         read from the file <i>name</i>.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_FILE_IO</td>
 *       <td class="ecr">
 *         The file <i>name</i> does not exist.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_BAD_FILE_FORMAT</td>
 *       <td class="ecr">
 *         The file <i>name</i> is not a valid FITS file.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The requested data set at index <i>position</i> does not exist.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function reads the properties of the data set with index @em position
 * from the file @em name.
 *
 * Currently only the FITS file format is supported. The property list is
 * created by reading the FITS keywords from extension @em position. The
 * numbering of the data sections starts from 0.
 * When creating the property list from a FITS header, any keyword without
 * a value like undefined keywords for instance are not transformed into
 * a property.
 *
 * @see uves_propertylist_load_regexp()
 */

uves_propertylist *
uves_propertylist_load(const char *name, int position)
{

    const cxchar *const _id = "uves_propertylist_load";

    register cxint n, status;

    qfits_header *header;

    uves_propertylist *self;


    if (name == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return NULL;
    }

    if (position < 0) {
        cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
        return NULL;
    }

    status = qfits_is_fits((cxchar *)name);
    if (status == -1) {
        cpl_error_set(_id, CPL_ERROR_FILE_IO);
        return NULL;
    }
    else {
        if (status == 0) {
            cpl_error_set(_id, CPL_ERROR_BAD_FILE_FORMAT);
            return NULL;
        }
    }

    /*
     * qfits_query_n_ext() only counts true extensions, i.e. it does not
     * count the primary FITS unit. But since we passed the qfits_is_fits()
     * check we can safely assume that there is one.
     */

    n = qfits_query_n_ext((cxchar *)name);

    if (n < position) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return NULL;
    }


    header = qfits_header_readext((cxchar *)name, position);

    if (header == NULL) {
        cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
        return NULL;
    }

    self = uves_propertylist_new();
    cx_assert(self);

    status = _uves_propertylist_from_fits(self, header, NULL, NULL);

    if (status) {
        uves_propertylist_delete(self);
        qfits_header_destroy(header);

        cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
        return NULL;
    }

    qfits_header_destroy(header);

    return self;

}


/**
 * @brief
 *   Create a filtered property list from a file.
 *
 * @param name      Name of the input file.
 * @param position  Index of the data set to read.
 * @param regexp    Regular expression used to filter properties.
 * @param invert    Flag inverting the sense of matching property names.
 *
 * @return
 *   The function returns the newly created property list or @c NULL if an
 *   error occurred.
 *
 * @error
 *   <table class="ec" align="center">
 *     <tr>
 *       <td class="ecl">CPL_ERROR_NULL_INPUT</td>
 *       <td class="ecr">
 *         The parameter <i>name</i> or the parameter <i>regexp</i> is a
 *         <tt>NULL</tt> pointer.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_ILLEGAL_INPUT</td>
 *       <td class="ecr">
 *         The <i>position</i> is less than 0, the properties cannot be
 *         read from the file <i>name</i>, or <i>regexp</i> is not a valid
*          extended regular expression.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_FILE_IO</td>
 *       <td class="ecr">
 *         The file <i>name</i> does not exist.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_BAD_FILE_FORMAT</td>
 *       <td class="ecr">
 *         The file <i>name</i> is not a valid FITS file.
 *       </td>
 *     </tr>
 *     <tr>
 *       <td class="ecl">CPL_ERROR_DATA_NOT_FOUND</td>
 *       <td class="ecr">
 *         The requested data set at index <i>position</i> does not exist.
 *       </td>
 *     </tr>
 *   </table>
 * @enderror
 *
 * The function reads all properties of the data set with index @em position
 * with matching names from the file @em name. If the flag @em invert is zero,
 * all properties whose names match the regular expression @em regexp are
 * read. If @em invert is set to a non-zero value, all properties with
 * names not matching @em regexp are read rather. The function expects
 * POSIX 1003.2 compliant extended regular expressions.
 *
 * Currently only the FITS file format is supported. The property list is
 * created by reading the FITS keywords from extension @em position.
 * The numbering of the data sections starts from 0.
 *
 * When creating the property list from a FITS header, any keyword without
 * a value like undefined keywords for instance are not transformed into
 * a property. FITS format specific keyword prefixes (e.g. @c HIERARCH) must
 * not be part of the given pattern string @em regexp, but only the actual
 * FITS keyword name may be given.
 *
 * @see uves_propertylist_load()
 */

uves_propertylist *
uves_propertylist_load_regexp(const char *name, int position,
                             const char *regexp, int invert)
{

    const cxchar *const _id = "uves_propertylist_load_regexp";

    register cxint n, status;

    qfits_header *header;

    uves_propertylist *self;

    uves_regexp filter;


    if (name == NULL || regexp == NULL) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return NULL;
    }

    if (position < 0) {
        cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
        return NULL;
    }

    status = regcomp(&filter.re, regexp, REG_EXTENDED | REG_NOSUB);
    if (status) {
        cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
        return NULL;
    }

    filter.invert = invert == 0 ? FALSE : TRUE;


    status = qfits_is_fits((cxchar *)name);
    if (status == -1) {
        cpl_error_set(_id, CPL_ERROR_FILE_IO);
        return NULL;
    }
    else {
        if (status == 0) {
            cpl_error_set(_id, CPL_ERROR_BAD_FILE_FORMAT);
            return NULL;
        }
    }

    /*
     * qfits_query_n_ext() only counts true extensions, i.e. it does not
     * count the primary FITS unit. But since we passed the qfits_is_fits()
     * check we can safely assume that there is one.
     */

    n = qfits_query_n_ext((cxchar *)name);

    if (n < position) {
        cpl_error_set(_id, CPL_ERROR_DATA_NOT_FOUND);
        return NULL;
    }


    header = qfits_header_readext((cxchar *)name, position);

    if (header == NULL) {
        cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
        return NULL;
    }

    self = uves_propertylist_new();
    cx_assert(self);

    status = _uves_propertylist_from_fits(self, header,
                                         _uves_propertylist_filter_regexp,
                                         &filter);

    if (status) {
        uves_propertylist_delete(self);
        qfits_header_destroy(header);

        regfree(&filter.re);

        cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
        return NULL;
    }

    qfits_header_destroy(header);
    regfree(&filter.re);

    return self;

}


/**
 * @internal
 * @brief
 *   Convert a property list to a FITS header.
 *
 * @param self  Property list to be saved.
 *
 * @return The function returns the created qfits header, or @c NULL
 *   in case of an error.
 *
 * The function  converts the properties from @em self to valid FITS
 * keywords and puts them into a @b qfits_header structure.
 *
 * If a property cannot be converted into a valid FITS keyword the
 * function fails.
 */

qfits_header *
uves_propertylist_to_fits(const uves_propertylist *self)
{
    const cxchar *const _id = "uves_propertylist_to_fits";

    qfits_header *header;


    cx_assert(self != NULL);

    header = qfits_header_new();

    if (!uves_deque_empty(self->properties)) {
        uves_deque_iterator i = uves_deque_begin(self->properties);
        uves_deque_iterator last = uves_deque_end(self->properties);

        while (i != last) {
            cxchar tmp[FITS_LINESZ + 1];
            cxchar key[FITS_LINESZ + 1];
            cxchar value[FITS_LINESZ + 1];
            cxfloat fval;
            cxdouble dval;
            cpl_property *property;

            property = uves_deque_get(self->properties, i);

            /*
             * Convert each property into a FITS keyword an error is
             * triggered for unsupported types. Also, since the FITS
             * format does not support array keywords, apart from strings,
             * i.e. character arrays, an error is triggered in this case too.
             *
             * A possible solution for the array case would be to create
             * a sequence of indexed keywords. But this must be checked.
             */

            strncpy(tmp, cpl_property_get_name(property), FITS_LINESZ);
            tmp[FITS_LINESZ] = '\0';

            if (!cx_strupper(tmp)) {
                cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);

                qfits_header_destroy(header);

                return NULL;
            }

            key[0] = '\0';

            if (strlen(tmp) > FITS_STDKEY_MAX &&
                strncmp(tmp, "HIERARCH ", 9)) {
                strncat(key, "HIERARCH ", 9);
            }
            strncat(key, tmp, FITS_LINESZ - strlen(key));

            switch (cpl_property_get_type(property)) {
                case CPL_TYPE_CHAR:
                    cx_snprintf(value, FITS_LINESZ, "'%c'",
                                cpl_property_get_char(property));
                    break;

                case CPL_TYPE_BOOL:
                {
                    cxint pvalue = cpl_property_get_bool(property);

                    cx_snprintf(value, FITS_LINESZ, "%c",
                                pvalue == 1 ? 'T' : 'F');
                    break;
                }

                case CPL_TYPE_INT:
                    cx_snprintf(value, FITS_LINESZ, "%d",
                                cpl_property_get_int(property));
                    break;

                case CPL_TYPE_LONG:
                    cx_snprintf(value, FITS_LINESZ, "%ld",
                                cpl_property_get_long(property));
                    break;

                case CPL_TYPE_FLOAT:
                    fval = cpl_property_get_float(property);
                    cx_snprintf(value, FITS_LINESZ, "%.7G", fval);

                    if (!strchr(value, '.')) {
                        if (strchr(value, 'E'))
                            cx_snprintf(value, FITS_LINESZ, "%.1E", fval);
                        else
                            strcat(value, ".");
                    }
                    break;

                case CPL_TYPE_DOUBLE:
                    dval = cpl_property_get_double(property);
                    cx_snprintf(value, FITS_LINESZ, "%.15G", dval);

                    if (!strchr(value, '.')) {
                        if (strchr(value, 'E'))
                            cx_snprintf(value, FITS_LINESZ, "%.1E", dval);
                        else
                            strcat(value, ".");
                    }
                    break;

                case CPL_TYPE_STRING:
                    if (!strcmp(key, "COMMENT") || !strcmp(key, "HISTORY")) {
                        cx_snprintf(value, FITS_LINESZ, "%s",
                                    cpl_property_get_string(property));
                    }
                    else {

                        cxint n = 0;

                        n = cx_snprintf(value, FITS_SVALUE_MAX + 1, "'%s'",
                                        cpl_property_get_string(property));

                        if (n > FITS_SVALUE_MAX) {
                            value[FITS_SVALUE_MAX - 1] = '\'';
                            value[FITS_SVALUE_MAX] = '\0';
                        }

                    }
                    break;

                default:
                    cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);

                    qfits_header_destroy(header);

                    return NULL;
                    break;
            }

            qfits_header_append(header, key, value,
                                (cxchar *)cpl_property_get_comment(property),
                                NULL);

            i = uves_deque_next(self->properties, i);
        }
    }


    /*
     * Add the END keyword as last entry.
     */

    /* FIXME: Maybe better to check if end is already present */

    qfits_header_append(header, "END", NULL, NULL, NULL);


    /*
     * Sort the header according to ESO's DICB standard
     */

    if (qfits_header_sort(&header) != 0) {
        cpl_error_set(_id, CPL_ERROR_INCOMPATIBLE_INPUT);

        qfits_header_destroy(header);

        return NULL;
    }

    return header;

}

/**
 * @internal
 * @brief
 *   Create a property list from a FITS header.
 *
 * @param header  FITS header to be converted.
 *
 * @return The function returns the created property list, or @c NULL
 *   in case of an error.
 *
 * The function  converts the FITS keywords from @em header into properties
 * and puts them into a property list.
 *
 * The FITS header @em header is left untouched, i.e. the conversion is
 * non-destructive. The special FITS keyword END indicating the end of
 * a FITS header is not transformed into a property, but simply ignored.
 *
 * In case of an error, an appropriate error code is set. If a FITS header
 * card cannot be parsed the error code is set to @c CPL_ERROR_ILLEGAL_INPUT
 * or to @c CPL_ERROR_INVALID_TYPE if a FITS keyword type is not supported.
 * If @em header is @c NULL the error code is set to @c CPL_ERROR_NULL_INPUT.
 */

uves_propertylist *
uves_propertylist_from_fits(const qfits_header *header)
{

    const cxchar *const _id = "uves_propertylist_from_fits";

    register cxint status;

    uves_propertylist *self;


    if (!header) {
        cpl_error_set(_id, CPL_ERROR_NULL_INPUT);
        return NULL;
    }

    self = uves_propertylist_new();
    cx_assert(self != NULL);

    status = _uves_propertylist_from_fits(self, header, NULL, NULL);

    if (status) {
        uves_propertylist_delete(self);

//        cpl_error_set_where(_id);

        switch (status) {
        case -2:
        case -1:
            cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT);
            break;

        case 1:
            cpl_error_set(_id, CPL_ERROR_INVALID_TYPE);
            break;

        default:
            /* This should never be reached */
            break;
        }

        return NULL;
    }

    return self;

}

#endif /* USE_CPL */
/**@}*/
