/*
 * 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 <math.h>

#include <cpl.h>

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

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

static int kmo_fit_profile_create(cpl_plugin *);
static int kmo_fit_profile_exec(cpl_plugin *);
static int kmo_fit_profile_destroy(cpl_plugin *);
static int kmo_fit_profile(cpl_parameterlist *, cpl_frameset *);

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

static char kmo_fit_profile_description[] =
"This recipe creates either spectral or spatial profiles of sources using dif-\n"
"ferent functions to fit. Spectral profiles can be created for F1I frames (if\n"
"WCS is defined in the input frame, the output parameters are in respect to the\n"
"defined WCS).\n"
"Spatial profiles can be created for F2I frames (any WCS information is ignored\n"
"here).\n"
"If the frames contain no noise information, constant noise is assumed for the\n"
"fitting procedure.\n"
"\n"
"BASIC PARAMETERS:\n"
"-----------------\n"
"--method\n"
"F1I frames can be fitted using either 'gauss', 'moffat' or 'lorentz' function.\n"
"F2I frames can be fitted using either 'gauss' or 'moffat' function.\n"
"\n"
"ADVANCED PARAMETERS\n"
"-------------------\n"
"--range\n"
"For F1I frames the spectral range can be defined. With available WCS informa-\n"
"tion the range can be provided in units (e.g. “1.2;1.5”), otherwise in pixels\n"
"(e.g. “112;224).\n"
"For F2I frames the spatial range can be defined as follow: “x1,x2;y1,y2”\n"
"\n"
"-------------------------------------------------------------------------------\n"
"  Input files:\n"
"\n"
"   DO                    KMOS                                                  \n"
"   category              Type   Explanation                    Required #Frames\n"
"   --------              -----  -----------                    -------- -------\n"
"   <none or any>         F1I or Frame with or                     Y       1   \n"
"                         F2I    without noise                                 \n"
"\n"
"  Output files:\n"
"\n"
"   DO                    KMOS\n"
"   category              Type   Explanation\n"
"   --------              -----  -----------\n"
"   FIT_PROFILE           F1I    Fitted profile (without noise frame)\n"
"                         or     (3 Extensions)                      \n"
"                         F2I                                        \n"
"-------------------------------------------------------------------------------\n"
"\n";

/*-----------------------------------------------------------------------------
 *                              Functions code
 *----------------------------------------------------------------------------*/

/**
 * @defgroup kmo_fit_profile kmo_fit_profile Fit spectral line profiles as well as spatial profiles
 *
 * See recipe description for details.
 */

/**@{*/

/**
  @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_fit_profile",
                        "Fit spectral line profiles as well as spatial profiles"
                        " with a simple function - for example to measure "
                        "resolution or find the centre of a source",
                        kmo_fit_profile_description,
                        "Alex Agudo Berbel",
                        "usd-help@eso.org",
                        kmos_get_license(),
                        kmo_fit_profile_create,
                        kmo_fit_profile_exec,
                        kmo_fit_profile_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_fit_profile_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 */
    /* --method */
    p = cpl_parameter_new_value("kmos.kmo_fit_profile.method",
                                CPL_TYPE_STRING,
                                "Either fit \"gauss\", \"moffat\" or "
                                "\"lorentz\" for 1D data."
                                "Either fit \"gauss\" or \"moffat\" for "
                                "2D data. ",
                                "kmos.kmo_fit_profile",
                                "gauss");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "method");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --range */
    p = cpl_parameter_new_value("kmos.kmo_fit_profile.range",
                                CPL_TYPE_STRING,
                                "The spectral or spatial range to combine. "
                                "Default is the whole range. "
                                "e.g. F1I: \"0.5,2.1\" (microns), "
                                "e.g. F2I: \"1,7;3,10\" (pixels: x1,x2;y1,y2), "
                                "pixels are counted from 1.",
                                "kmos.kmo_fit_profile",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "range");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    return 0;
}

