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

/*----------------------------------------------------------------------------*/
/**
 *                              Defines
 */
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/**
 *                 Typedefs: Structs and enum types
 */
/*----------------------------------------------------------------------------*/

typedef struct {
  kmos_molecfit_parameter mf;            /* Generic molecfit parameter                                                                           */
  cpl_propertylist *pl_atmos_params;     /* Primary property list header in the ATMOS_PARM file                                                  */
  cpl_table        *atmprof[N_IFUS];     /* cpl_table in the ATMOS_PARM file    (one for each extension DATA executed in kmos_molecfit_model)    */
  cpl_propertylist *pl_best_fit_params;  /* Primary property list header in the BEST_FIT_PARM file                                               */
  cpl_table        *res_table[N_IFUS];   /* cpl_table in the BEST_FIT_PARM file (one for each extension DATA executed in kmos_molecfit_model)    */
  cpl_table        *telluric[N_IFUS];    /* Results of telluric corrections after execute convolution                                            */
} kmos_molecfit_calctrans_parameter;


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

/* Fill the internal KMOS configuration file */
static kmos_molecfit_calctrans_parameter * kmos_molecfit_calctrans_conf(
    const cpl_parameterlist *list);

/* Use the kmos_molecfit_model output to configure kmos_molecfit_caltrans */
static cpl_error_code kmos_molecfit_calctrans_frames_conf(
    kmos_molecfit_calctrans_parameter *conf, cpl_frameset *frameset);

/* Fill the molecfit specific recipe configuration file */
static cpl_parameterlist * kmos_molecfit_calctrans_mf_conf(
    kmos_molecfit_calctrans_parameter *conf, double median);

/* Clean variables allocated in the recipe */
static void kmos_molecfit_calctrans_clean(
    kmos_molecfit_calctrans_parameter *conf);

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

#define RECIPE_NAME      KMOS_MOLECFIT_CALCTRANS
#define CONTEXT          "kmos."RECIPE_NAME

static char kmos_molecfit_calctrans_description[] =
    "This recipe applies the results from kmos_molecfit_model and runs calctrans to calculate the \n"
    "telluric correction for scientific input data. Scientific input data can have category: \n"
    " - STAR_SPEC (24 DATA plus 24 NOISE extensions)\n"
    " - EXTRACT_SPEC (24 DATA extensions, additional 24 NOISE extensions are optional)\n"
    " - SCIENCE (24 DATA extensions, additional 24 NOISE extensions are optional)\n"
    " - SCI_RECONSTRUCTED (24 DATA extensions, additional 24 NOISE extensions are optional)\n"
    "It is not mandatory that all the DATA extension contains data.\n"
    "It accounts for the difference in airmass between the input model and the input scientific data. \n"
    "It accounts for different spectral resolutions between the various KMOS IFUs.\n"
    "\n"
    "Input files: (It's mandatory to provide 1 file only of type A) \n"
    "\n"
    "   DO                 KMOS                                                  \n"
    "   category           Type   required   Explanation                         \n"
    "   --------           -----  --------   -----------                         \n"
    "   STAR_SPEC          F1I       A       The science spectrum to compute the telluric correction for (1D spectrum, IMAGE format).\n"
    "   EXTRACT_SPEC       F1I       A       The science spectrum to compute the telluric correction for (1D spectrum, IMAGE format).\n"
    "   SCIENCE            F3I       A       The science spectrum to compute the telluric correction for (3D cube,     IMAGE format).\n"
    "   SCI_RECONSTRUCTED  F3I       A       The science spectrum to compute the telluric correction for (3D cube,     IMAGE format).\n"
    "   ATMOS_PARM         F1L       Y       Atmospheric model as computed by the recipe kmos_molecfit_model.\n"
    "   BEST_FIT_PARM      F1L       Y       Best fitting model and parameters as computed by the recipe kmos_molecfit_model.\n"
    "   KERNEL_LIBRARY     F2I       N       The kernel library; must be the same grating as the other inputs.\n"
    "\n"
    "Output files:                                                               \n"
    "\n"
    "   DO                 KMOS                                                  \n"
    "   category           Type              Explanation                         \n"
    "   --------           -----             -----------                         \n"
    "   TELLURIC_DATA      F1L               Telluric correction and intermediate products for each IFU. Output fits file with 48 ext. (24-data and 24-error, in TABLE format).\n"
    "                                           Each data extension contains the telluric correction for one IFU.\n"
    "   TELLURIC_CORR      F1I               Telluric correction for each IFU. Output fits file with 48 ext. (24-data and 24-error, in IMAGE format).\n"
    "                                           Each data extension contains the telluric correction for one IFU.\n"
    "\n"
    "----------------------------------------------------------------------------\n"
    "The input atmospheric model and best fitting parameters (as obtained in the kmos_molecfit_model) for a given IFU.X are used to generate a telluric correction for IFU.Y.\n"
    "The instrumental spectral resolution of IFU.Y is taken into account if a kernel library is provided.\n"
    "The mapping between IFU.X and IFU.Y is either specified by the user or automatic.\n"
    "The difference in airmass between the data used in kmos_molecfit_model and the input data of kmos_molecfit_calctrans are taken into account.\n"
    "\n";

/* Standard CPL recipe definition */
cpl_recipe_define(	kmos_molecfit_calctrans,
                  	KMOS_BINARY_VERSION,
                  	"Jose A. Escartin, Yves Jung",
                  	"usd-help@eso.org",
                  	"2017",
                  	"Read the results from kmos_molecfit_model and computes the telluric correction for a scientific input data.",
                  	kmos_molecfit_calctrans_description);


/*----------------------------------------------------------------------------*/
/**
 * @defgroup kmos_molecfit_calctrans  It runs molectift on KMOS standard star file to compute an atmospheric model.
 */
/*----------------------------------------------------------------------------*/

