/* $Id: kmo_test_create_data.c,v 1.10 2013-02-28 10:10:23 aagudo Exp $
 *
 * This file is part of the KMOS Pipeline
 * Copyright (C) 2002,2003 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
 *
 * Automatic data generation functions
 *
 * who       when        what
 * --------  ----------  ----------------------------------------------
 * aagudo    2008-02-06  created
 *
 * The functions in this file are only used in functions generating test data
 * in kmos/tests and recipes/tests, therefore no test functions are provided
 * for the functions present here. (This file has to be loccated here since it
 * is referred to from kmos/tests and recipes/tests)
 *
 */

/*
 * $Author: aagudo $
 * $Date: 2013-02-28 10:10:23 $,
                               ""
 * $Revision: 1.10 $
 * $Name: not supported by cvs2svn $
 */

#include <stdio.h>
#include <string.h>
#include <sys/stat.h>

#include "kmo_test_create_data.h"
#include "kmo_dfs.h"
#include "kmo_constants.h"
#include "kmo_debug.h"
#include "kmo_error.h"
#include "kmo_utils.h"
#include "kmo_cpl_extensions.h"

/*----------------------------------------------------------------------------*/
/**
    @defgroup kmos_test_create_data     Helper functions for recipe kmo_test_create_data.

    This is used for internal unit tests only.

    @{
 */
/*----------------------------------------------------------------------------*/

float   test_global_seed_data            = 1.1,
        test_global_seed_noise           = 0.1;

int     test_global_size_x               = 5,
        test_global_size_y               = 6,
        test_global_size_z               = 7,
        test_global_nr_frames            = 5;

const char  *test_global_raw_path        = "ref_data/KMOS_dummy.fits";

const char  *test_global_path_test_data  = "test_data/";
const char  *test_global_path_ref_data   = "ref_data/";

// !!! global_valid MUST have size of nr_ifus !!!
int test_global_valid[] =  {TRUE, FALSE, TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, \
                       TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, \
                       TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE};

/**
    @defgroup kmo_create_data  Functions to create test data

    @{
 */

/**
    @brief Checks if a file or folder of a given name exists.

    @param name The name of the file or folder to check.

    @return
        TRUE if the file or folder exists, FALSE otherwise.
*/
int kmo_test_file_exists(const char *name)
{
    struct stat buf;

    if (stat(name, &buf) == 0) {
        return TRUE;
    }
    return FALSE;
}

