/*
 * 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
 */

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

/*-----------------------------------------------------------------------------
 *                              Includes
 *----------------------------------------------------------------------------*/

#include <string.h>

#include <cpl.h>

#include "kmo_debug.h"
#include "kmo_utils.h"
#include "kmo_dfs.h"
#include "kmo_error.h"
#include "kmo_priv_make_image.h"
#include "kmo_priv_functions.h"
#include "kmo_constants.h"

/*-----------------------------------------------------------------------------
 *                          Functions prototypes
 *----------------------------------------------------------------------------*/

static int kmo_make_image_create(cpl_plugin *);
static int kmo_make_image_exec(cpl_plugin *);
static int kmo_make_image_destroy(cpl_plugin *);
static int kmo_make_image(cpl_parameterlist *, cpl_frameset *);

/*-----------------------------------------------------------------------------
 *                          Static variables
 *----------------------------------------------------------------------------*/

static char kmo_make_image_description[] =
"This recipe collapses a cube along the spectral axis using rejection. By \n"
"default all spectral slices are averaged.\n"
"Errors are propagated for the same spectral ranges as for the input data if\n"
"a noise map is provided.\n"
"\n"
"BASIC PARAMETERS:\n"
"-----------------\n"
"--range\n"
"The spectral range can be delimited to one or several sub-ranges like \n"
"\"1.8,1.9\" or \"1.8,1.9; 2.0,2.11\"\n"
"\n"
"--cmethod\n"
"Following methods of frame combination are available:\n"
"   * 'ksigma' (Default)\n"
"       An iterative sigma clipping. For each position all pixels in the\n"
"       spectrum are examined. If they deviate significantly, they will be\n"
"       rejected according to the conditions:\n"
"           val > mean + stdev * cpos_rej\n"
"       and\n"
"           val < mean - stdev * cneg_rej\n"
"       where --cpos_rej, --cneg_rej and --citer are the wished parameters\n"
"       In the first iteration median and percentile level are used.\n"
"   * 'median'\n"
"       At each pixel position the median is calculated.\n"
"   * 'average'\n"
"       At each pixel position the average is calculated.\n"
"   * 'sum'\n"
"       At each pixel position the sum is calculated.\n"
"   * 'min_max'\n"
"       The specified number of min and max pixel values will be rejected.\n"
"       --cmax and --cmin apply to this method.\n"
"\n"
"ADVANCED PARAMETERS\n"
"-------------------\n"
"--threshold\n"
"Optionally an OH spectrum can be provided. In this case a threshold can be\n"
"defined. The wavelengths of values above the threshold level in the OH\n"
"spectrum are omitted in the input frame. This parameter can be combined with\n"
"the --range parameter. Negative threshold values are ignored.\n"
"Own spectra can be converted into the required F1S KMOS FITS format for the\n"
"OH spectrum using kmo_fits_stack.\n"
"\n"
"--cpos_rej\n"
"--cneg_rej\n"
"--citer\n"
"   see --cmethod='ksigma'\n"
"--cmax\n"
"--cmin\n"
"   see --cmethod='min_max'\n"
"\n"
"---------------------------------------------------------------------------\n"
"  Input files:\n"
"   DO CATG           Type   Explanation                    Required #Frames\n"
"   -------           -----  -----------                    -------- -------\n"
"   <none or any>     F3I    data frame                         Y       1   \n"
"   <none or any>     F1S    OH line spectrum                   N      0,1  \n"
"\n"
"  Output files:\n"
"   DO CATG           Type   Explanation\n"
"   -------           -----  -----------\n"
"   MAKE_IMAGE        F2I    Collapsed data cubes\n"
"---------------------------------------------------------------------------\n"
"\n";

/*----------------------------------------------------------------------------*/
/**
  @defgroup kmo_make_image     Collapse a cube to create a spatial image
 */
/*----------------------------------------------------------------------------*/

/**@{*/