/**@{*/

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

/*----------------------------------------------------------------------------*/
/**
 * @brief    Interpret the command line options and execute the data processing
 *
 * @param    frameset   the frames list
 * @param    parlist    the parameters list
 *
 * @return   CPL_ERROR_NONE if everything is OK or CPL_ERROR_CODE in other case
 *
 */
/*----------------------------------------------------------------------------*/
static int kmos_molecfit_calctrans(
    cpl_frameset *frameset, const cpl_parameterlist *parlist)
{
  /* Check initial Entries */
  if (kmos_check_and_set_groups(frameset) != CPL_ERROR_NONE) {
      return cpl_error_get_code();
  }

  /* Get initial errorstate */
  cpl_errorstate initial_errorstate = cpl_errorstate_get();

  /* Extract and verify data in the parameters of the recipe */
  cpl_msg_info(cpl_func, "Configuring initial parameters in the recipe ...");
  kmos_molecfit_calctrans_parameter *conf = kmos_molecfit_calctrans_conf(parlist);
  cpl_error_ensure(conf, CPL_ERROR_ILLEGAL_INPUT,
                   return (int)CPL_ERROR_ILLEGAL_INPUT, "Problems with the configuration parameters");

  /* Complete configuration with the ATMOS_PARAM and BEST_FIT_PARM, outputs of the recipe kmos_molecfit_model */
  if (kmos_molecfit_calctrans_frames_conf(conf, frameset) != CPL_ERROR_NONE) {
      kmos_molecfit_calctrans_clean(conf);
      return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                        "Configuration with kmos_molecfit_model output failed!");
  }

  if (!(conf->mf.fit_wavelenght.fit)) cpl_msg_info(cpl_func, "Not fit wavelenght!");
  if (!(conf->mf.fit_continuum.fit) ) cpl_msg_info(cpl_func, "Not fit continuum!" );

  /* Loading data spectrums */
  cpl_error_code err = kmos_molecfit_load_spectrums(frameset, &(conf->mf), RECIPE_NAME);
  if(err != CPL_ERROR_NONE) {
      kmos_molecfit_calctrans_clean(conf);
      return err;
  }

  /* Loading kernels */
  const cpl_frame *frmKernel = cpl_frameset_find(frameset, KERNEL_LIBRARY);
  if (frmKernel && conf->mf.use_input_kernel) {
      if (kmos_molecfit_load_kernels(frmKernel, &(conf->mf)) != CPL_ERROR_NONE) {
          kmos_molecfit_calctrans_clean(conf);
          return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                            "Loading convolution kernel data in the input frameset failed!");
      }
  } else {
      if (frmKernel) {
          cpl_msg_warning(cpl_func, "Kernel is provided, but use_input_kernel = false !");
      } else if (conf->mf.use_input_kernel){
          cpl_msg_warning(cpl_func, "Kernel isn't provided, but use_input_kernel = true !");
      }
      cpl_msg_info(cpl_func, "Using the default molecfit kernels -> With the values inside BEST_FIT_PARMS!");
  }

  cpl_msg_info(cpl_func, " +++ All input data loaded successfully! +++");


  /* Saving generic multi-extension output *.fits file */
  cpl_msg_info(cpl_func, "Saving generic multi-extension output fits file ('%s','%s') ...", TELLURIC_DATA, TELLURIC);
  cpl_error_code sTel_data = kmos_molecfit_save(frameset, parlist, RECIPE_NAME, conf->mf.parms, TELLURIC_DATA, conf->mf.grating.name, conf->mf.suppress_extension, NULL);
  cpl_error_code sTel_img  = kmos_molecfit_save(frameset, parlist, RECIPE_NAME, conf->mf.parms, TELLURIC_CORR, conf->mf.grating.name, conf->mf.suppress_extension, NULL);
  if (sTel_data != CPL_ERROR_NONE || sTel_img != CPL_ERROR_NONE) {
      kmos_molecfit_calctrans_clean(conf);
      return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                        "Saving generic multi-extension output fits files ('%s','%s') failed!", TELLURIC_DATA, TELLURIC_CORR);
  }


  /* Constant values in Molecfit executions */
  const double wlmin = -1.;
  const double wlmax = -1.;

  /* Get molecules in the grating */
  cpl_table *molecules = conf->mf.grating.molecules;

  /* Change the TMPDIR environment variable to force molecfit and calctrans to create its temporary directory in the current working directory.
   * This is to adhere to pipeline recipe standards, which require recipe output of any kind being created only in the current working directory. */
  char *cwd    = kmos_molecfit_cwd_get();
  char *tmpdir = NULL;
  if (cwd) {
      kmos_molecfit_tmpdir_set(cwd, &tmpdir);
      cpl_free(cwd);
      cwd = NULL;
  }

  /* Execute molecfit (lblrtm and convolution) */
  for (cpl_size n_ifuY = 0; n_ifuY < N_IFUS; n_ifuY++) {

      /* Get specific IFU */
      kmos_spectrum *ifuY = &(conf->mf.ifus[n_ifuY]);

      /* Running Molecfit lblrtm only in IFU_Y (when ifuY->num is equal to ifuY->map) */
      if (ifuY->num == ifuY->map) {

          if (conf->telluric[n_ifuY]) {
              kmos_molecfit_calctrans_clean(conf);
              kmos_molecfit_tmpdir_set(tmpdir, NULL);
              cpl_free(tmpdir);
              return (int)cpl_error_set_message(cpl_func, CPL_ERROR_DUPLICATING_STREAM,
                                                "Unexpected error: Molecfit call mf_run_calctrans_lblrtm(...) already executed in this IFU");
          }

          /* Building molecfic configuration variable */
          cpl_msg_info(cpl_func, "Building molecfict configuration variable ... ");
          cpl_parameterlist *mf_config_ifuY = kmos_molecfit_calctrans_mf_conf(conf, ifuY->median);
          if (!mf_config_ifuY) {
              kmos_molecfit_calctrans_clean(conf);
              kmos_molecfit_tmpdir_set(tmpdir, NULL);
              cpl_free(tmpdir);
              return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                                "Building molecfit configuration variable failed!");
          }

          /* Get state */
          cpl_errorstate preState_lblrtm = cpl_errorstate_get();

          /* Initialize the mf_state for molecfit */
          mf_calctrans_state *mf_state = mf_init_calctrans_state();

          /* Execute Molecfit lblrtm routine */
          cpl_table *table_lblrtm = cpl_table_duplicate(ifuY->data);
          cpl_msg_info(cpl_func, "Executing mf_run_calctrans_lblrtm(...), in IFU_Y->map=%d),(num=%d,name=%s)",
                       ifuY->map, ifuY->num, ifuY->name);
          mf_run_calctrans_lblrtm( mf_config_ifuY,             /* cpl_parameterlist  *parlist   */
                                   conf->mf.header_spectrums,  /* cpl_propertylist   *plist     */
                                   &(table_lblrtm),            /* cpl_table          **inspec   */
                                   1,                          /* cpl_size           nspec      */
                                   molecules,                  /* cpl_table          *molectab  */
                                   wlmin,                      /* double             wl_start   */
                                   wlmax,                      /* double             wl_end     */
                                   conf->atmprof[n_ifuY],      /* cpl_table          *atmprof   */
                                   conf->res_table[n_ifuY],    /* cpl_table          *res_table */
                                   &mf_state);                 /* mf_calctrans_state **state    */

          /* Check execution */
          if (cpl_errorstate_is_equal(preState_lblrtm)) {
              cpl_msg_info(cpl_func, "Molecfit call (mf_run_calctrans_lblrtm) run successfully! in %s ...", ifuY->name);
          } else {
              cpl_msg_info(cpl_func, "Molecfit call (mf_run_calctrans_lblrtm) failed! in %s -> error: %s", ifuY->name, cpl_error_get_message());
              kmos_molecfit_calctrans_clean(conf);
              mf_cleanup(mf_state);
              cpl_parameterlist_delete(mf_config_ifuY);
              cpl_table_delete(table_lblrtm);
              kmos_molecfit_tmpdir_set(tmpdir, NULL);
              cpl_free(tmpdir);
              return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                                "Molecfit call (mf_run_calctrans_lblrtm) failed!");
          }

          /* Write down IFU executed and cleanup */
          cpl_parameterlist_delete(mf_config_ifuY);
          cpl_table_delete(table_lblrtm);


          /* Execute molecfit convolution: including ifuX == ifuY */
          for (cpl_size n_ifuX = 0; n_ifuX < N_IFUS; n_ifuX++) {

              /* Get specifics ifus */
              kmos_spectrum *ifuX = &(conf->mf.ifus[n_ifuX]);

              /* Running Molecfit convolution in each IFU_X(ifu->num) match with the reference math with IFU_Y (when ifuX->map == ifu->num) */
              if (ifuX->map == ifuY->num) {

                  if (conf->telluric[n_ifuX]) {
                      kmos_molecfit_calctrans_clean(conf);
                      mf_cleanup(mf_state);
                      kmos_molecfit_tmpdir_set(tmpdir, NULL);
                      cpl_free(tmpdir);
                      return (int)cpl_error_set_message(cpl_func, CPL_ERROR_DUPLICATING_STREAM,
                                                        "Unexpected error: Molecfit call mf_run_calctrans_convolution(...) already executed in this IFU");
                  }

                  /* Building molecfic configuration variable */
                  cpl_msg_info(cpl_func, "Building molecfict configuration variable ... ");
                  cpl_parameterlist *mf_config2_ifuX = kmos_molecfit_calctrans_mf_conf(conf, ifuY->median);
                  if (!mf_config2_ifuX) {
                      kmos_molecfit_calctrans_clean(conf);
                      kmos_molecfit_tmpdir_set(tmpdir, NULL);
                      cpl_free(tmpdir);
                      return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                                        "Building molecfit configuration variable failed!");
                  }

                  /* Convolution of the model transmission spectrum with the kernel */
                  cpl_table *table_conv = cpl_table_duplicate(ifuY->data);
                  cpl_msg_info(cpl_func, "Executing mf_run_calctrans_convolution(...), in IFU_X->map=%d),(num=%d,name=%s) IFU_Y->map=%d),(num=%d,name=%s)",
                               ifuX->map, ifuX->num, ifuX->name, ifuY->map, ifuY->num, ifuY->name);
                  cpl_errorstate preState_convolution = cpl_errorstate_get();
                  cpl_table *telluric_correction = NULL;
                  mf_run_calctrans_convolution( mf_config2_ifuX,            /* cpl_parameterlist  *parlist    */
                                                conf->mf.header_spectrums,  /* cpl_propertylist   *plist,     */
                                                &(table_conv),              /* cpl_table          **inspec    */
                                                1,                          /* cpl_size           nspec,      */
                                                ifuX->kernel.data,          /* cpl_matrix         *kernel,    */
                                                wlmin,                      /* double             wl_start,   */
                                                wlmax,                      /* double             wl_end,     */
                                                conf->atmprof[n_ifuY],      /* cpl_table          *atmprof,   */
                                                conf->res_table[n_ifuY],    /* cpl_table          *res_table, */
                                                &mf_state,                  /* mf_calctrans_state **state,    */
                                                &telluric_correction);      /* cpl_table          **result    */

                  if (telluric_correction) {
                      conf->telluric[n_ifuX] = cpl_table_duplicate(telluric_correction);
                      cpl_table_delete(telluric_correction);
                  }

                  /* Check execution */
                  if (cpl_errorstate_is_equal(preState_convolution)) {
                      if (conf->telluric[n_ifuX]) {
                          cpl_msg_info(cpl_func, "Molecfit call (mf_run_calctrans_convolution) run successfully! in %s ...", ifuX->name);
                      } else {
                          kmos_molecfit_calctrans_clean(conf);
                          mf_cleanup(mf_state);
                          cpl_parameterlist_delete(mf_config2_ifuX);
                          cpl_table_delete(table_conv);
                          kmos_molecfit_tmpdir_set(tmpdir, NULL);
                          cpl_free(tmpdir);
                          return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                                            "Molecfit call (mf_run_calctrans_convolution) doesn't provide error, but telluric_correction is NULL!");
                      }
                  } else {
                      cpl_msg_info(cpl_func, "Molecfit call (mf_run_calctrans_convolution) failed! in %s -> error: %s", ifuX->name, cpl_error_get_message());
                      kmos_molecfit_calctrans_clean(conf);
                      mf_cleanup(mf_state);
                      cpl_parameterlist_delete(mf_config2_ifuX);
                      cpl_table_delete(table_conv);
                      kmos_molecfit_tmpdir_set(tmpdir, NULL);
                      cpl_free(tmpdir);
                      return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                                        "Molecfit call (mf_run_calctrans_convolution) failed!");

                  }

                  /* Write down IFU executed */
                  cpl_parameterlist_delete(mf_config2_ifuX);
                  cpl_table_delete(table_conv);
              }
          }

          /* Clean Molecfit state for the next execution */
          mf_cleanup(mf_state);
      }
  }

  /* Restore tmp_directory */
  kmos_molecfit_tmpdir_set(tmpdir, NULL);
  cpl_free(tmpdir);

  /* Save data in each the IFU */
  for (cpl_size n_ifu = 0; n_ifu < N_IFUS; n_ifu++) {

      /* Get specifics ifus */
      kmos_spectrum *ifu = &(conf->mf.ifus[n_ifu]);

      cpl_table  *telluric_data = conf->telluric[n_ifu];
      double     *data          = NULL;
      cpl_vector *telluric_vec  = NULL;
      if (telluric_data) {

          /* Wrap the data */
          data = cpl_table_get_data_double(telluric_data, "mtrans");
          cpl_vector *vAux = cpl_vector_wrap(cpl_table_get_nrow(telluric_data), data);
          telluric_vec = cpl_vector_duplicate(vAux);
          cpl_vector_unwrap(vAux);

          double mtrans_max = cpl_vector_get_max(telluric_vec);
          //cpl_vector_divide_scalar(telluric_vec, mtrans_max);
          cpl_msg_info(cpl_func, "Saving telluric data and image in IFU=%02d ... (Convolution executed) --> Max_value(%lf)", ifu->num, mtrans_max);

      } else {
          cpl_msg_info(cpl_func, "Saving data in IFU=%02d ...", ifu->num);
      }

      cpl_propertylist *header_data;
      cpl_propertylist *header_noise;
      if (ifu->num == ifu->map) {
          header_data  = cpl_propertylist_duplicate(ifu->header_ext_data);
          header_noise = cpl_propertylist_duplicate(ifu->header_ext_noise);
      } else {

          kmos_spectrum *ifu_ref = &(conf->mf.ifus[ifu->map - 1]);
          header_data  = cpl_propertylist_duplicate(ifu_ref->header_ext_data);
          header_noise = cpl_propertylist_duplicate(ifu_ref->header_ext_noise);

          char* name_data = cpl_sprintf("IFU.%d.DATA", ifu->num);
          cpl_propertylist_update_string(header_data, EXTNAME, name_data);
          cpl_free(name_data);

          char* name_noise = cpl_sprintf("IFU.%d.NOISE", ifu->num);
          cpl_propertylist_update_string(header_noise, EXTNAME, name_noise);
          cpl_free(name_noise);
      }

      /* Save cpl_table with the data of the mf_convolution execution */
      sTel_data = kmos_molecfit_save_mf_results(header_data, header_noise,
                                                TELLURIC_DATA, conf->mf.grating.name, conf->mf.suppress_extension, NULL, telluric_data, NULL);
      if (sTel_data != CPL_ERROR_NONE) {
          if (telluric_vec) cpl_vector_delete(telluric_vec);
          cpl_propertylist_delete(header_data);
          cpl_propertylist_delete(header_noise);
          kmos_molecfit_calctrans_clean(conf);
          return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                            "Saving Molecfit output fits file ('%s') failed!, ext=%lld", TELLURIC_DATA, n_ifu + 1);
      }


      /* Save data image with the data obtained in the mf_convolution execution */
      sTel_img = kmos_molecfit_save_mf_results(header_data, header_noise,
                                               TELLURIC_CORR, conf->mf.grating.name, conf->mf.suppress_extension, NULL, NULL, telluric_vec);
      if (sTel_img != CPL_ERROR_NONE) {
          if (telluric_vec) cpl_vector_delete(telluric_vec);
          cpl_propertylist_delete(header_data);
          cpl_propertylist_delete(header_noise);
          kmos_molecfit_calctrans_clean(conf);
          return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                            "Saving Molecfit output fits file ('%s') failed!, ext=%lld", TELLURIC_CORR, n_ifu + 1);
      }

      /* Cleanup */
      if (telluric_vec) cpl_vector_delete(telluric_vec);
      cpl_propertylist_delete(header_data);
      cpl_propertylist_delete(header_noise);
  }

  /* Cleanup configuration */
  cpl_msg_info(cpl_func,"Cleaning variables ...");
  kmos_molecfit_calctrans_clean(conf);

  /* Check Recipe status and end */
  if (cpl_errorstate_is_equal(initial_errorstate) && cpl_error_get_code() == CPL_ERROR_NONE ) {
      cpl_msg_info(cpl_func,"Recipe successfully!");
  } else {
      /* Dump the error history since recipe execution start.
       * At this point the recipe cannot recover from the error */
      cpl_errorstate_dump(initial_errorstate, CPL_FALSE, NULL);
      cpl_msg_info(cpl_func,"Recipe failed!, error(%d)=%s", cpl_error_get_code(), cpl_error_get_message());
  }

  return (int)cpl_error_get_code();
}