/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
static int kmo_fit_profile_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_fit_profile(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_fit_profile_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_fit_profile(cpl_parameterlist *parlist, cpl_frameset *frameset)
{
    cpl_image       *data_img_in            = NULL,
                    *data_img_in2           = NULL,
                    *data_img_out           = NULL,
                    *noise_img_in           = NULL,
                    *noise_img_in2          = NULL;

    cpl_vector      *data_vec_in            = NULL,
                    *data_vec_out           = NULL,
                    *noise_vec_in           = NULL,
                    *ranges                 = NULL,
                    *ranges2                = NULL,
                    *fit_par                = NULL,
                    *lambda_vec             = NULL,
                    *lambda_vec2            = NULL,
                    *data_vec2              = NULL,
                    *noise_vec2             = NULL;

    cpl_frame        *frame                 = NULL;

    int              ret_val                = 0,
                     nr_devices             = 0,
                     i                      = 0,
                     j                      = 0,
                     x1                     = 0,
                     x2                     = 0,
                     y1                     = 0,
                     y2                     = 0,
                     valid_ifu              = FALSE,
                     devnr                  = 0,
                     index_data             = 0,
                     index_noise            = 0;

    double           crpix1                 = 0.0,
                     crval1                 = 0.0,
                     cdelt1                 = 0.0,
                     *pranges               = NULL;

    const char       *ranges_txt            = NULL,
                     *method                = NULL;

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

    main_fits_desc   desc;

    KMO_TRY
    {
        kmo_init_fits_desc(&desc);

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

        KMO_TRY_ASSURE(cpl_frameset_get_size(frameset) == 1,
                       CPL_ERROR_NULL_INPUT,
                       "A fits-file must be provided!");

        KMO_TRY_EXIT_IF_NULL(
            frame = kmo_dfs_get_frame(frameset, "0"));

        /* load descriptor */
        desc = kmo_identify_fits_header(
                    cpl_frame_get_filename(frame));
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_ASSURE((desc.fits_type == f2i_fits) ||
                       (desc.fits_type == f1i_fits),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "DATA isn't in the correct format (either F2I or F1I)!");

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

        /* --- get parameters --- */
        cpl_msg_info(cpl_func, "--- Parameter setup for kmo_fit_profile ---");

        KMO_TRY_EXIT_IF_NULL(
            method = kmo_dfs_get_parameter_string(parlist,
                                           "kmos.kmo_fit_profile.method"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist, "kmos.kmo_fit_profile.method"));

        ranges_txt = kmo_dfs_get_parameter_string(parlist,
                                                  "kmos.kmo_fit_profile.range");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist, "kmos.kmo_fit_profile.range"));

        cpl_msg_info("", "-------------------------------------------");

        if (strcmp(ranges_txt, "") != 0) {

            ranges = kmo_identify_ranges(ranges_txt);
            KMO_TRY_CHECK_ERROR_STATE();

            KMO_TRY_EXIT_IF_NULL(
                pranges = cpl_vector_get_data(ranges));

            KMO_TRY_ASSURE((pranges[0] < pranges[1]),
                           CPL_ERROR_ILLEGAL_INPUT,
                           "x1 must be smaller than x2!");

            KMO_TRY_ASSURE((((desc.fits_type == f2i_fits) &&
                             (cpl_vector_get_size(ranges) == 4) ) || (
                             (desc.fits_type == f1i_fits) &&
                             (cpl_vector_get_size(ranges) == 2) )),
                     CPL_ERROR_ILLEGAL_INPUT,
                     "'range' must contain 2 values for F1I and "
                     "4 values for F2I!");

            if (desc.fits_type == f2i_fits) {
                x1 = pranges[0];
                x2 = pranges[1];
                y1 = pranges[2];
                y2 = pranges[3];

                KMO_TRY_ASSURE((x1 > 0) &&
                               (y1 > 0) &&
                               (x2 < desc.naxis1) &&
                               (y2 < desc.naxis2),
                     CPL_ERROR_ILLEGAL_INPUT,
                     "Provided range is larger than images in F2I!");

                KMO_TRY_ASSURE(y1 < y2,
                     CPL_ERROR_ILLEGAL_INPUT,
                     "y1 must be smaller than y2!");

            } else {
                // for F1I-files this will be done individually for each
                // extension.
            }
        } else {
            if (desc.fits_type == f2i_fits) {
                x1 = 1;
                x2 = desc.naxis1;
                y1 = 1;
                y2 = desc.naxis2;
            } else {
                ranges = cpl_vector_new(2);
                cpl_vector_set(ranges, 0, 1);
                cpl_vector_set(ranges, 1, desc.naxis1);
            }
        }

        /* --- load & save primary header --- */
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_save_main_header(frameset, FIT_PROFILE, "", frame,
                                     NULL, parlist, cpl_func));

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

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

            if (desc.ex_noise) {
                index_noise = kmo_identify_index_desc(desc, devnr, TRUE);
            }
            KMO_TRY_CHECK_ERROR_STATE();

            KMO_TRY_EXIT_IF_NULL(
                sub_header_data = kmo_dfs_load_sub_header(frameset, "0", devnr,
                                                          FALSE));

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

            if (desc.ex_noise) {
                // load noise anyway since we have to save it in the output
                KMO_TRY_EXIT_IF_NULL(
                    sub_header_noise = kmo_dfs_load_sub_header(frameset, "0",
                                                               devnr, TRUE));
            }

            if (valid_ifu) {
                if (desc.fits_type == f2i_fits) {
                    //
                    // process images
                    //

                    // load data
                    KMO_TRY_EXIT_IF_NULL(
                        data_img_in = kmo_dfs_load_image(frameset, "0",
                                                         devnr, FALSE, FALSE, NULL));

                    KMO_TRY_EXIT_IF_NULL(
                        data_img_in2 = cpl_image_extract(data_img_in,
                                                         x1, y1, x2, y2));

                    // load noise, if existing
                    if (desc.ex_noise && desc.sub_desc[index_noise-1].valid_data) {
                        KMO_TRY_EXIT_IF_NULL(
                            noise_img_in = kmo_dfs_load_image(frameset, "0",
                                                              devnr, TRUE, FALSE, NULL));

                        KMO_TRY_EXIT_IF_NULL(
                            noise_img_in2 = cpl_image_extract(noise_img_in,
                                                              x1, y1, x2, y2));
                    }

                    // process data
                    KMO_TRY_EXIT_IF_NULL(
                        fit_par = kmo_fit_profile_2D(data_img_in2,
                                                     noise_img_in2,
                                                     method,
                                                     &data_img_out,
                                                     &pl));
                    cpl_vector_delete(fit_par); fit_par = NULL;

                    // update subheader with fit parameters
                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_propertylist_append(sub_header_data, pl));

                    /* save data  (noise is omitted for the fitted data)*/
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_image(data_img_out, FIT_PROFILE, "",
                                           sub_header_data, 0./0.));

                    /* free memory */
                    cpl_image_delete(data_img_in); data_img_in = NULL;
                    cpl_image_delete(data_img_in2); data_img_in2 = NULL;
                    cpl_image_delete(data_img_out); data_img_out = NULL;
                    cpl_image_delete(noise_img_in); noise_img_in = NULL;
                    cpl_image_delete(noise_img_in2); noise_img_in2 = NULL;
                } else {
                    //
                    // process vectors
                    //

                    // load data
kmclipm_vector *ddd = NULL;
                    KMO_TRY_EXIT_IF_NULL(
                        ddd = kmo_dfs_load_vector(frameset, "0",
                                                  devnr, FALSE));
data_vec_in = kmclipm_vector_create_non_rejected(ddd);
kmclipm_vector_delete(ddd); ddd = NULL;

                    // extract CRVAL, CRPIX and CDELT to convert the ranges-
                    // vector from spectral- to pixel-space
                    if (cpl_propertylist_has(sub_header_data, CRPIX1) &&
                        cpl_propertylist_has(sub_header_data, CRVAL1) &&
                        cpl_propertylist_has(sub_header_data, CDELT1))
                    {
                        crpix1 = cpl_propertylist_get_double(sub_header_data,
                                                                CRPIX1);
                        crval1 = cpl_propertylist_get_double(sub_header_data,
                                                                CRVAL1);
                        cdelt1 = cpl_propertylist_get_double(sub_header_data,
                                                                CDELT1);

                        if (strcmp(ranges_txt, "") == 0) {
                            cpl_vector_set(ranges, 0,
                                crpix1*crval1 +
                                    cdelt1*(cpl_vector_get(ranges,0)-1));
                            cpl_vector_set(ranges, 1,
                                crpix1*crval1 +
                                    cdelt1*(cpl_vector_get(ranges,1)-1));
                        }
                    } else {
                        // set crpix1 to 2 because the output position should
                        // be zero-based
                        // (like in the cpl_vector definition)
//                        crpix1 = 2;
                        crpix1 = 1;
                        crval1 = 1;
                        cdelt1 = 1;
                    }

                    // ranges2 will have the same size as the input spectra
                    // and will contain zeros for positions outside the provided
                    // spectral range and ones for values inside
                    KMO_TRY_EXIT_IF_NULL(
                        ranges2 = kmo_identify_slices(ranges,
                                                  crpix1,
                                                  crval1,
                                                  cdelt1,
                                                  desc.naxis1));
                    KMO_TRY_EXIT_IF_NULL(
                        pranges = cpl_vector_get_data(ranges2));

                    x1 = -1; x2 = -1;
                    for (j = 0; j < cpl_vector_get_size(ranges2); j++) {
                        if ((pranges[j] == 1) && (x1 == -1)) {
                            x1 = j;
                        }
                        if ((pranges[j] == 1) && (x1 != -1)) {
                            x2 = j;
                        }
                    }
                    cpl_vector_delete(ranges2); ranges2 = NULL;

                    // create lambda-vector for IFU
                    KMO_TRY_EXIT_IF_NULL(
                        lambda_vec = kmo_create_lambda_vec(desc.naxis1,
                                                           crpix1,
                                                           crval1,
                                                           cdelt1));

                    KMO_TRY_EXIT_IF_NULL(
                        lambda_vec2 = cpl_vector_extract(lambda_vec, x1, x2, 1));

                    KMO_TRY_EXIT_IF_NULL(
                        data_vec2 = cpl_vector_extract(data_vec_in, x1, x2, 1));

                    // load noise, if existing and
                    // extract same range as with data
                    if (desc.ex_noise  && desc.sub_desc[index_noise-1].valid_data) {
                        KMO_TRY_EXIT_IF_NULL(
                            ddd = kmo_dfs_load_vector(frameset, "0",
                                                      devnr, TRUE));
noise_vec_in = kmclipm_vector_create_non_rejected(ddd);
kmclipm_vector_delete(ddd); ddd =NULL;

                        KMO_TRY_EXIT_IF_NULL(
                            noise_vec2 = cpl_vector_extract(noise_vec_in,
                                                               x1, x2, 1));
                    }

                    // process data
                    KMO_TRY_EXIT_IF_NULL(
                        fit_par = kmo_fit_profile_1D(lambda_vec2,
                                                     data_vec2,
                                                     noise_vec2,
                                                     method,
                                                     &data_vec_out,
                                                     &pl));

                    cpl_vector_delete(fit_par); fit_par = NULL;

                    // update CRPIX if WCS information is available
                    if (cpl_propertylist_has(sub_header_data, CRPIX1) &&
                        cpl_propertylist_has(sub_header_data, CRVAL1) &&
                        cpl_propertylist_has(sub_header_data, CDELT1))
                    {
                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_update_property_double(sub_header_data,
                                                           CRPIX1,
                                                           crpix1-x1,
                                               "[pix] Reference pixel in x"));

                        if (desc.ex_noise) {
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(sub_header_noise,
                                                               CRPIX1,
                                                               crpix1-x1,
                                               "[pix] Reference pixel in x"));
                        }
                    }

                    // append fit parameters and errors
                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_propertylist_append(sub_header_data, pl));

                    // save data  (noise is omitted for the fitted data)