/*----------------------------------------------------------------------------*/
/**
  @brief    Build the list of available plugins, for this module. 
  @param    list    the plugin list
  @return   0 if everything is ok, -1 otherwise

  Create the recipe instance and make it available to the application using the 
  interface. This function is exported.
*/
/*----------------------------------------------------------------------------*/
int cpl_plugin_get_info(cpl_pluginlist *list)
{
    cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
    cpl_plugin *plugin = &recipe->interface;

    cpl_plugin_init(plugin,
            CPL_PLUGIN_API,
            KMOS_BINARY_VERSION,
            CPL_PLUGIN_TYPE_RECIPE,
            "kmo_make_image",
            "Collapse a cube to create a spatial image",
            kmo_make_image_description,
            "Alex Agudo Berbel",
            "usd-help@eso.org",
            kmos_get_license(),
            kmo_make_image_create,
            kmo_make_image_exec,
            kmo_make_image_destroy);

    cpl_pluginlist_append(list, plugin);

    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options    
  @param    plugin  the plugin
  @return   0 if everything is ok

  Defining the command-line/configuration parameters for the recipe.
*/
/*----------------------------------------------------------------------------*/
static int kmo_make_image_create(cpl_plugin *plugin)
{
    cpl_recipe *recipe;
    cpl_parameter *p;

    /* Check that the plugin is part of a valid recipe */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else
        return -1;

    /* Create the parameters list in the cpl_recipe object */
    recipe->parameters = cpl_parameterlist_new();

    /* Fill the parameters list */
    /* --range */
    p = cpl_parameter_new_value("kmos.kmo_make_image.range", CPL_TYPE_STRING,
            "The spectral ranges to combine. e.g."
            "\"x1_start,x1_end;x2_start,x2_end\" (microns)",
            "kmos.kmo_make_image", "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "range");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --threshold (if < 0, no thresholding at all) */
    p = cpl_parameter_new_value("kmos.kmo_make_image.threshold",
            CPL_TYPE_DOUBLE, "The OH threshold level (%)",
            "kmos.kmo_make_image", 0.1);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "threshold");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    return kmos_combine_pars_create(recipe->parameters, "kmos.kmo_make_image", 
            DEF_REJ_METHOD, FALSE);
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
*/
/*----------------------------------------------------------------------------*/
static int kmo_make_image_exec(cpl_plugin *plugin)
{
    cpl_recipe  *recipe;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else return -1 ;

    return kmo_make_image(recipe->parameters, recipe->frames);
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Destroy what has been created by the 'create' function
  @param    plugin  the plugin
  @return   0 if everything is ok
*/
/*----------------------------------------------------------------------------*/
static int kmo_make_image_destroy(cpl_plugin *plugin)
{
    cpl_recipe *recipe;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else return -1 ;

    cpl_parameterlist_delete(recipe->parameters);
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Interpret the command line options and execute the data processing
  @param    parlist     the parameters list
  @param    frameset   the frames list
  @return   0 if everything is ok

  Possible _cpl_error_code_ set in this function:
  @li CPL_ERROR_ILLEGAL_INPUT      if operator not valid,
                                   if first operand not 3d or
                                   if second operand not valid
  @li CPL_ERROR_INCOMPATIBLE_INPUT if the dimensions of the two operands do
                                   not match
*/
/*----------------------------------------------------------------------------*/
static int kmo_make_image(cpl_parameterlist *parlist, cpl_frameset *frameset)
{
    const char       *cmethod             = NULL;

    double           threshold           = 0.0,
                     spec_crpix          = 0.0,
                     spec_crval          = 0.0,
                     spec_cdelt          = 0.0,
                     ifu_crpix           = 0.0,
                     ifu_crval           = 0.0,
                     ifu_cdelt           = 0.0,
                     cpos_rej            = 0.0,
                     cneg_rej            = 0.0;

    cpl_imagelist    *data_in            = NULL,
                     *noise_in           = NULL;

    cpl_image        *data_out           = NULL,
                     *noise_out          = NULL;

    const char       *ranges_txt         = NULL;

    cpl_vector       *ranges             = NULL,
                     *identified_slices  = NULL,
                     *spec_data_in       = NULL,
                     *spec_lambda_in     = NULL;

    int              ret_val             = 0,
                     nr_devices          = 0,
                     i                   = 0,
                     valid_ifu           = FALSE,
                     citer               = 0,
                     cmax                = 0,
                     cmin                = 0,
                     devnr               = 0,
                     index_data          = 0,
                     index_noise         = 0;

    cpl_propertylist *sub_header_data    = NULL,
                     *sub_header_noise   = NULL,
                     *spec_header         = NULL;

    main_fits_desc   desc1,
                     desc2;

    cpl_frame        *op1_frame          = NULL,
                     *op2_frame          = NULL;

    KMO_TRY
    {
        kmo_init_fits_desc(&desc1);
        kmo_init_fits_desc(&desc2);

        /* --- check input --- */
        KMO_TRY_ASSURE((parlist != NULL) && (frameset != NULL),
                CPL_ERROR_NULL_INPUT, "Not all input data is provided");

        KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset, "kmo_make_image") == 1,
                CPL_ERROR_ILLEGAL_INPUT,"Cannot identify RAW and CALIB frames");

        cpl_msg_info("", "--- Parameter setup for kmo_make_image ----");
        threshold = kmo_dfs_get_parameter_double(parlist,
                "kmos.kmo_make_image.threshold");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(kmo_dfs_print_parameter_help(parlist, 
                    "kmos.kmo_make_image.threshold"));

        ranges_txt = kmo_dfs_get_parameter_string(parlist,
                "kmos.kmo_make_image.range");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(kmo_dfs_print_parameter_help(parlist, 
                    "kmos.kmo_make_image.range"));
        ranges = kmo_identify_ranges(ranges_txt);
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_EXIT_IF_ERROR(kmos_combine_pars_load(parlist,
                    "kmos.kmo_make_image", &cmethod, &cpos_rej, &cneg_rej,
                    &citer, &cmin, &cmax, FALSE));
        cpl_msg_info("", "-------------------------------------------");

        KMO_TRY_ASSURE((cpl_frameset_get_size(frameset) == 1) ||
                ((cpl_frameset_get_size(frameset) == 2) && (threshold != 0.0)),
                CPL_ERROR_NULL_INPUT,
                "A cube or a cube and a OH line spectrum must be provided");

        /* Get First Frame and its Descriptor */
        op1_frame=cpl_frameset_get_position(frameset, 0) ;
        desc1 = kmo_identify_fits_header(cpl_frame_get_filename(op1_frame));
        KMO_TRY_CHECK_ERROR_STATE_MSG("Wrong File Format");
        KMO_TRY_ASSURE(desc1.fits_type == f3i_fits, CPL_ERROR_ILLEGAL_INPUT,
                "First file has wrong data type (expect F3I)");

        /* Get Second Frame and its decriptor */
        if (cpl_frameset_get_size(frameset) > 1) {
            op2_frame=cpl_frameset_get_position(frameset, 1) ;
            desc2 = kmo_identify_fits_header(cpl_frame_get_filename(op2_frame));
            KMO_TRY_CHECK_ERROR_STATE_MSG("Wrong File Format");
            KMO_TRY_ASSURE(desc2.fits_type == f1s_fits, CPL_ERROR_ILLEGAL_INPUT,
                    "Second file has wrong data type (expect F1S)");
            
            /* Load OH-lines header */
            spec_header = kmos_dfs_load_sub_header(op2_frame, 1, FALSE);
            KMO_TRY_EXIT_IF_NULL(spec_header) ;
            KMO_TRY_ASSURE(cpl_propertylist_get_int(spec_header, NAXIS) == 1,
                    CPL_ERROR_ILLEGAL_INPUT, 
                    "Second input file must be a vector");
            spec_crpix = cpl_propertylist_get_double(spec_header, CRPIX1);
            KMO_TRY_CHECK_ERROR_STATE_MSG("Cannot get CRPIX1");
            spec_crval = cpl_propertylist_get_double(spec_header, CRVAL1);
            KMO_TRY_CHECK_ERROR_STATE_MSG("Cannot get CRVAL1");
            spec_cdelt = cpl_propertylist_get_double(spec_header, CDELT1);
            KMO_TRY_CHECK_ERROR_STATE_MSG("Cannot get CDELT1");

            /* Load OH lines data */
            kmclipm_vector * tmp_vec ;
            tmp_vec = kmos_dfs_load_vector(op2_frame, 1, FALSE) ;
            spec_data_in = kmclipm_vector_create_non_rejected(tmp_vec);
            kmclipm_vector_delete(tmp_vec);
            KMO_TRY_EXIT_IF_NULL(spec_data_in) ;

            /* Convert threshold from percentage to absolute value */
            threshold = threshold * cpl_vector_get_max(spec_data_in);

            // create lambda-vector for OH-lines
            KMO_TRY_EXIT_IF_NULL(
                spec_lambda_in = kmo_create_lambda_vec(
                    cpl_vector_get_size(spec_data_in), 
                    (int)spec_crpix, spec_crval, spec_cdelt));
        }

        /* --- load, update & save primary header --- */
        KMO_TRY_EXIT_IF_ERROR(kmo_dfs_save_main_header(frameset, MAKE_IMAGE, 
                    "", op1_frame, NULL, parlist, cpl_func));

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

        for (i = 1; i <= nr_devices; i++) {

            /* Get the Device Nb */
            if (desc1.ex_noise == FALSE) {
                devnr = desc1.sub_desc[i - 1].device_nr;
            } else {
                devnr = desc1.sub_desc[2 * i - 1].device_nr;
            }

            /* Get the Data index */
            if (desc1.ex_badpix == FALSE) {
                index_data = kmo_identify_index_desc(desc1, devnr, FALSE);
            } else {
                index_data = kmo_identify_index_desc(desc1, devnr, 2);
            }
            KMO_TRY_CHECK_ERROR_STATE();

            /* Get the Noise index */
            if (desc1.ex_noise) {
                index_noise = kmo_identify_index_desc(desc1, devnr, TRUE);
            }
            KMO_TRY_CHECK_ERROR_STATE();

            /* Load the Extension Header */
            sub_header_data = kmos_dfs_load_sub_header(op1_frame, devnr, FALSE);
            KMO_TRY_EXIT_IF_NULL(sub_header_data) ;

            /* Check if IFU is valid */
            valid_ifu = FALSE;
            if (desc1.sub_desc[index_data-1].valid_data == TRUE) {
                valid_ifu = TRUE;
            }

            /* Load noise anyway since we have to save it in the output */
            if (desc1.ex_noise) {
                sub_header_noise = kmos_dfs_load_sub_header(op1_frame, devnr, 
                        TRUE);
                KMO_TRY_EXIT_IF_NULL(sub_header_noise) ;
                if (cpl_propertylist_has(sub_header_noise, CRPIX3))
                    cpl_propertylist_erase(sub_header_noise, CRPIX3);
                if (cpl_propertylist_has(sub_header_noise, CRVAL3))
                    cpl_propertylist_erase(sub_header_noise, CRVAL3);
                if (cpl_propertylist_has(sub_header_noise, CDELT3))
                    cpl_propertylist_erase(sub_header_noise, CDELT3);
                if (cpl_propertylist_has(sub_header_noise, CTYPE3))
                    cpl_propertylist_erase(sub_header_noise, CTYPE3);
                if (cpl_propertylist_has(sub_header_noise, CUNIT3))
                    cpl_propertylist_erase(sub_header_noise, CUNIT3);
                if (cpl_propertylist_has(sub_header_noise, CD1_3))
                    cpl_propertylist_erase(sub_header_noise, CD1_3);
                if (cpl_propertylist_has(sub_header_noise, CD2_3))
                    cpl_propertylist_erase(sub_header_noise, CD2_3);
                if (cpl_propertylist_has(sub_header_noise, CD3_3))
                    cpl_propertylist_erase(sub_header_noise, CD3_3);
                if (cpl_propertylist_has(sub_header_noise, CD3_2))
                    cpl_propertylist_erase(sub_header_noise, CD3_2);
                if (cpl_propertylist_has(sub_header_noise, CD3_1))
                    cpl_propertylist_erase(sub_header_noise, CD3_1);
            }

            if (valid_ifu) {
                /* Load data */
                data_in = kmos_dfs_load_cube(op1_frame, devnr, FALSE) ;
                KMO_TRY_EXIT_IF_NULL(data_in) ;

                /* Load noise, if existing */
                if (desc1.ex_noise && desc1.sub_desc[index_noise-1].valid_data){
                    noise_in = kmos_dfs_load_cube(op1_frame, devnr, TRUE) ;
                    KMO_TRY_EXIT_IF_NULL(noise_in) ;
                }

                /* Interpolate oh-lines to fit input data */
                ifu_crpix = cpl_propertylist_get_double(sub_header_data,CRPIX3);
                KMO_TRY_CHECK_ERROR_STATE_MSG("CRPIX3 is missing");
                ifu_crval = cpl_propertylist_get_double(sub_header_data,CRVAL3);
                KMO_TRY_CHECK_ERROR_STATE_MSG("CRVAL3 is missing");
                ifu_cdelt = cpl_propertylist_get_double(sub_header_data,CDELT3);
                KMO_TRY_CHECK_ERROR_STATE_MSG("CDELT3 is missing");

                if (spec_data_in == NULL) {
                    identified_slices = kmo_identify_slices(ranges, ifu_crpix, 
                            ifu_crval, ifu_cdelt, desc1.naxis3);
                } else {
                    identified_slices = kmo_identify_slices_with_oh(
                            spec_data_in, spec_lambda_in, ranges, threshold,
                            ifu_crpix, ifu_crval, ifu_cdelt, desc1.naxis3);
                }
                KMO_TRY_EXIT_IF_NULL(identified_slices) ;

                if (cpl_propertylist_has(sub_header_data, CRPIX3))
                    cpl_propertylist_erase(sub_header_data, CRPIX3);
                if (cpl_propertylist_has(sub_header_data, CRVAL3))
                    cpl_propertylist_erase(sub_header_data, CRVAL3);
                if (cpl_propertylist_has(sub_header_data, CDELT3))
                    cpl_propertylist_erase(sub_header_data, CDELT3);
                if (cpl_propertylist_has(sub_header_data, CTYPE3))
                    cpl_propertylist_erase(sub_header_data, CTYPE3);
                if (cpl_propertylist_has(sub_header_data, CUNIT3))
                    cpl_propertylist_erase(sub_header_data, CUNIT3);
                if (cpl_propertylist_has(sub_header_data, CD1_3))
                    cpl_propertylist_erase(sub_header_data, CD1_3);
                if (cpl_propertylist_has(sub_header_data, CD2_3))
                    cpl_propertylist_erase(sub_header_data, CD2_3);
                if (cpl_propertylist_has(sub_header_data, CD3_3))
                    cpl_propertylist_erase(sub_header_data, CD3_3);
                if (cpl_propertylist_has(sub_header_data, CD3_2))
                    cpl_propertylist_erase(sub_header_data, CD3_2);
                if (cpl_propertylist_has(sub_header_data, CD3_1))
                    cpl_propertylist_erase(sub_header_data, CD3_1);

                /* Process & save data */
                KMO_TRY_EXIT_IF_ERROR(kmclipm_make_image(data_in, noise_in,
                            &data_out, &noise_out, identified_slices, cmethod,
                            cpos_rej, cneg_rej, citer, cmax, cmin));

                KMO_TRY_EXIT_IF_ERROR(kmo_dfs_save_image(data_out, MAKE_IMAGE,
                            "", sub_header_data, 0./0.));

                /* Process & save noise, if existing */
                if (desc1.ex_noise) {
                    KMO_TRY_EXIT_IF_ERROR(kmo_dfs_save_image(noise_out, 
                                MAKE_IMAGE, "", sub_header_noise, 0./0.));
                }

                /* Free memory */
                cpl_imagelist_delete(data_in); data_in = NULL;
                cpl_imagelist_delete(noise_in); noise_in = NULL;
                cpl_image_delete(data_out); data_out = NULL;
                cpl_image_delete(noise_out); noise_out = NULL;
                cpl_vector_delete(identified_slices); identified_slices = NULL;
            } else {
                if (cpl_propertylist_has(sub_header_data, CRPIX3))
                    cpl_propertylist_erase(sub_header_data, CRPIX3);
                if (cpl_propertylist_has(sub_header_data, CRVAL3))
                    cpl_propertylist_erase(sub_header_data, CRVAL3);
                if (cpl_propertylist_has(sub_header_data, CDELT3))
                    cpl_propertylist_erase(sub_header_data, CDELT3);
                if (cpl_propertylist_has(sub_header_data, CTYPE3))
                    cpl_propertylist_erase(sub_header_data, CTYPE3);
                if (cpl_propertylist_has(sub_header_data, CUNIT3))
                    cpl_propertylist_erase(sub_header_data, CUNIT3);
                if (cpl_propertylist_has(sub_header_data, CD1_3))
                    cpl_propertylist_erase(sub_header_data, CD1_3);
                if (cpl_propertylist_has(sub_header_data, CD2_3))
                    cpl_propertylist_erase(sub_header_data, CD2_3);
                if (cpl_propertylist_has(sub_header_data, CD3_3))
                    cpl_propertylist_erase(sub_header_data, CD3_3);
                if (cpl_propertylist_has(sub_header_data, CD3_2))
                    cpl_propertylist_erase(sub_header_data, CD3_2);
                if (cpl_propertylist_has(sub_header_data, CD3_1))
                    cpl_propertylist_erase(sub_header_data, CD3_1);

                // invalid IFU, just save sub_headers
                 KMO_TRY_EXIT_IF_ERROR(kmo_dfs_save_sub_header(MAKE_IMAGE, "", 
                             sub_header_data));

                 if (desc1.ex_noise) {
                     KMO_TRY_EXIT_IF_ERROR(kmo_dfs_save_sub_header(MAKE_IMAGE, 
                                 "", sub_header_noise));
                 }
            }

            // free memory
            cpl_propertylist_delete(sub_header_data); sub_header_data = NULL;
            cpl_propertylist_delete(sub_header_noise); sub_header_noise = NULL;
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_val = -1;
    }

    kmo_free_fits_desc(&desc1);
    kmo_free_fits_desc(&desc2);
    cpl_propertylist_delete(sub_header_data); sub_header_data = NULL;
    cpl_propertylist_delete(sub_header_noise); sub_header_noise = NULL;
    cpl_propertylist_delete(spec_header); spec_header = NULL;
    cpl_imagelist_delete(data_in); data_in = NULL;
    cpl_imagelist_delete(noise_in); noise_in = NULL;
    cpl_image_delete(data_out); data_out = NULL;
    cpl_image_delete(noise_out); noise_out = NULL;
    cpl_vector_delete(spec_data_in); spec_data_in = NULL;
    cpl_vector_delete(spec_lambda_in); spec_lambda_in = NULL;
    cpl_vector_delete(ranges); ranges = NULL;

    return ret_val;
}

/**@}*/