/**@}*/

/*----------------------------------------------------------------------------*/
/**
 * @brief Function needed by cpl_recipe_define to fill the input parameters
 *
 * @param  self   parameterlist where you need put parameters
 *
 * @return cpl_error_code
 *
 */
/*----------------------------------------------------------------------------*/
static cpl_error_code kmos_molecfit_calctrans_fill_parameterlist(
    cpl_parameterlist *self)
{
  /* Add the different default parameters to the recipe */
  cpl_errorstate prestate = cpl_errorstate_get();

  /* Fill the parameters list */
  cpl_error_code e;
  cpl_boolean    range     = CPL_TRUE;
  const void     *dummyMin = NULL;
  const void     *dummyMax = NULL;


  /* --use_input_kernel */
  cpl_boolean use_input_kernel = CPL_TRUE;
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "use_input_kernel",
                                   !range, dummyMin, dummyMax, CPL_TYPE_BOOL, &use_input_kernel,
                                   "In order to use kernel library provide by the user.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;


  /*** Mapping IFUS ***/
  int automatic = -1.;

  /* --ifu_1 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_1",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 1 .", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_2 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_2",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 2 .", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_3 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_3",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 3 .", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_4 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_4",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 4 .", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_5 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_5",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 5 .", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_6 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_6",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 6 .", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_7 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_7",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 7 .", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_8 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_8",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 8 .", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_9 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_9",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 9 .", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_10 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_10",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 10.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_11 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_11",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 11.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_12 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_12",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 12.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_13 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_13",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 13.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_14 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_14",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 14.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_15 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_15",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 15.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_16 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_16",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 16.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_17 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_17",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 17.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_18 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_18",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 18.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_19 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_19",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 19.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_20 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_20",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 20.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_21 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_21",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 21.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_22 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_22",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 22.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_23 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_23",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 23.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --ifu_24 */
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "IFU_24",
                                   !range, dummyMin, dummyMax, CPL_TYPE_INT, &automatic,
                                   "IFU number in ATMOS_PARM and BEST_FIT_PARM to be used to compute the telluric correction for IFU 24.", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* --suppress_extension */
  cpl_boolean suppress_extension = CPL_FALSE;
  e = kmos_molecfit_fill_parameter(RECIPE_NAME, self, "suppress_extension",
                                   !range, dummyMin, dummyMax, CPL_TYPE_BOOL, &suppress_extension,
                                   "Suppress arbitrary filename extension.(TRUE (apply) or FALSE (don't apply).", CPL_FALSE);
  if (e != CPL_ERROR_NONE) return (int)e;

  /* Check possible errors */
  if (!cpl_errorstate_is_equal(prestate)) {
      return cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                   "kmos_molecfit_calctrans_fill_parameterlist failed!");
  }

  return CPL_ERROR_NONE;
}