/**
    @brief Concatenates to strings.

    @param new      The concatenated string.
    @param string1  The first string.
    @param string2  The second string.

    @return         The concatenated string.
*/
const char* kmo_test_cat_strings(char *newstr, const char *string1, const char *string2)
{
    KMO_TRY
    {
        KMO_TRY_ASSURE(newstr != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "First char pointer has to be allocated!");

        KMO_TRY_ASSURE(string1 != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Second char pointer mustn't be NULL!");

        KMO_TRY_ASSURE(string2 != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Third char pointer mustn't be NULL!");

        strcpy(newstr, string1);
        strcat(newstr, string2);
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();

    }

    return newstr;
}

/**
    @brief Switches off error-messages.
*/
void kmo_test_verbose_off()
{
    if (getenv("KMO_TEST_VERBOSE") == NULL) {
        cpl_msg_set_level(CPL_MSG_OFF);
    }
}

/**
    @brief Switches on error-messages.
*/
void kmo_test_verbose_on()
{
    if (getenv("KMO_TEST_VERBOSE") == NULL) {
        cpl_msg_set_level(CPL_MSG_WARNING);
    }
}

/**
    @brief Switches on or off error-messages for esorex-calls.
*/
const char* kmo_test_esorex_verbose()
{
    if (getenv("KMO_TEST_VERBOSE") == NULL) {
        return " --msg-level=off ";
    } else {
        return " --msg-level=info ";
    }
}

void kmo_get_pipe_command(char *cmd, const char *filename, const char *sof, int extend)
{
    FILE *fd = NULL;
    if (extend) {
        fd = fopen(filename, "a+");
    } else {
        fd = fopen(filename, "w");
    }
    fprintf(fd, "%s\n", "**********************************************************");
    fprintf(fd, "%s\n", cmd);
    if (sof != NULL) {
        fprintf(fd, "\nWith %s:\n------------------\n", sof);
        fclose(fd);

        char sofcmd[1024];
        sprintf(sofcmd, "cat %s >> %s", sof, filename);
        system(sofcmd);
        fd = fopen(filename, "a+");
    }
    fprintf(fd, "%s\n", "**********************************************************");
    fclose(fd);

    strcat(cmd, " >> ");
    strcat(cmd, filename);
    strcat(cmd, " 2>&1 ");
}

/**
    @brief
        Calculates the sum of the mean of all data extensions (data and noise).

    @param filename     The keyword belonging to the recipe. Internally it is
                        converted to the corresponding filename generated by
                        esorex
    @paraam dont_lower  TRUE if @c filename shouldn't be set lowercase,
                        FALSE otherwise

    @return The sum of the mean of all valid data extensions.
*/
double kmo_test_esorex_data(const char* filename, int dont_lower)
{
    double          ret_val     = 0.0,
                    data_val    = 0.0,
                    noise_val   = 0.0;

    main_fits_desc  desc;

    int             nr_devices  = 0,
                    i           = 0,
                    j           = 0,
                    valid_ifu   = FALSE,
                    index_data  = 0,
                    index_noise = 0,
                    devnr       = 0;

    char            name[256];

    const char      **ptbl_names    = NULL;

    cpl_image       *img        = NULL;

    cpl_imagelist   *img_list   = NULL;

    kmclipm_vector  *vec        = NULL;

    cpl_table       *tbl        = NULL;

    cpl_array       *tbl_names  = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(filename != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "No filename is provided!");

        // setup correct filename
        strcpy(name, filename);
        if (!dont_lower) {
            kmo_strlower(name);
        }
        strcat(name, ".fits");

        kmo_init_fits_desc(&desc);
        KMO_TRY_CHECK_ERROR_STATE();

        desc = kmo_identify_fits_header(name);
        KMO_TRY_CHECK_ERROR_STATE();

        // --- load data ---
        if (desc.ex_noise == TRUE) {
            nr_devices = desc.nr_ext / 2;
        } else {
            nr_devices = desc.nr_ext;
        }

        for (i = 1; i <= nr_devices; i++) {
            if (desc.ex_noise == FALSE) {
                devnr = desc.sub_desc[i - 1].device_nr;
            } else {
                devnr = desc.sub_desc[2 * i - 1].device_nr;
            }

            valid_ifu = FALSE;
            if (desc.ex_badpix == FALSE) {
                index_data = kmo_identify_index(name, devnr, FALSE);
            } else {
                index_data = kmo_identify_index(name, devnr, 2);
            }
            KMO_TRY_CHECK_ERROR_STATE();

//            if (desc.ex_noise == FALSE) {
                if (desc.sub_desc[index_data-1].valid_data == TRUE) {
                    valid_ifu = TRUE;
                }
//            } else {
//                if (desc.sub_desc[2 * i - 1].valid_data == TRUE) {
//                    valid_ifu = TRUE;
//                }
//            }
            KMO_TRY_CHECK_ERROR_STATE();

            if (valid_ifu) {
                if ((desc.fits_type == f2i_fits) ||
                    (desc.fits_type == f2d_fits) ||
                    (desc.fits_type == b2d_fits) ||
                    (desc.fits_type == raw_fits))
                {
                    KMO_TRY_EXIT_IF_NULL(
                        img = kmclipm_image_load(name, CPL_TYPE_FLOAT, 0,
                                                 index_data));

                    data_val += cpl_image_get_mean(img);
                    if (cpl_error_get_code() ==  CPL_ERROR_DATA_NOT_FOUND) {
                        // whole image is rejected
                        cpl_error_reset();
                    }

                    cpl_image_delete(img); img = NULL;

                    if (desc.ex_noise == TRUE) {
                        index_noise = kmo_identify_index(name, devnr, TRUE);
                        KMO_TRY_CHECK_ERROR_STATE();

                        if (desc.sub_desc[index_noise-1].valid_data) {
                            KMO_TRY_EXIT_IF_NULL(
                                img = kmclipm_image_load(name, CPL_TYPE_FLOAT,
                                                         0, index_noise));

                            noise_val += cpl_image_get_mean(img);
                            if (cpl_error_get_code()==CPL_ERROR_DATA_NOT_FOUND) {
                                // whole image is rejected
                                cpl_error_reset();
                            }
                            cpl_image_delete(img); img = NULL;
                        }
                    }
                } else if (desc.fits_type == f3i_fits) {
                    KMO_TRY_EXIT_IF_NULL(
                        img_list = kmclipm_imagelist_load(name,
                                                          CPL_TYPE_FLOAT,
                                                          index_data));

                    KMO_TRY_EXIT_IF_NULL(
                        img = cpl_imagelist_collapse_create (img_list));

                    data_val += cpl_image_get_mean(img);
                    if (cpl_error_get_code() ==  CPL_ERROR_DATA_NOT_FOUND) {
                        // whole image is rejected
                        cpl_error_reset();
                    }

                    cpl_image_delete(img); img = NULL;
                    cpl_imagelist_delete(img_list); img_list = NULL;

                    if (desc.ex_noise == TRUE) {
                        index_noise = kmo_identify_index(name, devnr, TRUE);
                        KMO_TRY_CHECK_ERROR_STATE();

                        if (desc.sub_desc[index_noise-1].valid_data) {
                            KMO_TRY_EXIT_IF_NULL(
                                img_list = kmclipm_imagelist_load(name,
                                                                  CPL_TYPE_FLOAT,
                                                                  index_noise));


                            img = cpl_imagelist_collapse_create(img_list);
                            if (cpl_error_get_code()==CPL_ERROR_DIVISION_BY_ZERO)
                            {
                                // Fix for CPL 6.1.1 and above (if NaN-cube
                                // provided cpl_imagelist_collapse_create() now
                                // throws, before it returned a 0-image)
                                cpl_error_reset();
                                cpl_image *tmp = cpl_imagelist_get(img_list, 0);
                                img = cpl_image_new(cpl_image_get_size_x(tmp),
                                                    cpl_image_get_size_y(tmp),
                                                    CPL_TYPE_FLOAT);
                                kmo_image_fill(img, 0.0);
                            }

                            noise_val += cpl_image_get_mean(img);
                            if (cpl_error_get_code()==CPL_ERROR_DATA_NOT_FOUND) {
                                // whole image is rejected
                                cpl_error_reset();
                            }

                            cpl_image_delete(img); img = NULL;
                            cpl_imagelist_delete(img_list); img_list = NULL;
                        }
                    }
                }  else if ((desc.fits_type == f1i_fits) ||
                            (desc.fits_type == f1d_fits)) {
                    KMO_TRY_EXIT_IF_NULL(
                        vec = kmclipm_vector_load(name, index_data));

                    data_val += kmclipm_vector_get_mean(vec);

                    kmclipm_vector_delete(vec); vec = NULL;

                    if (desc.ex_noise == TRUE) {
                        index_noise = kmo_identify_index(name, devnr, TRUE);
                        KMO_TRY_CHECK_ERROR_STATE();

                        if (desc.sub_desc[index_noise-1].valid_data) {
                            KMO_TRY_EXIT_IF_NULL(
                                vec = kmclipm_vector_load(name, index_noise));

                            noise_val += kmclipm_vector_get_mean(vec);

                            kmclipm_vector_delete(vec); vec = NULL;
                        }
                    }
                }   else if (desc.fits_type == f1s_fits) {
                    KMO_TRY_EXIT_IF_NULL(
                        vec = kmclipm_vector_load(name, index_data));

                    data_val += kmclipm_vector_get_mean(vec);

                    kmclipm_vector_delete(vec); vec = NULL;
                }   else if ((desc.fits_type == f1l_fits) ||
                             (desc.fits_type == f2l_fits)) {
                    KMO_TRY_EXIT_IF_NULL(
                        tbl = kmclipm_table_load(name, index_data, 0));

                    KMO_TRY_EXIT_IF_NULL(
                        tbl_names = cpl_table_get_column_names(tbl));

                    KMO_TRY_EXIT_IF_NULL(
                        ptbl_names = cpl_array_get_data_string_const(
                                                                    tbl_names));

                    for (j = 0; j < cpl_array_get_size(tbl_names); j++) {
                        data_val += cpl_table_get_column_mean(tbl,
                                                              ptbl_names[j]);
                    }

                    cpl_table_delete(tbl); tbl = NULL;
                    cpl_array_delete(tbl_names); tbl_names = NULL;
                } else {
                    KMO_TRY_ERROR_SET_MSG(CPL_ERROR_ILLEGAL_INPUT,
                                          "Fits-format not yet supported!!!");
                }
                KMO_TRY_CHECK_ERROR_STATE();
            }

            ret_val += data_val + noise_val;
            data_val = 0.0;
            noise_val = 0.0;
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_val     = -DBL_MAX;
    }

    kmo_free_fits_desc(&desc);

    return ret_val;
}

/**
    @brief Creates 2048x2048pixel data to test the pipeline
 */
void kmo_test_create_pipeline_data()
{
    printf("------------------------------\n");
    printf(" > Creating pipeline test data\n");
    kmo_test_create_pipeline_data_core("pipeline/",
                                       "log_generate_pipeline_data.txt");
    printf("------------------------------\n");
}

/**
    @brief Creates 2048x2048pixel data to test the pipeline
 */
void kmo_test_create_pipeline_data_core(const char * subpath, const char* log_path)
{
    const char  *darkpath   = "ref_data/K_only_sky_dark.fits",
                *flatpath   = "ref_data/K_only_sky_flat.fits",
                *arcpath    = "ref_data/K_only_sky_arc.fits",
                *skypath    = "ref_data/K_only_sky.fits",
                *stdpath    = "ref_data/K_stars+obj_rot0.25.fits",
                *valid      = "\"1;1;1;1;1;1;1;1\"",
                *filter     = "\"K\"",
                *grating    = "\"K\"",
                *sub_key    = "\"ESO DET CHIP GAIN;double;1.1;ESO DET CHIP RON;double;0.1\"";

    char        cmd[2048],
                path[1024];

    double      rotangle    = 0.0;

//    cpl_msg_info("", " > Creating pipeline test data...");

    // setup directories
    if (kmo_test_file_exists(test_global_path_test_data) == FALSE) {
        mkdir(test_global_path_test_data, 0777);
    }

    strcpy(path, test_global_path_test_data);
    strcat(path, subpath);

    if (kmo_test_file_exists(path) == FALSE) {
        mkdir(path, 0777);
    }

    // DARK
    sprintf(cmd, "esorex kmo_dev_setup --type=DARK --rotangle=%f --valid=%s"
            " --subkey=%s %s",
            rotangle, valid, sub_key, darkpath);
    kmo_get_pipe_command(cmd, log_path, NULL, FALSE);
    system(cmd);
    sprintf(cmd, "mv dark_123.fits %s", path); system(cmd);
    sprintf(cmd, "mv dark_231.fits %s", path); system(cmd);
    sprintf(cmd, "mv dark_312.fits %s", path); system(cmd);

    // FLAT_ON
    sprintf(cmd, "esorex kmo_dev_setup --type=FLAT_ON  --rotangle=%f --valid=%s"
            " --filt=%s --grat=%s --subkey=%s %s",
            rotangle, valid, filter, grating,
            sub_key, flatpath);
    kmo_get_pipe_command(cmd, log_path, NULL, TRUE);
    system(cmd);
    sprintf(cmd, "mv flat_on_123.fits %s", path); system(cmd);
    sprintf(cmd, "mv flat_on_231.fits %s", path); system(cmd);
    sprintf(cmd, "mv flat_on_312.fits %s", path); system(cmd);

    // FLAT_OFF
    sprintf(cmd, "esorex kmo_dev_setup --type=FLAT_OFF --rotangle=%f "
            "--valid=%s --filt=%s --grat=%s %s",
            rotangle, valid, filter, grating,
            darkpath);
    kmo_get_pipe_command(cmd, log_path, NULL, TRUE);
    system(cmd);
    sprintf(cmd, "mv flat_off_123.fits %s", path); system(cmd);
    sprintf(cmd, "mv flat_off_231.fits %s", path); system(cmd);
    sprintf(cmd, "mv flat_off_312.fits %s", path); system(cmd);

    // ARC_ON
    sprintf(cmd, "esorex kmo_dev_setup --type=ARC_ON   --rotangle=%f "
            "--valid=%s --filt=%s --grat=%s --subkey=%s %s",
            rotangle, valid, filter, grating,
            sub_key, arcpath);
    kmo_get_pipe_command(cmd, log_path, NULL, TRUE);
    system(cmd);
    sprintf(cmd, "mv arc_on_123.fits %s", path); system(cmd);

    // ARC_OFF
    sprintf(cmd, "esorex kmo_dev_setup --type=ARC_OFF  --rotangle=%f "
            "--valid=%s --filt=%s --grat=%s --subkey=%s %s",
           rotangle, valid, filter, grating,
            sub_key, darkpath);
    kmo_get_pipe_command(cmd, log_path, NULL, TRUE);
    system(cmd);
    sprintf(cmd, "mv arc_off_123.fits %s", path); system(cmd);

    // SKY
    sprintf(cmd, "esorex kmo_dev_setup --type=SKY --rotangle=%f --valid=%s"
            " --filt=%s --grat=%s %s",
            rotangle, valid, filter, grating,
            skypath);
    kmo_get_pipe_command(cmd, log_path, NULL, TRUE);
    system(cmd);
    sprintf(cmd, "mv sky_123.fits %s", path); system(cmd);
    sprintf(cmd, "mv sky_231.fits %s", path); system(cmd);
    sprintf(cmd, "mv sky_312.fits %s", path); system(cmd);

    // STD OBJ
    sprintf(cmd, "esorex kmo_dev_setup --type=STD --rotangle=%f --valid=%s"
            " --filt=%s --grat=%s  --subkey=%s "
            " --mainkey=\"ESO TPL ID;string;KMOS_spec_cal_stdstarscipatt\" "
            " --date=\"2011-01-12T11:10:00.0000\" "
            " --objects=\"0;1;0;0;1;0;0;0\" %s ",
            rotangle, valid, filter, grating,
            sub_key, stdpath);
    kmo_get_pipe_command(cmd, log_path, NULL, TRUE);
    system(cmd);
    sprintf(cmd, "mv std_123.fits %s/std_obj_123.fits", path); system(cmd);

    // STD SKY
    sprintf(cmd, "esorex kmo_dev_setup --type=STD --rotangle=%f --valid=%s"
            " --filt=%s --grat=%s  --subkey=%s "
            " --mainkey=\"ESO TPL ID;string;KMOS_spec_cal_stdstarscipatt\" "
            " --date=\"2011-01-12T11:12:00.0000\" "
            " --objects=\"0;0;0;0;0;0;0;0\" %s ",
            rotangle, valid, filter, grating,
            sub_key, skypath);
    kmo_get_pipe_command(cmd, log_path, NULL, TRUE);
    system(cmd);
    sprintf(cmd, "mv std_123.fits %s/std_sky_123.fits", path); system(cmd);

    // SCIENCE OBJ
    sprintf(cmd, "esorex kmo_dev_setup --type=STD --rotangle=%f --valid=%s"
            " --filt=%s --grat=%s  --subkey=%s "
            "--date=\"2011-01-12T12:12:00.0000\" "
            "--mainkey=\"ESO OCS ARM5 NAME;string;AAA;"
            "ESO OCS ARM10 NAME;string;BBB;"
            "ESO OCS ARM18 NAME;string;CCC;ESO OCS ARM21 NAME;string;DDD\""
            " --objects=\"0;1;0;0;1;0;0;0\" %s",
            rotangle, valid, filter, grating,
            sub_key, stdpath);
    kmo_get_pipe_command(cmd, log_path, NULL, TRUE);
    system(cmd);
    sprintf(cmd, "mv std_123.fits %s/science_obj_123.fits", path); system(cmd);

    // SCIENCE SKY
    sprintf(cmd, "esorex kmo_dev_setup --type=STD --rotangle=%f --valid=%s"
            " --filt=%s --grat=%s  --subkey=%s "
            "--date=\"2011-01-12T12:13:00.0000\""
            " --objects=\"0;0;0;0;0;0;0;0\" %s",
            rotangle, valid, filter, grating,
            sub_key, skypath);
    kmo_get_pipe_command(cmd, log_path, NULL, TRUE);
    system(cmd);
    sprintf(cmd, "mv std_123.fits %s/science_sky_123.fits", path); system(cmd);

    system("rm tmp_delete1.fits tmp_delete2.fits tmp_delete3.fits");

    cpl_msg_info("", "Finished creating pipeline test data...");
}

/**
    @brief
        Frame constructor

    @param   filename         frame filename
    @param   tag              frame tag
    @param   group            frame group

    @return  Newly allocated frame with the given contents.
 */
cpl_frame* kmo_test_frame_new(const char *filename,
                              const char *tag,
                              cpl_frame_group group)
{
    cpl_frame   *f      = NULL;

    KMO_TRY
    {
        f = cpl_frame_new();
        KMO_TRY_EXIT_IF_NOT(f != NULL);

        cpl_frame_set_filename(f, filename);

        cpl_frame_set_tag(f, tag);

        cpl_frame_set_group(f, group);
    }
    KMO_CATCH
    {
        f = NULL;
    }

    return f;
}

/**
    @brief Updates (or adds) additional keywords to a fits-header.

    @param   pl         The propertylist to update.
    @param   keys       The array containing the keywords.
    @param   vals       The array containing the values as strings.
    @param   types      The array containing the CPL-types for the keywords.
    @param   nr_keys    The number of keywords to add.

    @return CPL_ERROR_NONE in case of success.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_ILLEGAL_INPUT    if an invalid CPL_TYPE has been provided.
 */
cpl_error_code kmo_test_update_keywords(cpl_propertylist *pl,
                                        char *keys[],
                                        char *vals[],
                                        int types[],
                                        int nr_keys)
{
    int             i   = 0;
    cpl_error_code  ret = CPL_ERROR_NONE;

    KMO_TRY
    {
        KMO_TRY_ASSURE(pl != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all inputs provided!");

        for (i = 0; i < nr_keys; i++) {
            switch (types[i]) {
                case CPL_TYPE_DOUBLE:
                    cpl_propertylist_update_double(pl,
                                                   keys[i],
                                                   atof(vals[i]));
                    break;
                case CPL_TYPE_INT:
                    cpl_propertylist_update_int(pl,
                                                keys[i],
                                                atoi(vals[i]));

                    break;
                case CPL_TYPE_FLOAT:
                    cpl_propertylist_update_float(pl,
                                                  keys[i],
                                                  atof(vals[i]));
                    break;
                case CPL_TYPE_STRING:
                    cpl_propertylist_update_string(pl,
                                                   keys[i],
                                                   vals[i]);
                    break;
                case CPL_TYPE_BOOL:
                    cpl_propertylist_update_bool(pl,
                                                   keys[i],
                                                   atoi(vals[i]));
                    break;
                default:
                    ret = CPL_ERROR_ILLEGAL_INPUT;
                    break;
            }
        }
    }
    KMO_CATCH
    {
    }

    return ret;
}

/**
    @brief Fills a vector with increasing values.

    @param   vec         The vector to fill with values.
    @param   seed        The starting value.
    @param   offset      The offset for the values.

 */
void kmo_test_fill_vector(cpl_vector *vec,
                          float seed,
                          float offset)
{
    int         i       = 0,
                size    = 0;
    double      *data   = NULL;

    KMO_TRY
    {
        size = cpl_vector_get_size(vec);
        KMO_TRY_EXIT_IF_NOT(size > 0);

        data = cpl_vector_get_data(vec);
        KMO_TRY_EXIT_IF_NOT(data != NULL);

        // horizontal stripes: bottom = 0, top = seed * (y-1)
        for (i = 0; i < size; i++) {
                data[i] = seed + i * offset;
        }
    }
    KMO_CATCH
    {
    }
}

/**
    @brief Fills an image with increasing values.

    @param   img         The image to fill with values.
    @param   seed        The starting value.
    @param   offset      The offset for the values.

 */
void kmo_test_fill_image(cpl_image *img,
                         float seed,
                         float offset)
{
    int         i       = 0,
                j       = 0;
    cpl_size    d       = 0,
                x       = 0,
                y       = 0;
    float       *data   = NULL,
                f       = 0.0;

    KMO_TRY
    {
        x = cpl_image_get_size_x(img);
        y = cpl_image_get_size_y(img);


        data = cpl_image_get_data_float(img);
        KMO_TRY_EXIT_IF_NOT(data != NULL);

        // horizontal stripes: bottom = 0, top = seed * (y-1)
        for (i = 0; i < y; i++) {
            for (j = 0; j < x; j++) {
                data[d] = seed + f;
                d++;
                f += offset;
            }
        }
    }
    KMO_CATCH
    {
    }
}

/**
    @brief Fills a cube with increasing values.

    @param   cube        The cube to fill with values.
    @param   seed        The starting value.
    @param   offset      The offset for the values.

 */
void kmo_test_fill_cube(cpl_imagelist *cube,
                        float seed,
                        float offset)
{
    int         i       = 0,
                size    = 0;

    KMO_TRY
    {
        size = cpl_imagelist_get_size(cube);
        KMO_TRY_EXIT_IF_NOT(size > 0);

        for (i = 0; i < size; i++) {
            kmo_test_fill_image(cpl_imagelist_get(cube, i),
                                seed, offset);
                seed += offset;
        }
    }
    KMO_CATCH
    {
    }
}

/**
    @brief Fills an image with a mask. Border is zero, the middle contains ones.

    @param   img         The image to fill with a mask.

 */
void kmo_test_fill_mask(cpl_image *img)
{
    int         i       = 0,
                j       = 0,
                x       = 0,
                y       = 0;

    float       *data   = NULL;

    KMO_TRY
    {
        x = cpl_image_get_size_x(img);
        y = cpl_image_get_size_y(img);


        data = cpl_image_get_data_float(img);
        KMO_TRY_EXIT_IF_NOT(data != NULL);

        // horizontal stripes: bottom = 0, top = seed * (y-1)
        for (i = 0; i < y; i++) {
            for (j = 0; j < x; j++) {

                if ((i == 0) || (i == y-1) || (j == 0) || (j == x-1)) {
                    data[j+i*x] = 0.0;
                } else {
                    data[j+i*x] = 1.0;
                }
            }
        }
    }
    KMO_CATCH
    {
    }
}

/**
    @brief Allocates images in an empty imagelist.

    @param   cube        The cube to allocate.
    @param   x           Size in x-dimension.
    @param   y           Size in y-dimension.
    @param   z           Size in z-dimension.

 */
void kmo_test_alloc_cube(cpl_imagelist *cube,
                         int x,
                         int y,
                         int z)
{
    int         i       = 0;
    cpl_image   *img    = NULL;

    KMO_TRY
    {
        KMO_TRY_EXIT_IF_NOT(cpl_imagelist_get_size(cube) == 0);

        for (i = 0; i < z; i++) {
            KMO_TRY_EXIT_IF_NULL(
                img = cpl_image_new(x, y, CPL_TYPE_FLOAT));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_imagelist_set(cube, img, i));
        }
    }
    KMO_CATCH
    {
    }
}

/**
    @brief Initialises a vector to zero.

    @param   vec         The vector to initialise.

 */
void kmo_test_init_vec(cpl_vector *vec)
{
    double      *data   = NULL;
    int         i       = 0;

    KMO_TRY
    {
        data = cpl_vector_get_data (vec);
        KMO_TRY_EXIT_IF_NOT(data != NULL);

        for (i = 0; i < cpl_vector_get_size(vec); i++) {
            data[i] = 0.0;
        }
    }
    KMO_CATCH
    {
    }
}

/**
    @brief Fills a cube with data either serial or gaussian data.

    @param   x      The 1st dimension.
    @param   y      The 2nd dimension.
    @param   z      The 3rd dimension.
    @param   gauss  TRUE if images should contain a gauss, FALSE otherwise.

    @return  The generated imagelist.
 */
cpl_imagelist* kmo_test_create_cube(int x, int y, int z, int gauss, int *offset)
{
    cpl_imagelist   *imglist    = NULL;
    cpl_image       *img        = NULL;

    int             i           = 0;

    KMO_TRY
    {
        imglist = cpl_imagelist_new();
        KMO_TRY_EXIT_IF_NOT(imglist != NULL);

        for (i = 0; i < z; i++) {
            img = cpl_image_new(x, y, CPL_TYPE_FLOAT);
            KMO_TRY_EXIT_IF_NOT(img != NULL);

            if (gauss == FALSE) {
                kmo_test_fill_image(img, (*offset)++, test_global_seed_data);
            } else {
                KMO_TRY_EXIT_IF_ERROR(
                    cpl_image_fill_gaussian(img, x/2, y/2,
                                            *offset + test_global_seed_data,
                                            x/4, y/4));
            }

            cpl_imagelist_set(imglist, img, i);
        }
    }
    KMO_CATCH
    {
    }

    return imglist;
}

/**
    @brief Creates the primary extension and saves it.

    @param   filename        The filename where to save the primary extension.
    @param   kmos_type       The type of KMOS-fits-file format.
    @param   primary_keys    The array containing additional keywords.
    @param   primary_vals    The array containing the additional values as
                             strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords.
    @param   nr_primary_keys The number of additional keywords to add.

 */
void kmo_test_create_primary_extension(const char *filename,
                                       char *primary_keys[],
                                       char *primary_vals[],
                                       int primary_types[],
                                       int nr_primary_keys)
{
    cpl_propertylist    *header = NULL;

    KMO_TRY
    {
        header = kmclipm_propertylist_load(test_global_raw_path, 0);
        KMO_TRY_EXIT_IF_NOT(header != NULL);

        KMO_TRY_EXIT_IF_ERROR(
            kmo_test_update_keywords(header,
                                     primary_keys, primary_vals,
                                     primary_types, nr_primary_keys));

        KMO_TRY_EXIT_IF_ERROR(
            cpl_propertylist_save(header, filename, CPL_IO_CREATE));
    }
    KMO_CATCH
    {
    }

    cpl_propertylist_delete(header); header = NULL;
}

/**
    @brief Creates a RAW file initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
*/
int  kmo_test_create_RAW_data(int x,
                              int y,
                              const char *filename,
                              char *primary_keys[],
                              char *primary_vals[],
                              int primary_types[],
                              int nr_primary_keys,
                              char *sub_keys[],
                              char *sub_vals[],
                              int sub_types[],
                              int nr_sub_keys,
                              float seed,
                              float offset)
{
    int                 i           = 0,
                        ret         = 0;
    cpl_image           *img        = NULL;
    cpl_propertylist    *header     = NULL;

    KMO_TRY
    {
        // load header from dummy
        KMO_TRY_EXIT_IF_NULL(
            header = kmclipm_propertylist_load(test_global_raw_path, 0));

        KMO_TRY_EXIT_IF_ERROR(
            kmo_test_update_keywords(header, primary_keys, primary_vals,
                                     primary_types, nr_primary_keys));

        cpl_image_save(NULL, filename, CPL_BPP_IEEE_FLOAT, header,
                        CPL_IO_DEFAULT);

        cpl_propertylist_delete(header); header = NULL;

        // copy extensions
        for (i = 1; i <= KMOS_NR_DETECTORS; i++) {
            KMO_TRY_EXIT_IF_NULL(
                img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

            kmo_test_fill_image(img, i - 1+offset, seed);

//            KMO_TRY_EXIT_IF_NULL(
//                header = kmclipm_propertylist_load(test_global_raw_path, i));
            KMO_TRY_EXIT_IF_NULL(
                header = cpl_propertylist_new());
            char *tmpstr = cpl_sprintf("CHIP%d.INT1", i);
            kmclipm_update_property_string(header, EXTNAME, tmpstr,
                                           "FITS extension name");
            cpl_free(tmpstr); tmpstr = NULL;
            kmclipm_update_property_int(header, CHIPINDEX, i, "Chip index");
            kmclipm_update_property_double(header, GAIN, 1, "Gain in e-/ADU");

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            cpl_image_save(img, filename, CPL_BPP_IEEE_FLOAT,
                            header, CPL_IO_EXTEND);

            cpl_image_delete(img); img = NULL;
            cpl_propertylist_delete(header); header = NULL;
        }
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_image_delete(img); img = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a RAW file with zero values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   filename        The filename where to save the primary extension.
 */
int  kmo_test_create_RAW_data_zero(int x,
                                   int y,
                                   const char *filename)
{
    int                 i           = 0,
                        ret         = 0;
    cpl_image           *img        = NULL;
    cpl_propertylist    *header     = NULL;

    KMO_TRY
    {
        // load header from dummy
        KMO_TRY_EXIT_IF_NULL(
            header = kmclipm_propertylist_load(test_global_raw_path, 0));

        cpl_image_save(NULL, filename, CPL_BPP_IEEE_FLOAT, header,
                        CPL_IO_DEFAULT);

        cpl_propertylist_delete(header); header = NULL;

        // copy extensions
        for (i = 1; i <= KMOS_NR_DETECTORS; i++) {
            KMO_TRY_EXIT_IF_NULL(
                img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

//            KMO_TRY_EXIT_IF_NULL(
//                header = kmclipm_propertylist_load(test_global_raw_path, i));
            KMO_TRY_EXIT_IF_NULL(
                header = cpl_propertylist_new());
            char *tmpstr = cpl_sprintf("CHIP%d.INT1", i);
            kmclipm_update_property_string(header, EXTNAME, tmpstr,
                                           "FITS extension name");
            cpl_free(tmpstr); tmpstr = NULL;
            kmclipm_update_property_int(header, CHIPINDEX, i, "Chip index");
            kmclipm_update_property_double(header, GAIN, 1, "Gain in e-/ADU");

            cpl_image_save(img, filename, CPL_BPP_IEEE_FLOAT,
                            header, CPL_IO_EXTEND);

            cpl_image_delete(img); img = NULL;
            cpl_propertylist_delete(header); header = NULL;
        }
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_image_delete(img); img = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a F2D file with data only initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
 */
int  kmo_test_create_F2D_data(int x,
                              int y,
                              int mask,
                              const char *filename,
                              char *primary_keys[],
                              char *primary_vals[],
                              int primary_types[],
                              int nr_primary_keys,
                              char *sub_keys[],
                              char *sub_vals[],
                              int sub_types[],
                              int nr_sub_keys)
{
    cpl_image           *img        = NULL;
    cpl_propertylist    *header     = NULL;
    int                 i           = 0,
                        ret         = 0;

    KMO_TRY
    {
        kmo_test_create_primary_extension(filename,
                                          primary_keys, primary_vals,
                                          primary_types,nr_primary_keys);

        // create sub-headers and extensions
        KMO_TRY_EXIT_IF_NULL(
            img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

        for (i = 0; i < KMOS_NR_DETECTORS; i++) {
//            KMO_TRY_EXIT_IF_NULL(
//                header = kmclipm_propertylist_load(test_global_raw_path, i + 1));
            KMO_TRY_EXIT_IF_NULL(
                header = cpl_propertylist_new());

            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(header, FALSE, FALSE,
                                        detector_frame, i + 1));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            if (mask == FALSE) {
                kmo_test_fill_image(img, i, test_global_seed_data);
            } else {
                kmo_test_fill_mask(img);
            }

            cpl_image_save(img, filename, CPL_BPP_IEEE_FLOAT, header,
                           CPL_IO_EXTEND);

            cpl_propertylist_delete(header); header = NULL;
        }
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_image_delete(img); img = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a F2D file with data and noise initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
 */
int  kmo_test_create_F2D_data_noise(int x,
                                    int y,
                                    const char *filename,
                                    char *primary_keys[],
                                    char *primary_vals[],
                                    int primary_types[],
                                    int nr_primary_keys,
                                    char *sub_keys[],
                                    char *sub_vals[],
                                    int sub_types[],
                                    int nr_sub_keys)
{
    cpl_propertylist    *header     = NULL;
    cpl_image           *img        = NULL;
    int                 i           = 0,
                        ret         = 0;

    KMO_TRY
    {

        kmo_test_create_F2D_data(x, y, FALSE, filename,
                                 primary_keys, primary_vals, primary_types,
                                 nr_primary_keys,
                                 sub_keys, sub_vals, sub_types, nr_sub_keys);

        KMO_TRY_EXIT_IF_NULL(
            header = kmclipm_propertylist_load(filename, 0));

        cpl_image_save(NULL, "tmp.fits", CPL_BPP_IEEE_FLOAT,
                       header, CPL_IO_DEFAULT);

        cpl_propertylist_delete(header); header = NULL;

         // subsequent extensions, noise
        for (i = 1; i <= KMOS_NR_DETECTORS; i++) {
            // load and resave data
            KMO_TRY_EXIT_IF_NULL(
                img = kmclipm_image_load(filename, CPL_TYPE_FLOAT, 0, i));

            KMO_TRY_EXIT_IF_NULL(
                header = kmclipm_propertylist_load(filename, i));

            cpl_image_save(img, "tmp.fits", CPL_BPP_IEEE_FLOAT, header,
                           CPL_IO_EXTEND);

            cpl_propertylist_delete(header); header = NULL;
            cpl_image_delete(img); img = NULL;

            // load noise header, generate noise, save
//            KMO_TRY_EXIT_IF_NULL(
//                header = kmclipm_propertylist_load(test_global_raw_path, i));
            KMO_TRY_EXIT_IF_NULL(
                header = cpl_propertylist_new());

            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(header, TRUE, FALSE, detector_frame, i));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            KMO_TRY_EXIT_IF_NULL(
                img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

            kmo_test_fill_image(img, i - 1, test_global_seed_noise);

            cpl_image_save(img, "tmp.fits", CPL_BPP_IEEE_FLOAT, header,
                           CPL_IO_EXTEND);

            cpl_propertylist_delete(header); header = NULL;
            cpl_image_delete(img); img = NULL;
        }
        rename("tmp.fits", filename);
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_image_delete(img); img = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a F2I file with data only initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   mask            If output should be a mask.
    @param   nr_images       The number of extensions the file will have.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
 */
int  kmo_test_create_F2I_data(int x,
                              int y,
                              int mask,
                              int nr_images,
                              const char *filename,
                              char *primary_keys[],
                              char *primary_vals[],
                              int primary_types[],
                              int nr_primary_keys,
                              char *sub_keys[],
                              char *sub_vals[],
                              int sub_types[],
                              int nr_sub_keys)
{
    cpl_image        *img           = NULL,
                     *tmp           = NULL;
    cpl_propertylist *header        = NULL;
    int              i              = 0,
                     offset         = 0,
                     ret            = 0;

    KMO_TRY
    {
        kmo_test_create_primary_extension(filename,
                                          primary_keys, primary_vals,
                                          primary_types,nr_primary_keys);


        // create sub-headers and extensions
        KMO_TRY_EXIT_IF_NULL(
            img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

        KMO_TRY_EXIT_IF_NULL(
            header = cpl_propertylist_new());

        for (i = 0; i < nr_images; i++) {
            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(header, FALSE, FALSE, ifu_frame, i + 1));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            if (test_global_valid[i] == TRUE) {
                if (mask == FALSE) {
                    kmo_test_fill_image(img, offset++, test_global_seed_data);
                } else {
                    kmo_test_fill_mask(img);
                }

                tmp = img;
            } else {
                tmp = NULL;
            }

            cpl_image_save(tmp, filename, CPL_BPP_IEEE_FLOAT, header,
                           CPL_IO_EXTEND);
        }
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_image_delete(img); img = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a F2I file with data and noise initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   nr_images       The number of extensions the file will have.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
 */
int  kmo_test_create_F2I_data_noise(int x,
                                    int y,
                                    int nr_images,
                                    const char *filename,
                                    char *primary_keys[],
                                    char *primary_vals[],
                                    int primary_types[],
                                    int nr_primary_keys,
                                    char *sub_keys[],
                                    char *sub_vals[],
                                    int sub_types[],
                                    int nr_sub_keys)
{
    cpl_propertylist    *header         = NULL;
    cpl_image           *img            = NULL;
    int                 i               = 0,
                        offset          = 0,
                        ret             = 0,
                        id              = -1;
    enum kmo_frame_type ttype           = illegal_frame;
    char                content[64],
                        *extname        = NULL;

    KMO_TRY
    {
        kmo_test_create_F2I_data(x, y, FALSE, nr_images, filename,
                                 primary_keys, primary_vals, primary_types,
                                 nr_primary_keys,
                                 sub_keys, sub_vals, sub_types, nr_sub_keys);

        KMO_TRY_EXIT_IF_NULL(
            header = kmclipm_propertylist_load(filename, 0));

        cpl_image_save(NULL, "tmp.fits", CPL_BPP_IEEE_FLOAT,
                       header, CPL_IO_DEFAULT);

        cpl_propertylist_delete(header); header = NULL;

        // create subsequent noise extensions
        for (i = 1; i <= nr_images; i++) {
            // load and resave data
            KMO_TRY_EXIT_IF_NULL(
                header = kmclipm_propertylist_load(filename, i));

            if (test_global_valid[i - 1] == TRUE) {
                KMO_TRY_EXIT_IF_NULL(
                    img = kmclipm_image_load(filename, CPL_TYPE_FLOAT, 0, i));

                cpl_image_save(img, "tmp.fits", CPL_BPP_IEEE_FLOAT, header,
                               CPL_IO_EXTEND);

                cpl_image_delete(img); img = NULL;
            } else {
                cpl_propertylist_save(header, "tmp.fits", CPL_IO_EXTEND);
            }

            KMO_TRY_EXIT_IF_ERROR(
                kmo_extname_extractor(cpl_propertylist_get_string(header,
                                                                  EXTNAME),
                                      &ttype,
                                      &id,
                                      content));

            // update noise header, generate noise, save
            KMO_TRY_EXIT_IF_NULL(
                extname = kmo_extname_creator(ttype, id, EXT_NOISE));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_string(header,
                                        EXTNAME,
                                        extname,
                                        "FITS extension name"));
            cpl_free(extname); extname = NULL;

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            if (test_global_valid[i - 1] == TRUE) {
                KMO_TRY_EXIT_IF_NULL(
                    img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

                kmo_test_fill_image(img, offset++, test_global_seed_noise);

                cpl_image_save(img, "tmp.fits", CPL_BPP_IEEE_FLOAT, header,
                            CPL_IO_EXTEND);

                cpl_image_delete(img); img = NULL;
            } else {
                cpl_propertylist_save(header, "tmp.fits", CPL_IO_EXTEND);
            }

            cpl_propertylist_delete(header); header = NULL;
        }
        rename("tmp.fits", filename);
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_image_delete(img); img = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a F3I file with data only initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   z               The size of the 3rd dimension.
    @param   gauss           TRUE if cube should be filled with gaussian data,
                             FALSE if it should be filled with linearly
                             increasing values.
    @param   nr_cubes        The number of extensions the file will have.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
 */
int  kmo_test_create_F3I_data(int x,
                              int y,
                              int z,
                              int gauss,
                              int nr_cubes,
                              const char *filename,
                              char *primary_keys[],
                              char *primary_vals[],
                              int primary_types[],
                              int nr_primary_keys,
                              char *sub_keys[],
                              char *sub_vals[],
                              int sub_types[],
                              int nr_sub_keys)
{
    cpl_imagelist       *cube           = NULL;
    cpl_propertylist    *header         = NULL;
    int                 j               = 0,
                        ret             = 0,
                        offset          = 0;

    KMO_TRY
    {
        kmo_test_create_primary_extension(filename,
                                          primary_keys, primary_vals,
                                          primary_types,nr_primary_keys);

        // generate data
        KMO_TRY_EXIT_IF_NULL(
            header = cpl_propertylist_new());

        for (j = 0; j < nr_cubes; j++) {
            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(header, FALSE, FALSE, ifu_frame, j + 1));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));
            if (test_global_valid[j] == TRUE) {
                KMO_TRY_EXIT_IF_NULL(
                    cube = kmo_test_create_cube(x, y, z, gauss, &offset));
                KMO_TRY_EXIT_IF_ERROR(
                    cpl_imagelist_save(cube, filename, CPL_BPP_IEEE_FLOAT,
                                       header, CPL_IO_EXTEND));
                cpl_imagelist_delete(cube); cube = NULL;
            } else {
                KMO_TRY_EXIT_IF_ERROR(
                    cpl_propertylist_save(header, filename, CPL_IO_EXTEND));
            }
        }
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief
        Creates a F3I file with data only initialised with some values.
        Some values are inifinite

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   z               The size of the 3rd dimension.
    @param   nr_cubes        The number of extensions the file will have.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
 */
int  kmo_test_create_F3I_data_infinite(int x,
                              int y,
                              int z,
                              int nr_cubes,
                              const char *filename,
                              char *primary_keys[],
                              char *primary_vals[],
                              int primary_types[],
                              int nr_primary_keys,
                              char *sub_keys[],
                              char *sub_vals[],
                              int sub_types[],
                              int nr_sub_keys)
{
    cpl_imagelist       *imglist        = NULL;
    cpl_propertylist    *header         = NULL;
    cpl_image           *img            = NULL;
    int                 i               = 0,
                        j               = 0,
                        offset          = 0,
                        ret             = 0,
                        count           = 0;

    KMO_TRY
    {
        kmo_test_create_primary_extension(filename,
                                          primary_keys, primary_vals,
                                          primary_types,nr_primary_keys);

        // generate data
        KMO_TRY_EXIT_IF_NULL(
            imglist = cpl_imagelist_new());

        KMO_TRY_EXIT_IF_NULL(
            header = cpl_propertylist_new());

        for (j = 0; j < nr_cubes; j++) {
            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(header, FALSE, FALSE, ifu_frame, j + 1));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));
            if (test_global_valid[j] == TRUE) {
                for (i = 0; i < z; i++) {
                    KMO_TRY_EXIT_IF_NULL(
                        img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

                    kmo_test_fill_image(img, offset++, test_global_seed_data);
                    KMO_TRY_CHECK_ERROR_STATE();

                    // add infinite values here
                    if (count == 2) {
                        // add -inf
                        cpl_image_set(img, 2, 1, -1.0/0.0);
                        count++;
                    }
                    if (count == 1) {
                        // add inf
                        cpl_image_set(img, 2, 1, 1.0/0.0);
                        count++;
                    }
                    if (count == 0) {
                        // add nan
                        cpl_image_set(img, 1, 1, 0.0/0.0);
                        count++;
                    }

                    cpl_imagelist_set(imglist, img, i);
                }

                cpl_imagelist_save(imglist, filename, CPL_BPP_IEEE_FLOAT,
                                header, CPL_IO_EXTEND);
            } else {
                cpl_propertylist_save(header, filename, CPL_IO_EXTEND);
            }
        }
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_imagelist_delete(imglist); imglist = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a F3I file with data only initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   z               The size of the 3rd dimension.
    @param   nr_cubes        The number of extensions the file will have.
    @param   val             The Array defining if the IFUs are active or not.
                             It must have the same length as @c nr_cubes .
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
*/
int  kmo_test_create_F3I_data2(int x,
                              int y,
                              int z,
                              int nr_cubes,
                              int val[],
                              const char *filename,
                              char *primary_keys[],
                              char *primary_vals[],
                              int primary_types[],
                              int nr_primary_keys,
                              char *sub_keys[],
                              char *sub_vals[],
                              int sub_types[],
                              int nr_sub_keys)
{
    cpl_imagelist       *imglist        = NULL;
    cpl_propertylist    *header         = NULL;
    cpl_image           *img            = NULL;
    int                 i               = 0,
                        j               = 0,
                        offset          = 0,
                        ret             = 0;

    KMO_TRY
    {
        kmo_test_create_primary_extension(filename,
                                          primary_keys, primary_vals,
                                          primary_types,nr_primary_keys);

        // generate data
        KMO_TRY_EXIT_IF_NULL(
            imglist = cpl_imagelist_new());

        KMO_TRY_EXIT_IF_NULL(
            header = cpl_propertylist_new());

        for (j = 0; j < nr_cubes; j++) {
            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(header, FALSE, FALSE, ifu_frame, j + 1));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            if (val[j] == TRUE) {
                for (i = 0; i < z; i++) {
                    KMO_TRY_EXIT_IF_NULL(
                        img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

                    kmo_test_fill_image(img, offset++, test_global_seed_data);

                    cpl_imagelist_set(imglist, img, i);
                }

                cpl_imagelist_save(imglist, filename, CPL_BPP_IEEE_FLOAT,
                                header, CPL_IO_EXTEND);
            } else {
                cpl_propertylist_save(header, filename, CPL_IO_EXTEND);
            }
        }
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_imagelist_delete(imglist); imglist = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a F3I file with data and noise initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   z               The size of the 3rd dimension.
    @param   nr_cubes        The number of extensions the file will have.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
*/
int  kmo_test_create_F3I_data_noise(int x,
                                    int y,
                                    int z,
                                    int gauss,
                                    int nr_cubes,
                                    const char *filename,
                                    char *primary_keys[],
                                    char *primary_vals[],
                                    int primary_types[],
                                    int nr_primary_keys,
                                    char *sub_keys[],
                                    char *sub_vals[],
                                    int sub_types[],
                                    int nr_sub_keys)
{
    cpl_image           *img            = NULL;
    cpl_imagelist       *imglist        = NULL;
    int                 i               = 0,
                        j               = 0,
                        offset          = 0,
                        ret             = 0,
                        id              = -1;
    cpl_propertylist    *header         = NULL;
    enum kmo_frame_type ttype           = illegal_frame;
    char                content[64],
                        *extname        = NULL;
    KMO_TRY
    {
        kmo_test_create_F3I_data(x, y, z, gauss, nr_cubes, filename,
                                 primary_keys, primary_vals, primary_types,
                                 nr_primary_keys,
                                 sub_keys, sub_vals, sub_types, nr_sub_keys);

        KMO_TRY_EXIT_IF_NULL(
            header = kmclipm_propertylist_load(filename, 0));

        cpl_image_save(NULL, "tmp.fits", CPL_BPP_IEEE_FLOAT,
                       header, CPL_IO_DEFAULT);

        cpl_propertylist_delete(header); header = NULL;

        // create subsequent noise extensions
        for (j = 1; j <= nr_cubes; j++) {
            // load and resave data
            KMO_TRY_EXIT_IF_NULL(
                header = kmclipm_propertylist_load(filename, j));

            if (test_global_valid[j - 1] == TRUE) {
                KMO_TRY_EXIT_IF_NULL(
                    imglist = kmclipm_imagelist_load(filename, CPL_TYPE_FLOAT, j));

                cpl_imagelist_save(imglist, "tmp.fits", CPL_BPP_IEEE_FLOAT,
                                   header, CPL_IO_EXTEND);

                cpl_imagelist_delete(imglist); imglist = NULL;
            } else {
                cpl_propertylist_save(header, "tmp.fits", CPL_IO_EXTEND);
            }

            KMO_TRY_EXIT_IF_ERROR(
                kmo_extname_extractor(cpl_propertylist_get_string(header, EXTNAME),
                                      &ttype, &id, content));
            // update noise header, generate noise, save
            KMO_TRY_EXIT_IF_NULL(
                extname = kmo_extname_creator(ttype, id, EXT_NOISE));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_string(header,
                                        EXTNAME,
                                        extname,
                                        "FITS extension name"));
            cpl_free(extname); extname = NULL;

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            if (test_global_valid[j - 1] == TRUE) {
                KMO_TRY_EXIT_IF_NULL(
                    imglist = cpl_imagelist_new());

                for (i = 0; i < z; i++) {
                    KMO_TRY_EXIT_IF_NULL(
                        img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

                    if (gauss == FALSE) {
                        kmo_test_fill_image(img, offset++, test_global_seed_noise);
                    } else {
                        KMO_TRY_EXIT_IF_ERROR(
                            cpl_image_fill_gaussian(img, x/2, y/2,
                                                    offset + test_global_seed_noise,
                                                    x/4, y/4));
                    }

                    cpl_imagelist_set(imglist, img, i);
                }

                cpl_imagelist_save(imglist, "tmp.fits", CPL_BPP_IEEE_FLOAT,
                                   header, CPL_IO_EXTEND);

                cpl_imagelist_delete(imglist); imglist = NULL;
            } else {
                cpl_propertylist_save(header, "tmp.fits", CPL_IO_EXTEND);

            }

            cpl_propertylist_delete(header); header = NULL;
        }
        rename("tmp.fits", filename);
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }
    cpl_imagelist_delete(imglist); imglist = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a F1I file with data only initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   nr_vectors      The number of extensions the file will have.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
*/
int  kmo_test_create_F1I_data(int x,
                              int nr_vectors,
                              const char *filename,
                              char *primary_keys[],
                              char *primary_vals[],
                              int primary_types[],
                              int nr_primary_keys,
                              char *sub_keys[],
                              char *sub_vals[],
                              int sub_types[],
                              int nr_sub_keys)
{
    cpl_vector          *vec            = NULL,
                        *tmp            = NULL;
    cpl_propertylist    *header         = NULL;
    int                 i               = 0,
                        ret             = 0;

    KMO_TRY
    {
        kmo_test_create_primary_extension(filename,
                                          primary_keys, primary_vals,
                                          primary_types,nr_primary_keys);

        // generate data
        KMO_TRY_EXIT_IF_NULL(
            vec = cpl_vector_new(x));

        KMO_TRY_EXIT_IF_NULL(
            header = cpl_propertylist_new());

        for (i = 0; i < nr_vectors; i++) {
            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(header, FALSE, FALSE, ifu_frame, i + 1));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            if (test_global_valid[i] == TRUE) {
                kmo_test_init_vec(vec);

                kmo_test_fill_vector(vec, i, test_global_seed_data);
                tmp = vec;
            } else {
                tmp = NULL;
            }

            cpl_vector_save(tmp, filename, CPL_BPP_IEEE_FLOAT, header,
                            CPL_IO_EXTEND);
        }

    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_vector_delete(vec); vec = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a F1I file with data and noise initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   nr_vectors      The number of extensions the file will have.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
*/
int  kmo_test_create_F1I_data_noise(int x,
                                    int nr_vectors,
                                    const char *filename,
                                    char *primary_keys[],
                                    char *primary_vals[],
                                    int primary_types[],
                                    int nr_primary_keys,
                                    char *sub_keys[],
                                    char *sub_vals[],
                                    int sub_types[],
                                    int nr_sub_keys)
{
    cpl_propertylist    *header     = NULL;
    kmclipm_vector      *vec        = NULL;
    int                 i           = 0,
                        ret         = 0,
                        id          = -1;
    enum kmo_frame_type ttype       = illegal_frame;
    char                content[64],
                        *extname    = NULL;

    KMO_TRY
    {
        kmo_test_create_F1I_data(x, nr_vectors, filename,
                                 primary_keys, primary_vals, primary_types,
                                 nr_primary_keys,
                                 sub_keys, sub_vals, sub_types, nr_sub_keys);


        KMO_TRY_EXIT_IF_NULL(
            header = kmclipm_propertylist_load(filename, 0));

        cpl_vector_save(NULL, "tmp.fits", CPL_BPP_IEEE_FLOAT,
                       header, CPL_IO_DEFAULT);

        cpl_propertylist_delete(header); header = NULL;

        // create subsequent noise extensions
        for (i = 1; i <= nr_vectors; i++) {
            // load and resave data
            KMO_TRY_EXIT_IF_NULL(
                header = kmclipm_propertylist_load(filename, i));

            if (test_global_valid[i - 1] == TRUE) {
                KMO_TRY_EXIT_IF_NULL(
                    vec = kmclipm_vector_load(filename, i));

               kmclipm_vector_save(vec, "tmp.fits", CPL_BPP_IEEE_FLOAT, header,
                                   CPL_IO_EXTEND, 0./0.);

                kmclipm_vector_delete(vec); vec = NULL;
            } else {
                cpl_propertylist_save(header, "tmp.fits", CPL_IO_EXTEND);
            }

            KMO_TRY_EXIT_IF_ERROR(
                kmo_extname_extractor(cpl_propertylist_get_string(header, EXTNAME),
                                      &ttype, &id, content));
            // update noise header, generate noise, save
            KMO_TRY_EXIT_IF_NULL(
                extname = kmo_extname_creator(ttype, id, EXT_NOISE));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_string(header,
                                        EXTNAME,
                                        extname,
                                        "FITS extension name"));
            cpl_free(extname); extname = NULL;

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            if (test_global_valid[i - 1] == TRUE) {
                cpl_vector *vec2 = NULL;
                KMO_TRY_EXIT_IF_NULL(
                    vec2 = cpl_vector_new(x));

                kmo_test_init_vec(vec2);

                kmo_test_fill_vector(vec2, i - 1, test_global_seed_noise);

                vec = kmclipm_vector_create(vec2);

                kmclipm_vector_save(vec, "tmp.fits", CPL_BPP_IEEE_FLOAT, header,
                               CPL_IO_EXTEND, 0./0.);

                kmclipm_vector_delete(vec); vec = NULL;
            } else {
                cpl_propertylist_save(header, "tmp.fits", CPL_IO_EXTEND);
            }

            cpl_propertylist_delete(header); header = NULL;
        }
        rename("tmp.fits", filename);
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    kmclipm_vector_delete(vec); vec = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}

/**
    @brief Creates a B2D file with data only initialised with some values.

    @param   x               The size of the 1st dimension.
    @param   y               The size of the 2nd dimension.
    @param   filename        The filename where to save the primary extension.
    @param   primary_keys    The array containing additional keywords for the
                             primary header.
    @param   primary_vals    The array containing the additional values for the
                             primary header as strings.
    @param   primary_types   The array containing the additional CPL-types for
                             the keywords for the primary header.
    @param   nr_primary_keys The number of additional keywords to add to the
                             primary header.
    @param   sub_keys        The array containing additional keywords for the
                             subsequent headers.
    @param   sub_vals        The array containing the additional values for the
                             subsequent headers as strings.
    @param   sub_types       The array containing the additional CPL-types for
                             the keywords for the subsequent headers.
    @param   nr_sub_keys     The number of additional keywords to add to the
                             subsequent headers.
 */
int  kmo_test_create_B2D_data(int x,
                              int y,
                              int mask,
                              const char *filename,
                              char *primary_keys[],
                              char *primary_vals[],
                              int primary_types[],
                              int nr_primary_keys,
                              char *sub_keys[],
                              char *sub_vals[],
                              int sub_types[],
                              int nr_sub_keys)
{
    cpl_image           *img        = NULL;
    cpl_propertylist    *header     = NULL;
    int                 i           = 0,
                        ret         = 0;

    KMO_TRY
    {
        kmo_test_create_primary_extension(filename,
                                          primary_keys, primary_vals,
                                          primary_types,nr_primary_keys);

        // create sub-headers and extensions
        KMO_TRY_EXIT_IF_NULL(
            img = cpl_image_new(x, y, CPL_TYPE_FLOAT));

        for (i = 0; i < KMOS_NR_DETECTORS; i++) {
//            KMO_TRY_EXIT_IF_NULL(
//                header = kmclipm_propertylist_load(test_global_raw_path, i + 1));
            KMO_TRY_EXIT_IF_NULL(
                header = cpl_propertylist_new());

            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(header, FALSE, TRUE,
                                        detector_frame, i + 1));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_test_update_keywords(header, sub_keys, sub_vals,
                                         sub_types, nr_sub_keys));

            if (mask == FALSE) {
                kmo_test_fill_image(img, i, test_global_seed_data);
            } else {
                kmo_test_fill_mask(img);
            }

            cpl_image_save(img, filename, CPL_BPP_IEEE_FLOAT, header,
                           CPL_IO_EXTEND);

            cpl_propertylist_delete(header); header = NULL;
        }
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        ret = -1;
    }

    cpl_image_delete(img); img = NULL;
    cpl_propertylist_delete(header); header = NULL;

    return ret;
}


/** @} */
