/* $Id: uves_propertylist-test.c,v 1.3 2009-06-05 05:49:02 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: 2009-06-05 05:49:02 $
 * $Revision: 1.3 $
 * $Name: not supported by cvs2svn $
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#undef CX_DISABLE_ASSERT
#undef CX_LOG_DOMAIN


#include <uves_propertylist.h>

#include <cpl_test.h>
//#include "cpl_init.h"
//#include "cpl_error.h"
//#include "uves_propertylist_impl.h"

#include <cpl.h>
#include <cxmemory.h>
#include <cxmessages.h>
#include <qfits.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define FLT_EPS  1.0e-6
#define DBL_EPS  1.0e-14


static void
test_property_dump(cpl_property *property)
{

    const cxchar *name = cpl_property_get_name(property);
    const cxchar *comment = cpl_property_get_comment(property);

    cxchar c;

    long size = cpl_property_get_size(property);

    cpl_type type = cpl_property_get_type(property);


    fprintf(stderr, "Property at address %p\n", (void *)property);
    fprintf(stderr, "\tname   : %p '%s'\n", (void *)name, name);
    fprintf(stderr, "\tcomment: %p '%s'\n", (void *)comment, comment);
    fprintf(stderr, "\ttype   : %#09x\n", type);
    fprintf(stderr, "\tsize   : %ld\n", size);
    fprintf(stderr, "\tvalue  : ");


    switch (type) {
    case CPL_TYPE_CHAR:
        c = cpl_property_get_char(property);
        if (!c)
            fprintf(stderr, "''");
        else
            fprintf(stderr, "'%c'", c);
        break;

    case CPL_TYPE_BOOL:
        fprintf(stderr, "%d", cpl_property_get_bool(property));
        break;

    case CPL_TYPE_INT:
        fprintf(stderr, "%d", cpl_property_get_int(property));
        break;

    case CPL_TYPE_LONG:
        fprintf(stderr, "%ld", cpl_property_get_long(property));
        break;

    case CPL_TYPE_FLOAT:
        fprintf(stderr, "%.7g", cpl_property_get_float(property));
        break;

    case CPL_TYPE_DOUBLE:
        fprintf(stderr, "%.15g", cpl_property_get_double(property));
        break;

    case CPL_TYPE_STRING:
        fprintf(stderr, "'%s'", cpl_property_get_string(property));
        break;

    default:
        fprintf(stderr, "unknown.");
        break;

    }

    fprintf(stderr, "\n");

    return;

}


static void
test_plist_dump(uves_propertylist *plist)
{

    cxlong i;
    cxlong sz = uves_propertylist_get_size(plist);


    fprintf(stderr, "Property list at address %p:\n", (void *) plist);

    for (i = 0; i < sz; i++) {
        cpl_property *p = uves_propertylist_get(plist, i);
        test_property_dump(p);
    }

    return;

}

static int test_main(void)
{

    const cxchar *keys[] = {
        "a", "b", "c", "d", "e", "f", "g",
        "A", "B", "C", "D", "E", "F", "G"
    };

    const cxchar *comments[] = {
        "A character value",
        "A boolean value",
        "A integer value",
        "A long integer value",
        "A floating point number",
        "A double precision number",
        "A string value",
    };

    cpl_type types[] = {
        CPL_TYPE_CHAR,
        CPL_TYPE_BOOL,
        CPL_TYPE_INT,
        CPL_TYPE_LONG,
        CPL_TYPE_FLOAT,
        CPL_TYPE_DOUBLE,
        CPL_TYPE_STRING
    };

    cxlong i;

    cxfloat fval[] = {-1.23456789, 0.};
    cxdouble dval[] = {-1.23456789, 0.};

    uves_propertylist *plist, *_plist;

    qfits_header *header, *_header;

    struct fcard {
        const cxchar *key;
        const cxchar *val;
        const cxchar *com;
        cpl_type type;
    };

    struct fcard hdr[] = {
        {"SIMPLE", "T",
         "Standard FITS format (NOST-100.0)",
         CPL_TYPE_BOOL},
        {"BITPIX", "16",
         "# of bits storing pix values",
         CPL_TYPE_INT},
        {"NAXIS", "2",
         "# of axes in frame",
         CPL_TYPE_INT},
        {"NAXIS1", "2148",
         "# pixels/axis",
         CPL_TYPE_INT},
        {"NAXIS2", "2340",
         "# pixels/axis",
         CPL_TYPE_INT},
        {"ORIGIN", "ESO",
         "European Southern Observatory",
         CPL_TYPE_STRING},
        {"DATE", "2002-03-08T04:27:21.420",
         "Date this file was written (dd/mm/yyyy)",
         CPL_TYPE_STRING},
        {"MJD-OBS", "52341.17813019",
         "Obs start 2002-03-08T04:16:30.448",
         CPL_TYPE_DOUBLE},
        {"DATE-OBS", "2002-03-08T04:16:30.448",
         "Date of observation",
         CPL_TYPE_STRING},
        {"EXPTIME", "600.000",
         "Total integration time. 00:10:00.000",
         CPL_TYPE_DOUBLE},
        {"TELESCOP", "VLT",
         "ESO <TEL>",
         CPL_TYPE_STRING},
        {"RA", "181.41734",
         "12:05:40.1 RA (J2000) pointing",
         CPL_TYPE_DOUBLE},
        {"DEC", "-7.65555",
         "-07:39:19.9  DEC (J2000) pointing",
         CPL_TYPE_DOUBLE},
        {"EQUINOX", "2000.",
         "Standard FK5 (years)",
         CPL_TYPE_DOUBLE},
        {"RADECSYS", "FK5",
         "Coordinate reference frame",
         CPL_TYPE_STRING},
        {"LST", "38309.370",
         "10:38:29.370 LST at start",
         CPL_TYPE_DOUBLE},
        {"UTC", "15438.000",
         "04:17:18.000 UT at start",
         CPL_TYPE_DOUBLE},
        {"OBSERVER", "UNKNOWN",
         "Name of observer",
         CPL_TYPE_STRING},
        {"INSTRUME", "UNKNOWN",
         "Instrument used",
         CPL_TYPE_STRING},
        {"PI-COI", "'555555555'",
         "Name of PI and COI",
         CPL_TYPE_STRING},
        {"OBJECT", "None",
         "Original target",
         CPL_TYPE_STRING},
        {"PCOUNT", "0",
         "Number of parameters per group",
         CPL_TYPE_INT},
        {"GCOUNT", "1",
         "Number of groups",
         CPL_TYPE_INT},
        {"CRVAL1", "181.41734",
         "12:05:40.1, RA at ref pixel",
         CPL_TYPE_DOUBLE},
        {"CRPIX1", "2341.8585366",
         "Reference pixel in X",
         CPL_TYPE_DOUBLE},
        {"CDELT1", "0.20500000",
         "SS arcsec per pixel in RA",
         CPL_TYPE_DOUBLE},
        {"CTYPE1", "RA---TAN",
         "pixel coordinate system",
         CPL_TYPE_STRING},
        {"CRVAL2", "-7.65555",
         "-07:39:19.9, DEC at ref pixel",
         CPL_TYPE_DOUBLE},
        {"CRPIX2", "2487.8585366",
         "Reference pixel in Y",
         CPL_TYPE_DOUBLE},
        {"CDELT2", "0.20500000",
         "SS arcsec per pixel in DEC",
         CPL_TYPE_DOUBLE},
        {"CTYPE2", "DEC--TAN",
         "pixel coordinate system",
         CPL_TYPE_STRING},
        {"BSCALE", "1.0",
         "pixel=FITS*BSCALE+BZERO",
         CPL_TYPE_DOUBLE},
        {"BZERO", "32768.0",
         "pixel=FITS*BSCALE+BZERO",
         CPL_TYPE_DOUBLE},
        {"CD1_1", "0.000057",
         "Translation matrix element",
         CPL_TYPE_DOUBLE},
        {"CD1_2", "0.000000",
         "Translation matrix element",
         CPL_TYPE_DOUBLE},
        {"CD2_1", "0.000000",
         "Translation matrix element",
         CPL_TYPE_DOUBLE},
        {"CD2_2", "0.000057",
         "Translation matrix element",
         CPL_TYPE_DOUBLE},
        {"HIERARCH ESO OBS DID", "ESO-VLT-DIC.OBS-1.7",
         "OBS Dictionary",
         CPL_TYPE_STRING},
        {"HIERARCH ESO OBS OBSERVER", "UNKNOWN",
         "Observer Name",
         CPL_TYPE_STRING},
        {"HIERARCH ESO OBS PI-COI NAME", "UNKNOWN",
         "PI-COI name",
         CPL_TYPE_STRING},
        {"HIERARCH ESO INS GRAT NAME", "HR",
         "Grating name",
         CPL_TYPE_STRING},
        {"HIERARCH ESO PRO CATG", "X",
         "Product category",
         CPL_TYPE_STRING},
        {"HIERARCH ESO TPL NEXP", "5",
         "Number of exposures",
         CPL_TYPE_INT},
        {"HISTORY", "1st history record", NULL, CPL_TYPE_STRING},
        {"COMMENT", "1st comment record", NULL, CPL_TYPE_STRING},
        {"HISTORY", "2st history record", NULL, CPL_TYPE_STRING},
        {"COMMENT", "2st comment record", NULL, CPL_TYPE_STRING},
        {"COMMENT", "3st comment record", NULL, CPL_TYPE_STRING},
        {"HISTORY", "3st history record", NULL, CPL_TYPE_STRING},
        {"END", NULL, NULL, CPL_TYPE_STRING}
    };

    FILE *file;

    const cxchar *longname = "0123456789012345678901234567890123456789"
        "0123456789012345678901234567890123456789";


    /*
     * Test 1: Create a property list and check its validity.
     */

    plist = uves_propertylist_new();

    cx_assert(plist != NULL);
    cx_assert(uves_propertylist_is_empty(plist));
    cx_assert(uves_propertylist_get_size(plist) == 0);


    /*
     * Test 2: Append properties to the list created in the previous test
     *         and verify the data.
     */

    uves_propertylist_append_char(plist, keys[0], 'a');
    uves_propertylist_set_comment(plist, keys[0], comments[0]);

    uves_propertylist_append_bool(plist, keys[1], 1);
    uves_propertylist_set_comment(plist, keys[1], comments[1]);

    uves_propertylist_append_int(plist, keys[2], -1);
    uves_propertylist_set_comment(plist, keys[2], comments[2]);

    uves_propertylist_append_long(plist, keys[3], 32768);
    uves_propertylist_set_comment(plist, keys[3], comments[3]);

    uves_propertylist_append_float(plist, keys[4], fval[0]);
    uves_propertylist_set_comment(plist, keys[4], comments[4]);

    uves_propertylist_append_double(plist, keys[5], dval[0]);
    uves_propertylist_set_comment(plist, keys[5], comments[5]);

    uves_propertylist_append_string(plist, keys[6], comments[6]);
    uves_propertylist_set_comment(plist, keys[6], comments[6]);

    cx_assert(!uves_propertylist_is_empty(plist));
    cx_assert(uves_propertylist_get_size(plist) == 7);


    for (i = 0; i < uves_propertylist_get_size(plist); i++) {
        cpl_property *p = uves_propertylist_get(plist, i);

        cx_assert(!strcmp(cpl_property_get_name(p), keys[i]));
        cx_assert(!strcmp(cpl_property_get_comment(p), comments[i]));
        cx_assert(cpl_property_get_type(p) == types[i]);

        cx_assert(uves_propertylist_contains(plist, keys[i]));
        cx_assert(!strcmp(uves_propertylist_get_comment(plist, keys[i]),
                          comments[i]));
        cx_assert(uves_propertylist_get_type(plist, keys[i]) == types[i]);
    }

    cx_assert(uves_propertylist_get_char(plist, keys[0]) == 'a');
    cx_assert(uves_propertylist_get_bool(plist, keys[1]) == 1);
    cx_assert(uves_propertylist_get_int(plist, keys[2]) == -1);
    cx_assert(uves_propertylist_get_long(plist, keys[3]) == 32768);

    fval[1] = uves_propertylist_get_float(plist, keys[4]);
    cx_assert(!memcmp(&fval[0], &fval[1], sizeof(float)));

    dval[1] = uves_propertylist_get_double(plist, keys[5]);
    cx_assert(!memcmp(&dval[0], &dval[1], sizeof(double)));

    cx_assert(!strcmp(uves_propertylist_get_string(plist, keys[6]),
                      comments[6]));


    /*
     * Test 3: Modify the values of the property list entries
     *         and verify the data.
     */

    cx_assert(uves_propertylist_set_char(plist, keys[0], 'b') == 0);
    cx_assert(uves_propertylist_get_char(plist, keys[0]) == 'b');

    cx_assert(uves_propertylist_set_bool(plist, keys[1], 0) == 0);
    cx_assert(uves_propertylist_get_bool(plist, keys[1]) == 0);

    cx_assert(uves_propertylist_set_int(plist, keys[2], -1) == 0);
    cx_assert(uves_propertylist_get_int(plist, keys[2]) == -1);

    cx_assert(uves_propertylist_set_long(plist, keys[3], 1) == 0);
    cx_assert(uves_propertylist_get_long(plist, keys[3]) == 1);

    fval[0] = 9.87654321;
    cx_assert(uves_propertylist_set_float(plist, keys[4], fval[0]) == 0);
    fval[1] = uves_propertylist_get_float(plist, keys[4]);
    cx_assert(!memcmp(&fval[0], &fval[1], sizeof(float)));

    dval[0] = -9.87654321;
    cx_assert(uves_propertylist_set_double(plist, keys[5], dval[0]) == 0);
    dval[1] = uves_propertylist_get_double(plist, keys[5]);
    cx_assert(!memcmp(&dval[0], &dval[1], sizeof(double)));

    cx_assert(uves_propertylist_set_string(plist, keys[6], comments[0]) == 0);
    cx_assert(!strcmp(uves_propertylist_get_string(plist, keys[6]),
                      comments[0]));

    /*
     * Test 4: Check that trying to modify an entry with a different
     *         type is properly failing.
     */

    if (0) test_plist_dump(plist);

    cx_assert(uves_propertylist_set_char(plist, keys[1], 'a') ==
              CPL_ERROR_TYPE_MISMATCH);
    if (0) test_plist_dump(plist);


    cx_assert(uves_propertylist_set_bool(plist, keys[2], 1) ==
              CPL_ERROR_TYPE_MISMATCH);

    cx_assert(uves_propertylist_set_int(plist, keys[3], 1) ==
              CPL_ERROR_TYPE_MISMATCH);
    cx_assert(uves_propertylist_set_long(plist, keys[4], 1) ==
              CPL_ERROR_TYPE_MISMATCH);
    cx_assert(uves_propertylist_set_float(plist, keys[5], 1.) ==
              CPL_ERROR_TYPE_MISMATCH);
    cx_assert(uves_propertylist_set_double(plist, keys[6], 1.) ==
              CPL_ERROR_TYPE_MISMATCH);

    cx_assert(uves_propertylist_set_string(plist, keys[0], comments[0]) ==
              CPL_ERROR_TYPE_MISMATCH);

    /*
     * Test 5: Verify that values are inserted correctly into the property
     *         list.
     */

    cx_assert(uves_propertylist_insert_char(plist, keys[0],
                                           keys[7], 'a') == 0);
    cx_assert(uves_propertylist_insert_after_char(plist, keys[0],
                                                 keys[7], 'c') == 0);

    cx_assert(uves_propertylist_insert_bool(plist, keys[1],
                                           keys[8], 0) == 0);
    cx_assert(uves_propertylist_insert_after_bool(plist, keys[1],
                                                 keys[8], 1) == 0);

    cx_assert(uves_propertylist_insert_int(plist, keys[2],
                                          keys[9], 0) == 0);
    cx_assert(uves_propertylist_insert_after_int(plist, keys[2],
                                                keys[9], 1) == 0);

    cx_assert(uves_propertylist_insert_long(plist, keys[3], keys[10],
                                           123456789) == 0);
    cx_assert(uves_propertylist_insert_after_long(plist, keys[3], keys[10],
                                                 123456789) == 0);

    cx_assert(uves_propertylist_insert_float(plist, keys[4], keys[11],
                                            fval[0]) == 0);
    cx_assert(uves_propertylist_insert_after_float(plist, keys[4], keys[11],
                                                  -fval[0]) == 0);

    cx_assert(uves_propertylist_insert_double(plist, keys[5], keys[12],
                                             dval[0]) == 0);
    cx_assert(uves_propertylist_insert_after_double(plist, keys[5], keys[12],
                                                   -dval[0]) == 0);

    cx_assert(uves_propertylist_insert_string(plist, keys[6],
                                             keys[13], "") == 0);
    cx_assert(uves_propertylist_insert_after_string(plist, keys[6],
                                                   keys[13], "") == 0);
    for (i = 0; i < 7; i++) {
        cpl_property *p0 = uves_propertylist_get(plist, 3 * i);
        cpl_property *p1 = uves_propertylist_get(plist, 3 * i + 1);
        cpl_property *p2 = uves_propertylist_get(plist, 3 * i + 2);

        cx_assert(!strcmp(cpl_property_get_name(p0), keys[i + 7]));
        cx_assert(!strcmp(cpl_property_get_name(p1), keys[i]));
        cx_assert(!strcmp(cpl_property_get_name(p2), keys[i + 7]));

        switch (cpl_property_get_type(p0)) {
        case CPL_TYPE_CHAR:
            cx_assert(cpl_property_get_char(p0) == 'a');
            cx_assert(cpl_property_get_char(p2) == 'c');
            break;

        case CPL_TYPE_BOOL:
            cx_assert(cpl_property_get_bool(p0) == 0);
            cx_assert(cpl_property_get_bool(p2) == 1);
            break;

        case CPL_TYPE_INT:
            cx_assert(cpl_property_get_int(p0) == 0);
            cx_assert(cpl_property_get_int(p2) == 1);
            break;

        case CPL_TYPE_LONG:
            cx_assert(cpl_property_get_long(p0) == 123456789);
            cx_assert(cpl_property_get_long(p2) == 123456789);
            break;

        case CPL_TYPE_FLOAT:
            fval[1] = cpl_property_get_float(p0);
            cx_assert(!memcmp(&fval[0], &fval[1], sizeof(float)));

            fval[1] = -cpl_property_get_float(p2);
            cx_assert(!memcmp(&fval[0], &fval[1], sizeof(float)));
            break;

        case CPL_TYPE_DOUBLE:
            dval[1] = cpl_property_get_double(p0);
            cx_assert(!memcmp(&dval[0], &dval[1], sizeof(double)));

            dval[1] = -cpl_property_get_double(p2);
            cx_assert(!memcmp(&dval[0], &dval[1], sizeof(double)));
            break;

        case CPL_TYPE_STRING:
            cx_assert(!strcmp(cpl_property_get_string(p0), ""));
            cx_assert(!strcmp(cpl_property_get_string(p2), ""));
            break;

        default:
            /* This point should never be reached */
            cx_log("uves_propertylist-test", CX_LOG_LEVEL_ERROR, "file %s: "
                   "line %d: Invalid value type encountered", __FILE__,
                   __LINE__);
            break;
        }
    }


    /*
     * Test 6: Verify that modification of or insertion at/after a non
     *         existing elements is reported correctly.
     */
    cx_assert(uves_propertylist_contains(plist, "Non-existing key") == 0);

    cx_assert(uves_propertylist_set_char(plist, "Non-existing key", 'a') ==
              CPL_ERROR_DATA_NOT_FOUND);
    cx_assert(uves_propertylist_set_bool(plist, "Non-existing key", 1) ==
              CPL_ERROR_DATA_NOT_FOUND);
    cx_assert(uves_propertylist_set_int(plist, "Non-existing key", 1) ==
              CPL_ERROR_DATA_NOT_FOUND);
    cx_assert(uves_propertylist_set_long(plist, "Non-existing key", 1) ==
              CPL_ERROR_DATA_NOT_FOUND);
    cx_assert(uves_propertylist_set_float(plist, "Non-existing key", 1) ==
              CPL_ERROR_DATA_NOT_FOUND);
    cx_assert(uves_propertylist_set_double(plist, "Non-existing key", 1) ==
              CPL_ERROR_DATA_NOT_FOUND);
    cx_assert(uves_propertylist_set_string(plist, "Non-existing key", "") ==
              CPL_ERROR_DATA_NOT_FOUND);

    cx_assert(uves_propertylist_insert_char(plist, "Non-existing key",
                                           "h", 'a') == 1);
    cx_assert(uves_propertylist_insert_bool(plist, "Non-existing key",
                                           "h", 1) == 1);
    cx_assert(uves_propertylist_insert_int(plist, "Non-existing key",
                                          "h", 1) == 1);
    cx_assert(uves_propertylist_insert_long(plist, "Non-existing key",
                                           "h", 1) == 1);
    cx_assert(uves_propertylist_insert_float(plist, "Non-existing key",
                                            "h", 1) == 1);
    cx_assert(uves_propertylist_insert_double(plist, "Non-existing key",
                                             "h", 1) == 1);
    cx_assert(uves_propertylist_insert_string(plist, "Non-existing key",
                                             "h", "") == 1);

    cx_assert(uves_propertylist_insert_after_char(plist, "Non-existing key",
                                                 "h", 'a') == 1);
    cx_assert(uves_propertylist_insert_after_bool(plist, "Non-existing key",
                                                 "h", 1) == 1);
    cx_assert(uves_propertylist_insert_after_int(plist, "Non-existing key",
                                                "h", 1) == 1);
    cx_assert(uves_propertylist_insert_after_long(plist, "Non-existing key",
                                                 "h", 1) == 1);
    cx_assert(uves_propertylist_insert_after_float(plist, "Non-existing key",
                                                  "h", 1) == 1);
    cx_assert(uves_propertylist_insert_after_double(plist, "Non-existing key",
                                                   "h", 1) == 1);
    cx_assert(uves_propertylist_insert_after_string(plist, "Non-existing key",
                                                   "h", "") == 1);


    /*
     * Test 7: Create a copy of the property list and verify that original
     *         and copy are identical but do not share any resources.
     */

    _plist = uves_propertylist_duplicate(plist);
    cx_assert(_plist != NULL);
    cx_assert(_plist != plist);

    for (i = 0; i < uves_propertylist_get_size(plist); i++) {
        cpl_property *p = uves_propertylist_get(plist, i);
        cpl_property *_p = uves_propertylist_get(_plist, i);

        cx_assert(cpl_property_get_name(p) != cpl_property_get_name(_p));
        cx_assert(!strcmp(cpl_property_get_name(p),
                          cpl_property_get_name(_p)));
        cx_assert(cpl_property_get_comment(p) == NULL ||
                  (cpl_property_get_comment(p) !=
                   cpl_property_get_comment(_p)));
        cx_assert(cpl_property_get_comment(p) == NULL ||
                  !strcmp(cpl_property_get_comment(p),
                          cpl_property_get_comment(_p)));

        switch (cpl_property_get_type(p)) {
        case CPL_TYPE_CHAR:
            cx_assert(cpl_property_get_char(p) ==
                      cpl_property_get_char(_p));
            break;

        case CPL_TYPE_BOOL:
            cx_assert(cpl_property_get_bool(p) ==
                      cpl_property_get_bool(_p));
            break;

        case CPL_TYPE_INT:
            cx_assert(cpl_property_get_int(p) ==
                      cpl_property_get_int(_p));
            break;

        case CPL_TYPE_LONG:
            cx_assert(cpl_property_get_long(p) ==
                      cpl_property_get_long(_p));
            break;

        case CPL_TYPE_FLOAT:
            fval[0] = cpl_property_get_float(p);
            fval[1] = cpl_property_get_float(_p);
            cx_assert(!memcmp(&fval[0], &fval[1], sizeof(float)));
            break;

        case CPL_TYPE_DOUBLE:
            dval[0] = cpl_property_get_double(p);
            dval[1] = cpl_property_get_double(_p);
            cx_assert(!memcmp(&dval[0], &dval[1], sizeof(double)));
            break;

        case CPL_TYPE_STRING:
            cx_assert(!strcmp(cpl_property_get_string(p),
                              cpl_property_get_string(_p)));
            break;

        default:
            /* This point should never be reached */
            cx_log("uves_propertylist-test", CX_LOG_LEVEL_ERROR, "file %s: "
                   "line %d: Invalid value type encountered", __FILE__,
                   __LINE__);
            break;
        }
    }

    uves_propertylist_delete(_plist);

    /*
     * Test 8: Erase elements from the property list and verify the list
     *         structure and the data.
     */

    for (i = 0; i < 7; i++) {
        uves_propertylist_erase(plist, keys[i + 7]);
        cx_assert(uves_propertylist_contains(plist, keys[i + 7]) == 1);

        uves_propertylist_erase(plist, keys[i + 7]);
        cx_assert(uves_propertylist_contains(plist, keys[i + 7]) == 0);
    }
    cx_assert(uves_propertylist_get_size(plist) == 7);

    for (i = 0; i < 7; i++) {
        cpl_property *p = uves_propertylist_get(plist, i);
        cx_assert(!strcmp(cpl_property_get_name(p), keys[i]));
    }

    if (0) test_plist_dump(plist);
    
    cx_assert(uves_propertylist_get_char(plist, keys[0]) == 'b');
    cx_assert(uves_propertylist_get_bool(plist, keys[1]) == 0);
    cx_assert(uves_propertylist_get_int(plist, keys[2]) == -1);
    cx_assert(uves_propertylist_get_long(plist, keys[3]) == 1);

    fval[0] = 9.87654321;
    fval[1] = uves_propertylist_get_float(plist, keys[4]);
    cx_assert(!memcmp(&fval[0], &fval[1], sizeof(float)));

    dval[0] = -9.87654321;
    dval[1] = uves_propertylist_get_double(plist, keys[5]);
    cx_assert(!memcmp(&dval[0], &dval[1], sizeof(double)));

    cx_assert(!strcmp(uves_propertylist_get_string(plist, keys[6]),
                      comments[0]));

    /*
     * Test 9: Erase all elements from the property list and verify that
     *         the list is empty.
     */

    uves_propertylist_empty(plist);

    cx_assert(uves_propertylist_is_empty(plist));
    cx_assert(uves_propertylist_get_size(plist) == 0);

    uves_propertylist_delete(plist);


    /*
     * Test 10: Create a property list from an input (FITS) file. Verify the
     *          loaded data.
     */

    /* Create a sample FITS header and save it to a file */

    header = qfits_header_new();

    for (i = 0; (cxsize)i < sizeof hdr / sizeof(struct fcard); i++)
        qfits_header_append(header, hdr[i].key, hdr[i].val, hdr[i].com, NULL);

    file = fopen("cpltest1.fits", "w");
    qfits_header_dump(header, file);
    fclose(file);

    /* Load the 1st FITS header into a property list */

    plist = uves_propertylist_load("cpltest1.fits", 0);
    cx_assert(plist != NULL);

    cx_assert(uves_propertylist_contains(plist, "END") == 0);
    cx_assert(uves_propertylist_get_size(plist) ==
              (sizeof hdr / sizeof(struct fcard) - 1));

    for (i = 0; i < uves_propertylist_get_size(plist); i++) {
        const cxchar *s = hdr[i].key;
        cpl_property *p = uves_propertylist_get(plist, i);

        /*
         * Strip HIERARCH from the keyword if it is present. HIERARCH
         * is not carried over to the property name.
         */

        if (strstr(hdr[i].key, "HIERARCH"))
            s = hdr[i].key + strlen("HIERARCH") + 1;

        cx_assert(!strcmp(cpl_property_get_name(p), s));
        cx_assert(hdr[i].com == NULL ||
                  !strcmp(cpl_property_get_comment(p), hdr[i].com));
        cx_assert(cpl_property_get_type(p) == hdr[i].type);

        switch (hdr[i].type) {
        case CPL_TYPE_BOOL:
            cx_assert(cpl_property_get_bool(p) ==
                      (*hdr[i].val == 'T' ? 1 : 0));
            break;

        case CPL_TYPE_INT:
            cx_assert(cpl_property_get_int(p) == atoi(hdr[i].val));
            break;

        case CPL_TYPE_DOUBLE:
            dval[0] = cpl_property_get_double(p);
            dval[1] = atof(hdr[i].val);
            cx_assert(!memcmp(&dval[0], &dval[1], sizeof(double)));
            break;

        case CPL_TYPE_STRING:
            cx_assert(!strcmp(cpl_property_get_string(p),
                              qfits_pretty_string(hdr[i].val)));
            break;

        default:
            cx_log("uves_propertylist-test", CX_LOG_LEVEL_ERROR, "file %s: "
                   "line %d: Invalid value type encountered", __FILE__,
                   __LINE__);
            break;
        }
    }


    /*
     * Test 11: Convert the qfits FITS header back into a property list and
     *          verify that the original property list and the one created
     *          from the FITS header are identical.
     */

    _plist = uves_propertylist_from_fits(header) ;
    cx_assert(_plist != NULL);
    cx_assert(uves_propertylist_get_size(plist) ==
              uves_propertylist_get_size(_plist));

    for (i = 0; i < uves_propertylist_get_size(plist); i++) {
        cpl_property *p = uves_propertylist_get(plist, i);
        cpl_property *_p = uves_propertylist_get(_plist, i);

        cx_assert(strcmp(cpl_property_get_name(p),
                         cpl_property_get_name(_p)) == 0);
        cx_assert(strcmp(cpl_property_get_comment(p),
                         cpl_property_get_comment(_p)) == 0);
        cx_assert(cpl_property_get_type(p) == cpl_property_get_type(_p));

        switch (cpl_property_get_type(p)) {
        case CPL_TYPE_BOOL:
            cx_assert(cpl_property_get_bool(p) == cpl_property_get_bool(_p));
            break;

        case CPL_TYPE_INT:
            cx_assert(cpl_property_get_int(p) == cpl_property_get_int(_p));
            break;

        case CPL_TYPE_DOUBLE:
            cx_assert(cpl_property_get_double(p) ==
                      cpl_property_get_double(_p));
            break;

        case CPL_TYPE_STRING:
            cx_assert(strcmp(cpl_property_get_string(p),
                             cpl_property_get_string(_p)) == 0);
            break;

        default:
            cx_log("uves_propertylist-test", CX_LOG_LEVEL_ERROR,
                   "file %s: line %d: Invalid value type encountered",
                   __FILE__, __LINE__);
            break;
        }
    }

    uves_propertylist_delete(_plist);


    /*
     * Test 12: Create a qfits header from the property list and verify
     *          the header data. Note that uves_propertylist_to_fits establishes
     *          the DICB sorting order.
     */

    qfits_header_sort(&header);

    file = fopen("cpltest1.fits", "w");
    qfits_header_dump(header, file);
    fclose(file);


    _header = uves_propertylist_to_fits(plist);
    cx_assert(_header != NULL);

    file = fopen("cpltest2.fits", "w");
    qfits_header_dump(_header, file);
    fclose(file);


    for (i = 0; i < header->n; i++) {
        cxchar key[FITS_LINESZ + 1];
        cxchar val[FITS_LINESZ + 1];
        cxchar com[FITS_LINESZ + 1];

        cxchar _key[FITS_LINESZ + 1];
        cxchar _val[FITS_LINESZ + 1];
        cxchar _com[FITS_LINESZ + 1];


        qfits_header_getitem(header, i, key, val, com, NULL);
        cx_assert(qfits_header_getitem(_header, i, _key, _val,
                                       _com, NULL) == 0);

        cx_assert(!strcmp(key, _key));
        cx_assert(_com == NULL || !strcmp(com, _com));

        switch (qfits_get_type(val)) {
        case QFITS_FLOAT:
            fval[0] = atof(val);
            fval[1] = atof(_val);

            cx_assert(fabs(fval[0] - fval[1]) < FLT_EPS);
            break;

        default:
            cx_assert(strlen(val) == 0 ||
                      !strcmp(qfits_pretty_string(val),
                              qfits_pretty_string(_val)));
            break;
        }

    }

    qfits_header_destroy(header);
    qfits_header_destroy(_header);


    /*
     * Test 13: Copy all propertys matching a given pattern from one
     *          property list to another.
     */

    _plist = uves_propertylist_new();

    uves_propertylist_copy_property_regexp(_plist, plist, "^ESO .*", 0);
    cx_assert(uves_propertylist_get_size(_plist) == 6);
    cx_assert(uves_propertylist_contains(_plist, "ESO OBS DID") != 0);
    cx_assert(uves_propertylist_contains(_plist, "ESO OBS OBSERVER") != 0);
    cx_assert(uves_propertylist_contains(_plist, "ESO OBS PI-COI NAME") != 0);
    cx_assert(uves_propertylist_contains(_plist, "ESO INS GRAT NAME") != 0);
    cx_assert(uves_propertylist_contains(_plist, "ESO PRO CATG") != 0);
    cx_assert(uves_propertylist_contains(_plist, "ESO TPL NEXP") != 0);

    uves_propertylist_empty(_plist);
    cx_assert(uves_propertylist_is_empty(_plist) != 0);

    uves_propertylist_copy_property_regexp(_plist, plist, "^ESO .*", 1);
    cx_assert(uves_propertylist_get_size(_plist) ==
              (uves_propertylist_get_size(plist) - 6));


    /*
     * Test 14: Erase all properties matching the given pattern from the
     *          property list.
     */

    uves_propertylist_empty(_plist);
    cx_assert(uves_propertylist_is_empty(_plist) != 0);

    uves_propertylist_copy_property_regexp(_plist, plist, "^ESO .*", 0);
    cx_assert(uves_propertylist_get_size(_plist) == 6);

    uves_propertylist_erase_regexp(_plist, "^ESO OBS .*", 0);
    cx_assert(uves_propertylist_get_size(_plist) == 3);

    uves_propertylist_erase_regexp(_plist, "ESO TPL NEXP", 0);
    cx_assert(uves_propertylist_get_size(_plist) == 2);

    uves_propertylist_delete(_plist);
    uves_propertylist_delete(plist);


    /*
     * Test 15: Create a property list from a file. Only properties matching
     *          the given pattern are loaded.
     */

    plist = NULL;

    plist = uves_propertylist_load_regexp("cpltest1.fits", 0,
                                         "^ESO .*", 0);
    cx_assert(plist != NULL);
    cx_assert(uves_propertylist_is_empty(plist) == 0);
    cx_assert(uves_propertylist_get_size(plist) == 6);
    cx_assert(uves_propertylist_contains(plist, "ESO OBS DID") != 0);
    cx_assert(uves_propertylist_contains(plist, "ESO OBS OBSERVER") != 0);
    cx_assert(uves_propertylist_contains(plist, "ESO OBS PI-COI NAME") != 0);
    cx_assert(uves_propertylist_contains(plist, "ESO INS GRAT NAME") != 0);
    cx_assert(uves_propertylist_contains(plist, "ESO PRO CATG") != 0);
    cx_assert(uves_propertylist_contains(plist, "ESO TPL NEXP") != 0);

    uves_propertylist_delete(plist);


    /*
     * Test 16: Append a property list to another.
     */

    plist = uves_propertylist_new();
    _plist = uves_propertylist_new();

    uves_propertylist_append_char(plist, keys[0], 'a');
    uves_propertylist_set_comment(plist, keys[0], comments[0]);

    uves_propertylist_append_bool(plist, keys[1], 1);
    uves_propertylist_set_comment(plist, keys[1], comments[1]);

    uves_propertylist_append_int(plist, keys[2], -1);
    uves_propertylist_set_comment(plist, keys[2], comments[2]);

    uves_propertylist_append_long(plist, keys[3], 32768);
    uves_propertylist_set_comment(plist, keys[3], comments[3]);

    uves_propertylist_append_float(_plist, keys[4], fval[0]);
    uves_propertylist_set_comment(_plist, keys[4], comments[4]);

    uves_propertylist_append_double(_plist, keys[5], dval[0]);
    uves_propertylist_set_comment(_plist, keys[5], comments[5]);

    uves_propertylist_append_string(_plist, keys[6], comments[6]);
    uves_propertylist_set_comment(_plist, keys[6], comments[6]);

    cx_assert(!uves_propertylist_is_empty(plist));
    cx_assert(uves_propertylist_get_size(plist) == 4);

    cx_assert(!uves_propertylist_is_empty(_plist));
    cx_assert(uves_propertylist_get_size(_plist) == 3);

    uves_propertylist_append(plist, _plist);

    cx_assert(!uves_propertylist_is_empty(plist));
    cx_assert(uves_propertylist_get_size(plist) == 7);

    cx_assert(!uves_propertylist_is_empty(_plist));
    cx_assert(uves_propertylist_get_size(_plist) == 3);

    for (i = 0; i < uves_propertylist_get_size(plist); i++) {
        cpl_property *p = uves_propertylist_get(plist, i);

        cx_assert(!strcmp(cpl_property_get_name(p), keys[i]));
        cx_assert(!strcmp(cpl_property_get_comment(p), comments[i]));
        cx_assert(cpl_property_get_type(p) == types[i]);

        cx_assert(uves_propertylist_contains(plist, keys[i]));
        cx_assert(!strcmp(uves_propertylist_get_comment(plist, keys[i]),
                          comments[i]));
        cx_assert(uves_propertylist_get_type(plist, keys[i]) == types[i]);
    }


    /*
     * Test 17: Create a FITS header using a list containing a property with
     *          a name of length 80 characters (the length of a FITS card)
     */

    uves_propertylist_empty(plist);

    uves_propertylist_append_string(plist, longname, comments[6]);

    qfits_header_destroy(uves_propertylist_to_fits(plist));


    uves_propertylist_delete(_plist);
    _plist = NULL;

    uves_propertylist_delete(plist);
    plist = NULL;

    cx_assert( cpl_error_get_code() == CPL_ERROR_UNSPECIFIED );
    cpl_error_reset();

    /*
     * All tests succeeded
     */

    return 0;

}

int main(void)
{
    cpl_test_init(PACKAGE_BUGREPORT, CPL_MSG_WARNING);

    //cpl_msg_set_level(CPL_MSG_DEBUG);
    test_main();

    return cpl_test_end(0);
}