/*----------------------------------------------------------------------------*/
/**
 * @brief  Generate the internal configuration file for the recipe and check values
 *
 * @param  list   parameterlist with the parameters
 *
 * @return configuration file or NULL if exist an error
 *
 */
/*----------------------------------------------------------------------------*/
static kmos_molecfit_calctrans_parameter * kmos_molecfit_calctrans_conf(
    const cpl_parameterlist *list)
{
  /* Check input */
  cpl_error_ensure(list, CPL_ERROR_NULL_INPUT,
                   return NULL, "kmos_molecfit_calctrans_fill_conf input list NULL!");

  /* Get preState */
  cpl_errorstate preState = cpl_errorstate_get();
  const cpl_parameter *p;


  /* Create the configuration parameter */
  kmos_molecfit_calctrans_parameter *conf = (kmos_molecfit_calctrans_parameter *)cpl_malloc(sizeof(kmos_molecfit_calctrans_parameter));
  kmos_molecfit_nullify(&(conf->mf));
  conf->pl_atmos_params    = NULL;
  conf->pl_best_fit_params = NULL;
  for (cpl_size i = 0; i < N_IFUS; i ++) {
      conf->atmprof[i]     = NULL;
      conf->res_table[i]   = NULL;
      conf->telluric[i]    = NULL;
  }

  /* Initialize input parameters propertylist */
  conf->mf.parms = cpl_propertylist_new();

  /* User input kernel ? */
  p = cpl_parameterlist_find_const(list, "use_input_kernel");
  conf->mf.use_input_kernel = cpl_parameter_get_bool(p);

  /* Mapping IFUS in kmos_molecfit_calctrans recipe */
  int ifu = -1;

  p = cpl_parameterlist_find_const(list, "IFU_1");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_2");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_3");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_4");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_5");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_6");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_7");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_8");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_9");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_10");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_11");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_12");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_13");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_14");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_15");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_16");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_17");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_18");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_19");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_20");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_21");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_22");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_23");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  p = cpl_parameterlist_find_const(list, "IFU_24");
  conf->mf.ifus[++ifu].map = cpl_parameter_get_int(p);

  /* suppress_extension */
  p = cpl_parameterlist_find_const(list, "suppress_extension");
  conf->mf.suppress_extension = cpl_parameter_get_bool(p);


  /* Save parameter in the output propertylist */
  cpl_propertylist_update_bool(conf->mf.parms, KMOS_MF_PARAM_RECIPE"USE_INPUT_KERNEL", conf->mf.use_input_kernel);

  ifu = -1;
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_1",            conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_2",            conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_3",            conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_4",            conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_5",            conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_6",            conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_7",            conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_8",            conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_9",            conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_10",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_11",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_12",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_13",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_14",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_15",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_16",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_17",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_18",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_19",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_20",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_21",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_22",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_23",           conf->mf.ifus[++ifu].map );
  cpl_propertylist_update_int( conf->mf.parms, KMOS_MF_PARAM_RECIPE"IFU_24",           conf->mf.ifus[++ifu].map );

  cpl_propertylist_update_bool(conf->mf.parms, KMOS_MF_PARAM_RECIPE"SUPPRESS_EXTENSION", conf->mf.use_input_kernel);


  /* Check status */
  if (!cpl_errorstate_is_equal(preState)) {
      /* Configuration failed */
      kmos_molecfit_calctrans_clean(conf);
      return NULL;
  } else {
      /* Configuration successfully */
      return conf;
  }
}