ddd = kmclipm_vector_create(cpl_vector_duplicate(data_vec_out));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_vector(ddd, FIT_PROFILE, "",
                                           sub_header_data, 0./0.));
kmclipm_vector_delete(ddd); ddd = NULL;

                    // free memory
                    cpl_vector_delete(data_vec_in); data_vec_in = NULL;
                    cpl_vector_delete(data_vec_out); data_vec_out = NULL;
                    cpl_vector_delete(noise_vec_in); noise_vec_in = NULL;
                    cpl_vector_delete(noise_vec2); noise_vec2 = NULL;
                    cpl_vector_delete(lambda_vec); lambda_vec = NULL;
                    cpl_vector_delete(lambda_vec2); lambda_vec2 = NULL;
                    cpl_vector_delete(data_vec2); data_vec2 = NULL;
                }

                cpl_propertylist_delete(pl); pl = NULL;
            } else {
                // invalid IFU, just save sub_headers */
                KMO_TRY_EXIT_IF_ERROR(
                    kmo_dfs_save_sub_header(FIT_PROFILE, "", sub_header_data));

                if (desc.ex_noise) {
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_sub_header(FIT_PROFILE, "", 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(&desc);
    cpl_image_delete(data_img_in); data_img_in = NULL;
    cpl_image_delete(data_img_in2); data_img_in2 = NULL;
    cpl_image_delete(data_img_out); data_img_out = NULL;
    cpl_image_delete(noise_img_in); noise_img_in = NULL;
    cpl_image_delete(noise_img_in2); noise_img_in2 = NULL;
    cpl_vector_delete(data_vec_in); data_vec_in = NULL;
    cpl_vector_delete(data_vec2); data_vec2 = NULL;
    cpl_vector_delete(data_vec_out); data_vec_out = NULL;
    cpl_vector_delete(noise_vec_in); noise_vec_in = NULL;
    cpl_vector_delete(noise_vec2); noise_vec2 = NULL;
    cpl_vector_delete(lambda_vec); lambda_vec = NULL;
    cpl_vector_delete(lambda_vec2); lambda_vec2 = NULL;
    cpl_vector_delete(fit_par); fit_par = NULL;
    cpl_vector_delete(ranges); ranges = NULL;
    cpl_vector_delete(ranges2); ranges2 = NULL;
    cpl_propertylist_delete(sub_header_data); sub_header_data = NULL;
    cpl_propertylist_delete(sub_header_noise); sub_header_noise = NULL;

    return ret_val;
}

/**@}*/