/*----------------------------------------------------------------------------*/
/**
 * @brief  Generate the internal configuration file for the recipe and check values
 *
 * @param  conf       configuration file in the recipe
 * @param  frameset   the frames list
 *
 * @return cpl_error_code
 *
 */
/*----------------------------------------------------------------------------*/
static cpl_error_code kmos_molecfit_calctrans_frames_conf(
    kmos_molecfit_calctrans_parameter *conf, cpl_frameset *frameset)
{
  /* Check input */
  cpl_error_ensure(conf && frameset, CPL_ERROR_NULL_INPUT,
                   return CPL_ERROR_NULL_INPUT, "kmos_molecfit_calctrans_recipe_model_conf inputs NULL!");

  /* Get preState */
  cpl_errorstate initialState = cpl_errorstate_get();


  /*** Get frame, header and check: ATMOS_PARAM ***/
  cpl_msg_info(cpl_func, "Loading '%s' header, input to kmos_molecfit_model recipe ...", ATMOS_PARM);
  const cpl_frame *frmAtmosParm = cpl_frameset_find(frameset, ATMOS_PARM);
  cpl_error_ensure(frmAtmosParm, CPL_ERROR_DATA_NOT_FOUND,
                   return CPL_ERROR_DATA_NOT_FOUND, ATMOS_PARM" not found in input frameset!");
  const char *fileAtmosParm = cpl_frame_get_filename(frmAtmosParm);
  conf->pl_atmos_params = cpl_propertylist_load(fileAtmosParm, 0);
  cpl_error_ensure(conf->pl_atmos_params, cpl_error_get_code(),
                   return cpl_error_get_code(), "Cannot load ATMOS_PARM primary header propertylist from '%s'!", fileAtmosParm);


  /*** Get frame, header and check: BEST_FIT_PARM ***/
  cpl_msg_info(cpl_func, "Loading '%s' header, input to kmos_molecfit_model recipe ...", BEST_FIT_PARM);
  const cpl_frame *frmBestFitParm = cpl_frameset_find(frameset, BEST_FIT_PARM);
  cpl_error_ensure(frmBestFitParm, CPL_ERROR_DATA_NOT_FOUND,
                   return CPL_ERROR_DATA_NOT_FOUND, BEST_FIT_PARM" not found in input frameset!");
  const char *fileBestFitParm = cpl_frame_get_filename(frmBestFitParm);
  conf->pl_best_fit_params = cpl_propertylist_load(fileBestFitParm, 0);
  cpl_error_ensure(conf->pl_best_fit_params, cpl_error_get_code(),
                   return cpl_error_get_code(), "Cannot load BEST_FIT_PARM primary header propertylist from '%s'!", fileBestFitParm);


  /*** Loading data form the input fits file ***/
  cpl_errorstate preState = cpl_errorstate_get();
  for (cpl_size n_ifu = 0; n_ifu < N_IFUS; n_ifu++) {

      /* Get number of extension */
      cpl_size ext = (n_ifu * 2) + 1;

      /* Get Atmospheric profile */
      conf->atmprof[n_ifu] = cpl_table_load(fileAtmosParm, ext, 0);
      if (!(conf->atmprof[n_ifu])) {
          /* The extension doesn't have atmprof */
          cpl_errorstate_set(preState);
      } else {
          cpl_msg_info(cpl_func, "%s,    ext=%02lld: Loaded input data to kmos_molecfit_model recipe.", ATMOS_PARM, ext);
      }

      /* Get Best fit parameters */
      conf->res_table[n_ifu] = cpl_table_load(fileBestFitParm, ext, 0);
      if (!(conf->res_table[n_ifu])) {
          /* The extension doesn't have data spectrum */
          cpl_errorstate_set(preState);
      } else {
          cpl_msg_info(cpl_func, "%s, ext=%02lld: Loaded input data to kmos_molecfit_model recipe.", BEST_FIT_PARM, ext);
      }
  }


  /*** Check and assign mapping of IFUS ***/
  cpl_msg_info(cpl_func, "Mapping IFU's ... (Using BEST_FIT_PARM data in automatic assignment)");
  for (cpl_size n_ifu = 0; n_ifu < N_IFUS; n_ifu++) {

      kmos_spectrum *ifu    = &(conf->mf.ifus[n_ifu]);
      cpl_size      ifu_num = n_ifu + 1;

      /* Check if it's necessary to re-mapping automatically */
      if (ifu->map != -1) {

          if (!(conf->res_table[ifu->map - 1])) {
              return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
                                           "Mapping IFU_X.%02lld->IFU_Y.%02d by the user failed! It doesn't contain IFU_Y data in the file %s.fits",
                                           ifu_num, ifu->map, BEST_FIT_PARM);
          }

          cpl_msg_info(cpl_func, "IFU_X.%02lld mapping to IFU_Y.%02d --> "
                       "Defined by the user with the input parameter (IFU_%02lld=%02d)",
                       ifu_num, ifu->map, ifu_num, ifu->map);
      } else {

          /* First rule: ifuX_i (science) have ifuY_i (calibration) */
          if (conf->res_table[n_ifu]) {
              ifu->map = ifu_num;
              cpl_msg_info(cpl_func, "IFU_X.%02lld mapping to IFU_Y.%02d --> "
                           "Defined automatically (Same  IFU_Y [with calibration data] for                  IFU_X [science data] - 1st rule)",
                           ifu_num, ifu->map);
          }

          /* Second rule: ifuX_(1-8)-->ifu_Y(1-8), ifuX_(9-16)-->ifu_Y(9-16), ifuX_(17-24)-->ifu_Y(17-24) */
          if (ifu->map == -1) {
              cpl_size ifuY_detector      = (n_ifu / 8) + 1;           /* Get detector of Science (IFU_X) */
              cpl_size ifuY_detector_init = (ifuY_detector - 1) * 8;   /* Starting IFU_Y (calibration) in the detector of IFU_X number (science) [valid = 0, 8, 16] */
              for (cpl_size ifu_Y = ifuY_detector_init; ifu_Y < ifuY_detector_init + 8 && ifu->map == -1; ifu_Y++) {

                  /* If exist data --> Map */
                  if (conf->res_table[ifu_Y]) {
                      ifu->map = ifu_Y + 1;
                      cpl_msg_info(cpl_func, "IFU_X.%02lld mapping to IFU_Y.%02d --> "
                                   "Defined automatically (First IFU_Y [with calibration data] inside detector:%lld of IFU_X [science data] - 2nd rule)",
                                   ifu_num, ifu->map, ifuY_detector);
                  }
              }
          }

          /* Third rule: If not found it in the normal range, looking for in all the range */
          if (ifu->map == -1) {
              cpl_size ifuX_detector = (n_ifu / 8) + 1;                /* Get detector of Science (IFU_X) */
              for (cpl_size ifu_Y = 0; ifu_Y < N_IFUS && ifu->map == -1; ifu_Y++) {

                  /* If exist data --> Map */
                  if (conf->res_table[ifu_Y]) {
                      cpl_size ifuY_detector = (ifu_Y / 8) + 1;        /* Get detector of calibration (IFU_Y) */
                      ifu->map = ifu_Y + 1;
                      cpl_msg_info(cpl_func, "IFU_X.%02lld mapping to IFU_Y.%02d --> "
                                   "Defined automatically (First IFU_Y [with calibration date] in the instrument (detector=%lld) outside the IFU_X detector=%lld [science data] - 3rd rule)", ifu_num, ifu->map,
                                   ifuY_detector, ifuX_detector);
                  }
              }
          }

          cpl_error_ensure(ifu->map != -1, CPL_ERROR_ILLEGAL_INPUT,
                           return CPL_ERROR_ILLEGAL_INPUT, "Cannot search any IFU_Y data for the IFU_X");
      }
  }


  /*** Get the molecfit parameters from the BEST_FIL_PARAMS propertylist ***/
  cpl_msg_info(cpl_func, "Loading input parameters provided from kmos_molecfit_model recipe ...");

  conf->mf.grating.wave_range       = cpl_propertylist_get_string(conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"WAVE_RANGE");
  conf->mf.grating.list_molec       = cpl_propertylist_get_string(conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"LIST_MOLEC");
  conf->mf.grating.fit_molec        = cpl_propertylist_get_string(conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"FIT_MOLEC" );
  conf->mf.grating.relcol           = cpl_propertylist_get_string(conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"RECOL"    );

  conf->mf.kernmode                 = cpl_propertylist_get_bool(  conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"KERNMODE"  );
  conf->mf.kernfac                  = cpl_propertylist_get_double(conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"KERNFAC"   );
  conf->mf.varkern                  = cpl_propertylist_get_bool(  conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"VARKERN"   );

  conf->mf.fit_continuum.fit        = cpl_propertylist_get_bool(  conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"FIT_CONT"  );
  conf->mf.fit_continuum.n          = cpl_propertylist_get_int(   conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"CONT_N"    );

  conf->mf.fit_wavelenght.fit       = cpl_propertylist_get_bool(  conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"FIT_WLC"   );
  conf->mf.fit_wavelenght.n         = cpl_propertylist_get_int(   conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"WLC_N"     );
  conf->mf.fit_wavelenght.const_val = cpl_propertylist_get_double(conf->pl_best_fit_params, KMOS_MF_PARAM_RECIPE"WLC_CONST" );


  /* Save parameter in the output propertylist */

  cpl_propertylist_update_string(conf->mf.parms, KMOS_MF_PARAM_RECIPE"WAVE_RANGE", conf->mf.grating.wave_range      );
  cpl_propertylist_update_string(conf->mf.parms, KMOS_MF_PARAM_RECIPE"LIST_MOLEC", conf->mf.grating.list_molec      );
  cpl_propertylist_update_string(conf->mf.parms, KMOS_MF_PARAM_RECIPE"FIT_MOLEC",  conf->mf.grating.fit_molec       );
  cpl_propertylist_update_string(conf->mf.parms, KMOS_MF_PARAM_RECIPE"RECOL",      conf->mf.grating.relcol          );

  cpl_propertylist_update_bool(  conf->mf.parms, KMOS_MF_PARAM_RECIPE"KERNMODE",   conf->mf.kernmode                );
  cpl_propertylist_update_double(conf->mf.parms, KMOS_MF_PARAM_RECIPE"KERNFAC",    conf->mf.kernfac                 );
  cpl_propertylist_update_bool(  conf->mf.parms, KMOS_MF_PARAM_RECIPE"VARKERN",    conf->mf.varkern                 );

  cpl_propertylist_update_bool(  conf->mf.parms, KMOS_MF_PARAM_RECIPE"FIT_CONT",   conf->mf.fit_continuum.fit       );
  cpl_propertylist_update_int(   conf->mf.parms, KMOS_MF_PARAM_RECIPE"CONT_N",     conf->mf.fit_continuum.n         );

  cpl_propertylist_update_bool(  conf->mf.parms, KMOS_MF_PARAM_RECIPE"FIT_WLC",    conf->mf.fit_wavelenght.fit      );
  cpl_propertylist_update_int(   conf->mf.parms, KMOS_MF_PARAM_RECIPE"WLC_N",      conf->mf.fit_wavelenght.n        );
  cpl_propertylist_update_double(conf->mf.parms, KMOS_MF_PARAM_RECIPE"WLC_CONST",  conf->mf.fit_wavelenght.const_val);


  /* Check status */
  if (!cpl_errorstate_is_equal(initialState)) {
      /* Configuration failed */
      return cpl_error_get_code();
  }

  return CPL_ERROR_NONE;
}

/*----------------------------------------------------------------------------*/
/**
 * @brief Function needed to fill the molecfit configuration file
 *
 * @param  conf   Recipe configuration.
 *
 * @return parameterlist with contain the config to molecfit or NULL if error
 *
 */
/*----------------------------------------------------------------------------*/
static cpl_parameterlist * kmos_molecfit_calctrans_mf_conf(
    kmos_molecfit_calctrans_parameter *conf, double median)
{
  /* Check input */
  cpl_error_ensure(conf, CPL_ERROR_NULL_INPUT,
                   return NULL, "conf input is NULL!");

  /* Add the config values necessaries to execute molecfit */
  cpl_errorstate prestate = cpl_errorstate_get();


  /*** Building generic configuration molecfic file ***/
  cpl_parameterlist *mf_config = kmos_molecfit_conf_generic(RECIPE_NAME, &(conf->mf));
  if (!mf_config) {
      return NULL;
  }


  /*** Set molecfit configuration with recipe parameters ***/
  cpl_error_code e         = CPL_ERROR_NONE;
  cpl_boolean    range     = CPL_TRUE;
  const void     *dummyMin = NULL;
  const void     *dummyMax = NULL;
  int            boolMin   = 0;
  int            boolMax   = 1;


  /*** PARAMETERS NOT INCLUDED IN THE RECIPE: HARD-CODED ***/


  /* --fit_cont */
  int fit_cont = conf->mf.fit_continuum.fit;
  if(!e) e = kmos_molecfit_fill_parameter(RECIPE_NAME, mf_config, "fit_cont",
                                          range, &boolMin, &boolMax, CPL_TYPE_INT, &fit_cont,
                                          "molecfit", CPL_TRUE);

  /* --cont_n */
  if(!e) e = kmos_molecfit_fill_parameter(RECIPE_NAME, mf_config, "cont_n",
                                          !range, dummyMin, dummyMax, CPL_TYPE_INT, &(conf->mf.fit_continuum.n),
                                          "molecfit", CPL_TRUE);

  /* --cont_const -> Spectrum data dependency, Calculate median (ifu->median) of the input spectrum (ifu->data) */
  double cont_const = median;
  cpl_msg_info(cpl_func,"--.cont_const = %g", cont_const);
  if(!e) e = kmos_molecfit_fill_parameter(RECIPE_NAME, mf_config, "cont_const",
                                          !range, dummyMin, dummyMax, CPL_TYPE_DOUBLE, &cont_const,
                                          "molecfit", CPL_TRUE);

  /* --fit_wlc */
  int fit_wlc = conf->mf.fit_wavelenght.fit;
  if(!e) e = kmos_molecfit_fill_parameter(RECIPE_NAME, mf_config, "fit_wlc",
                                          range, &boolMin, &boolMax, CPL_TYPE_INT, &fit_wlc,
                                          "molecfit", CPL_TRUE);

  /* --wlc_n */
  if(!e) e = kmos_molecfit_fill_parameter(RECIPE_NAME, mf_config, "wlc_n",
                                          !range, dummyMin, dummyMax, CPL_TYPE_INT, &(conf->mf.fit_wavelenght.n),
                                          "molecfit", CPL_TRUE);

  /* --wlc_const */
  if(!e) e = kmos_molecfit_fill_parameter(RECIPE_NAME, mf_config, "wlc_const",
                                          !range, dummyMin, dummyMax, CPL_TYPE_DOUBLE, &(conf->mf.fit_wavelenght.const_val),
                                          "molecfit", CPL_TRUE);


  /*** Check possible errors ***/
  if (!cpl_errorstate_is_equal(prestate) || e != CPL_ERROR_NONE) {
      cpl_parameterlist_delete(mf_config);
      cpl_error_set_message(cpl_func, cpl_error_get_code(),
                            "Building molecfit configuration variable failed!");
      return NULL;
  }

  return mf_config;
}

/*----------------------------------------------------------------------------*/
/**
 * @brief    Deallocate the given parameter configuration object and its contents
 *
 * @param    conf       The parameter configuration variable in the recipe.
 */
/*----------------------------------------------------------------------------*/
static void kmos_molecfit_calctrans_clean(
    kmos_molecfit_calctrans_parameter *conf)
{
  if (conf) {

      kmos_molecfit_clean(&(conf->mf));

      if (conf->pl_atmos_params)     cpl_propertylist_delete(conf->pl_atmos_params);
      if (conf->pl_best_fit_params)  cpl_propertylist_delete(conf->pl_best_fit_params);
      for (cpl_size i = 0; i < N_IFUS; i ++) {
          if (conf->atmprof[i])      cpl_table_delete(       conf->atmprof[i]);
          if (conf->res_table[i])    cpl_table_delete(       conf->res_table[i]);
          if (conf->telluric[i])     cpl_table_delete(       conf->telluric[i]);
      }

      cpl_free(conf);
  }
}
