/* 
 * 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 "kmclipm_vector.h"
#include "kmo_priv_arithmetic.h"
#include "kmos_molecfit.h"
#include "kmo_dfs.h"

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

#define KMOS_MOLECFIT_CORRECT_REFLEX_SUFFIX   "ESO PRO REFLEX SUFFIX"

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

typedef struct {
  cpl_propertylist *header_ext_data;       /* Data header                                 */
  cpl_propertylist *header_ext_noise;      /* Noise header                                */
  cpl_imagelist    *cube_data;             /* Data  cube (3D)                             */
  cpl_imagelist    *cube_noise;            /* Noise cube (3D)                             */
  cpl_vector       *vec_data;              /* Data  (1D)                                  */
  cpl_vector       *vec_noise;             /* Noise (1D)                                  */
  cpl_vector       *correction;            /* Data correction (1D) TELLURIC_CORR/RESPONSE */

} kmos_molecfit_correct_data;

typedef struct {
  kmos_molecfit_correct_data ifus[N_IFUS]; /* Input data                                          */
  cpl_propertylist *parms;                 /* Input parameters propertylist                       */
  cpl_propertylist *pl_data;               /* Header    of input data       fits file             */
  cpl_propertylist *pl_correction;         /* Header    of input correction fits file             */
  const char       *tag_output;            /* Tag of the output files                             */
} kmos_molecfit_correct_parameter;


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

/* Use the data input and kmos_molecfit_calctrans output to configure kmos_molecfit_correct */
static cpl_error_code kmos_molecfit_correct_frame_conf(
    kmos_molecfit_correct_parameter *conf,
    cpl_frame                       *frmCorrection,
    cpl_frame                       *frmData);

/* Fill the internal KMOS configuration file */
static kmos_molecfit_correct_parameter * kmos_molecfit_correct_conf(
    const cpl_parameterlist *list);

/* Clean variables allocated in the recipe */
static void kmos_molecfit_correct_clean(
    kmos_molecfit_correct_parameter *conf);

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

#define RECIPE_NAME      KMOS_MOLECFIT_CORRECT
#define CONTEXT          "kmos."RECIPE_NAME

static char kmos_molecfit_correct_description[] =
    "This recipe reads the results from kmos_molecfit_calctrans and apply the telluric correction for scientific input data file.\n"
    "The 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"
    "The recipe will be run on all the extension that contain data, it include noise extensions.\n"
    "It is independent from molecfit/calctrans and uses one utility that is available in the KMOS pipeline (kmo_arithmetic).\n"
    "The result obtained by the proposed molecfit/calctrans recipes must be the same as at the current molecfit and calctrans stand-alone tools.\n"
    "\n"
    "Input files: (It's mandatory to provide 1 to N files type A, but only 1 type B that it will apply in all of A's) \n"
    "\n"
    "   DO                 KMOS                                               \n"
    "   category           Type   required   Explanation                      \n"
    "   --------           -----  --------   -----------                      \n"
    "   STAR_SPEC          F1I       A       The spectrum (1D spectrum, IMAGE format).\n"
    "   EXTRACT_SPEC       F1I       A       The spectrum (1D spectrum, IMAGE format).\n"
    "   SCIENCE            F3I       A       The spectrum (3D cube,     IMAGE format).\n"
    "   SCI_RECONSTRUCTED  F3I       A       The spectrum (3D cube,     IMAGE format).\n"
    "   TELLURIC_CORR      F1I       B       Telluric correction for each IFU (24-data extensions, additionaly 24-error are optional, in IMAGE format).\n"
    "   RESPONSE           F1S       B       Response correction for each IFU (24-data extensions, additionaly 24-error are optional, in IMAGE format).\n"
    "\n"
    "Output files: (The output is 1 file of type C. The file match with the input dimensions)\n"
    "\n"
    "   DO                 KMOS                                               \n"
    "   category           Type              Explanation                      \n"
    "   --------           -----             -----------                      \n"
    "   SINGLE_SPECTRA     F1I       C       The 1D input data corrected file.\n"
    "   SINGLE_CUBES       F3I       C       The 3D input data corrected file.\n"
    "\n";

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


/*----------------------------------------------------------------------------*/
/**
 * @defgroup kmos_molecfit_correct  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_correct(
    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();

  /* Get --suppress_extension parameter : Flag to activate/suppress the output grating prefix */
  const cpl_parameter *p = cpl_parameterlist_find_const(parlist, "suppress_extension");
  cpl_boolean suppress_extension = cpl_parameter_get_bool(p);

  /*** Get frame, header and check: correction TELLURIC_CORR/RESPONSE ***/
  cpl_errorstate preStateLoadCorrection = cpl_errorstate_get();
  cpl_msg_info(cpl_func, "Loading header, input spectrum correction ...");
  cpl_frame *frmCorrection = cpl_frameset_find(frameset, TELLURIC_CORR);
  if (!frmCorrection) {
      cpl_errorstate_set(preStateLoadCorrection);
      frmCorrection = cpl_frameset_find(frameset, RESPONSE);
      if (!frmCorrection) {
          return cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
                                       "Frame with input correction data spectrum not found in frameset ('%s','%s') !",
                                       TELLURIC_CORR, RESPONSE);
      }
  }

  /*** Create the raw frameset ***/
  cpl_size     nframes  = cpl_frameset_get_size(frameset);
  cpl_frameset *fs_raws = cpl_frameset_new();
  for (cpl_size i = 0; i < nframes; i++) {

     cpl_frame  *frame    = cpl_frameset_get_position(frameset, i);

     if (   !strcmp(cpl_frame_get_tag(frame), STAR_SPEC         )
         || !strcmp(cpl_frame_get_tag(frame), EXTRACT_SPEC      )
         || !strcmp(cpl_frame_get_tag(frame), SCIENCE           )
         || !strcmp(cpl_frame_get_tag(frame), RECONSTRUCTED_CUBE) ){

         cpl_frameset_insert(fs_raws, cpl_frame_duplicate(frame));
     }
  }

  /* Check size of raw frameset*/
  cpl_size n_raws = cpl_frameset_get_size(fs_raws);
  if (n_raws < 1) {
      cpl_frameset_delete(fs_raws);
      return cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
                                   "Not raw Frames in the input frameset!");
  }

  /* Loop to compute all the inputs raws files */
  for (cpl_size i = 0; i < n_raws; i++) {

      /* Get one input data file */
      cpl_frame *frmData = cpl_frameset_get_position(fs_raws, i);

      /* Extract and verify data in the parameters of the recipe */
      cpl_msg_info(cpl_func, "Configuring initial parameters in the recipe ...");
      kmos_molecfit_correct_parameter *conf = kmos_molecfit_correct_conf(parlist);
      if (!conf) {
          cpl_frameset_delete(fs_raws);
          return (int)cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
                                            "Problems with the configuration parameters");

      }

      /* Complete configuration using the data input and kmos_molecfit_calctrans output to configure kmos_molecfit_correct */
      if (kmos_molecfit_correct_frame_conf(conf, frmCorrection, frmData) != CPL_ERROR_NONE) {
          kmos_molecfit_correct_clean(conf);
          cpl_frameset_delete(fs_raws);
          return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                            "Configuration with kmos_molecfit_model output failed!");
      }

      const char *fileData = cpl_frame_get_filename(frmData);
      cpl_msg_info(cpl_func, " +++ All input data loaded successfully from the input file: %s +++", fileData);

      /* Saving generic multi-extension output *.fits file */
      char *filename;
      if (suppress_extension) {
          filename = cpl_sprintf("%s_%lld.fits", conf->tag_output, i);
      } else {
          filename = cpl_sprintf("%s_%s", conf->tag_output, cpl_propertylist_get_string(conf->pl_data, "ARCFILE"));
      }

      cpl_msg_info(cpl_func, "Saving generic multi-extension output fits file ('%s') ...", filename);
      if (kmos_molecfit_save(frameset, parlist, RECIPE_NAME, conf->parms, conf->tag_output, NULL, CPL_FALSE, filename) != CPL_ERROR_NONE) {
          cpl_free(filename);
          kmos_molecfit_correct_clean(conf);
          cpl_frameset_delete(fs_raws);
          return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                            "Saving generic multi-extension output fits files failed!");
      }


      /* Execute correction */
      for (cpl_size n_ifu = 0; n_ifu < N_IFUS; n_ifu++) {
          kmos_molecfit_correct_data *ifu = &(conf->ifus[n_ifu]);

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

          /* Apply the correction and save */
          cpl_error_code e_sData;
          cpl_error_code e_sNoise = CPL_ERROR_NONE;

          /*** Get input data ***/
          if (ifu->vec_data) {

              cpl_msg_info(cpl_func,     "Correcting                   1D data  spectrum    , IFU.%02lld ...", n_ifu + 1);
              cpl_vector_divide(ifu->vec_data, ifu->correction);

              cpl_msg_info(cpl_func,     "Saving in '%s' 1D data              , IFU.%02lld ...", filename, n_ifu + 1);
              e_sData  = cpl_vector_save(ifu->vec_data, filename, CPL_TYPE_FLOAT, ifu->header_ext_data, CPL_IO_EXTEND);

              if (ifu->vec_noise) {

                  cpl_msg_info(cpl_func, "Correcting                   1D noise spectrum, IFU.%02lld ...", n_ifu + 1);
                  cpl_vector_divide(ifu->vec_noise, ifu->correction);

                  cpl_msg_info(cpl_func, "Saving in '%s' 1D noise             , IFU.%02lld ...", filename, n_ifu + 1);
                  e_sNoise = cpl_vector_save(ifu->vec_noise, filename, CPL_TYPE_FLOAT, ifu->header_ext_noise, CPL_IO_EXTEND);

              } else if (ifu->header_ext_noise){

                  cpl_msg_info(cpl_func, "Saving in '%s'    header noise      , IFU.%02lld ...", filename, n_ifu + 1);
                  e_sNoise = cpl_propertylist_save(ifu->header_ext_noise, filename, CPL_IO_EXTEND);
              }

          } else if (ifu->cube_data) {

              /* Conver ifu->correction for operate with kmo_priv_arithmetic */
              kmclipm_vector *correction = kmclipm_vector_create(cpl_vector_duplicate(ifu->correction));

              cpl_msg_info(cpl_func,     "Correcting                    3D data   cube       , IFU.%02lld ...", n_ifu + 1);
              if (kmo_arithmetic_3D_1D(ifu->cube_data, correction, NULL, NULL, "/") != CPL_ERROR_NONE) {
                  cpl_free(filename);
                  kmclipm_vector_delete(correction);
                  kmos_molecfit_correct_clean(conf);
                  cpl_frameset_delete(fs_raws);
                  return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                                    "Correction 3D failed in IFU.%02lld!", n_ifu + 1);
              }

              cpl_msg_info(cpl_func,     "Saving in '%s' 3D data              , IFU.%02lld ...", filename, n_ifu + 1);
              e_sData  = cpl_imagelist_save(ifu->cube_data, filename, CPL_TYPE_FLOAT, ifu->header_ext_data, CPL_IO_EXTEND);

              if (ifu->cube_noise) {

                  cpl_msg_info(cpl_func, "Correcting                    3D noise  cube, IFU.%02lld ...", n_ifu + 1);
                  if (kmo_arithmetic_3D_1D(ifu->cube_noise, correction, NULL, NULL, "/") != CPL_ERROR_NONE) {
                      cpl_free(filename);
                      kmclipm_vector_delete(correction);
                      kmos_molecfit_correct_clean(conf);
                      cpl_frameset_delete(fs_raws);
                      return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                                        "Correction 3D failed in IFU.%02lld!", n_ifu + 1);
                  }

                  cpl_msg_info(cpl_func, "Saving in '%s' 3D noise correction cube, IFU.%02lld ...", filename, n_ifu + 1);
                  e_sNoise = cpl_imagelist_save(ifu->cube_noise, filename, CPL_TYPE_FLOAT, ifu->header_ext_noise, CPL_IO_EXTEND);

              } else if (ifu->header_ext_noise) {

                  cpl_msg_info(cpl_func, "Saving in '%s'    header noise      , IFU.%02lld ...", filename, n_ifu + 1);
                  e_sNoise = cpl_propertylist_save(ifu->header_ext_noise, filename, CPL_IO_EXTEND);
              }

              /* Cleanup correction */
              kmclipm_vector_delete(correction);

          } else {

              cpl_msg_info(cpl_func, "Saving in '%s'    header data       , IFU.%02lld ...", filename, n_ifu + 1);
              e_sData      = cpl_propertylist_save(ifu->header_ext_data,  filename, CPL_IO_EXTEND);
              if (ifu->header_ext_noise) {
                  cpl_msg_info(cpl_func, "Saving in '%s'    header noise      , IFU.%02lld ...", filename, n_ifu + 1);
                  e_sNoise = cpl_propertylist_save(ifu->header_ext_noise, filename, CPL_IO_EXTEND);
              }
          }

          /* Check save  */
          if (e_sData != CPL_ERROR_NONE || e_sNoise != CPL_ERROR_NONE) {
              cpl_free(filename);
              kmos_molecfit_correct_clean(conf);
              cpl_frameset_delete(fs_raws);
              return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                                "Saving correction output failed!, ext=%lld", n_ifu + 1);
          }

          /* Check execution */
          if (!cpl_errorstate_is_equal(preState)) {
              cpl_msg_info(cpl_func, "Correction failed! in IFU.%02lld -> error: %s", n_ifu + 1, cpl_error_get_message());
              cpl_free(filename);
              kmos_molecfit_correct_clean(conf);
              cpl_frameset_delete(fs_raws);
              return (int)cpl_error_set_message(cpl_func, cpl_error_get_code(),
                                                "Correction failed! in IFU.%02lld ...", n_ifu + 1);

          }
      }

      /* Cleanup configuration */
      cpl_msg_info(cpl_func,"Cleaning variables from the input file : %s", fileData);
      cpl_free(filename);
      kmos_molecfit_correct_clean(conf);
  }

  /* Cleanup */
  cpl_frameset_delete(fs_raws);


  /* 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_correct_fill_parameterlist(
    cpl_parameterlist *self)
{
  cpl_error_ensure(self, CPL_ERROR_NULL_INPUT, return CPL_ERROR_NULL_INPUT, "Null input");

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


  /* --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_correct_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_correct_parameter * kmos_molecfit_correct_conf(
    const cpl_parameterlist *list)
{
  /* Check input */
  cpl_error_ensure(list, CPL_ERROR_NULL_INPUT,
                   return NULL, "kmos_molecfit_correct_fill_conf input list NULL!");

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

  /* Create the configuration parameter */
  kmos_molecfit_correct_parameter *conf = (kmos_molecfit_correct_parameter *)cpl_malloc(sizeof(kmos_molecfit_correct_parameter));
  conf->parms = cpl_propertylist_new();
  conf->pl_data        = NULL;
  conf->pl_correction  = NULL;
  for (cpl_size n_ifu = 0; n_ifu < N_IFUS; n_ifu++) {
      kmos_molecfit_correct_data *ifu = &(conf->ifus[n_ifu]);

      ifu->header_ext_data  = NULL;
      ifu->header_ext_noise = NULL;

      ifu->cube_data        = NULL;
      ifu->cube_noise       = NULL;

      ifu->vec_data         = NULL;
      ifu->vec_noise        = NULL;

      ifu->correction       = NULL;
  }

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

  /* 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_correct_frame_conf(
    kmos_molecfit_correct_parameter *conf,
    cpl_frame                       *frmCorrection,
    cpl_frame                       *frmData)
{
  /* Check input */
  cpl_error_ensure(conf && frmCorrection && frmData, CPL_ERROR_NULL_INPUT,
                   return CPL_ERROR_NULL_INPUT, "kmos_molecfit_correct_inputs_conf inputs NULL!");

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


  /*** Get frame, header and check input data ***/
  cpl_msg_info(cpl_func, "Loading header, input data frame ...");
  const char *fileData = cpl_frame_get_filename(frmData);
  conf->pl_data = cpl_propertylist_load(fileData, 0);
  cpl_error_ensure(conf->pl_data, cpl_error_get_code(),
                   return cpl_error_get_code(), "Cannot load input data primary header propertylist from '%s'!", fileData);

  /* Get REFLEX_SUFFIX if exist in the file */
  if (cpl_propertylist_has(conf->pl_data, KMOS_MOLECFIT_CORRECT_REFLEX_SUFFIX)) {
      cpl_propertylist_update_string(conf->parms, KMOS_MOLECFIT_CORRECT_REFLEX_SUFFIX, cpl_propertylist_get_string(conf->pl_data, KMOS_MOLECFIT_CORRECT_REFLEX_SUFFIX));
  }

  /* Propagate the keyword : ESO PRO MJD-OBS */
  if (cpl_propertylist_has(conf->pl_data, PRO_MJD_OBS)) {
      cpl_propertylist_update_double(conf->parms, PRO_MJD_OBS,
              cpl_propertylist_get_double(conf->pl_data, PRO_MJD_OBS));
  }

  /* Propagate the keyword : ESO PRO DATE-OBS */
  if (cpl_propertylist_has(conf->pl_data, PRO_DATE_OBS)) {
      cpl_propertylist_update_string(conf->parms, PRO_DATE_OBS,
              cpl_propertylist_get_string(conf->pl_data, PRO_DATE_OBS));
  }

  /* Get input data tag and select ouput tag */
  const char *tag = cpl_frame_get_tag(frmData);
  if (          strcmp(tag, STAR_SPEC         ) == 0
             || strcmp(tag, EXTRACT_SPEC      ) == 0 ){
      conf->tag_output = SINGLE_SPECTRA;
  } else if (   strcmp(tag, SCIENCE           ) == 0
             || strcmp(tag, RECONSTRUCTED_CUBE) == 0 ){
      conf->tag_output = SINGLE_CUBES;
  } else {
      return cpl_error_set_message(cpl_func, CPL_ERROR_TYPE_MISMATCH,
                                   "Tag input data unexpected: %s", tag);
  }

  /* Get if the file raw have 24(1) or 48(2) extensions */
  cpl_size n_extensions_raw = cpl_fits_count_extensions(fileData);
  cpl_size ext24_raw = (N_IFUS == n_extensions_raw) ? 1 : 2;

  const char *fileCorrection = cpl_frame_get_filename(frmCorrection);
  conf->pl_correction = cpl_propertylist_load(fileCorrection, 0);
  cpl_error_ensure(conf->pl_correction, cpl_error_get_code(),
                   return cpl_error_get_code(), "Cannot load TELLURIC_CORR primary header propertylist from '%s'!", fileCorrection);

  /* Get input correction tag */
  const char *tag_correction = cpl_frame_get_tag(frmCorrection);

  /* Get if the file correction have 24(1) or 48(2) extensions */
  cpl_size n_extensions_correction = cpl_fits_count_extensions(fileCorrection);
  cpl_size ext24_correction = (N_IFUS == n_extensions_correction) ? 1 : 2;


  /*** 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++) {
      kmos_molecfit_correct_data *ifu = &(conf->ifus[n_ifu]);

      /* Get number of extensions */
      cpl_size correction_ext = (n_ifu * ext24_correction) + 1;  /* 24/48 extensions (odd  ext - data ) */
      cpl_size data_ext       = (n_ifu * ext24_raw       ) + 1;  /* 24/48 extensions (odd  ext - data ) */
      cpl_size noise_ext      =  data_ext                  + 1;  /* 24/48 extensions (even ext - noise) */


      /*** Get input data header ***/
      ifu->header_ext_data = cpl_propertylist_load(fileData, data_ext);
      cpl_error_ensure(ifu->header_ext_data, cpl_error_get_code(),
                       return cpl_error_get_code(), "Cannot load input data extension header propertylist from '%s', ext=%lld!", fileData, n_ifu + 1);


      /*** Get input noise header: 48 extensions in the file? ***/
      if (ext24_raw > 1) {

          /* Load header noise extension */
          ifu->header_ext_noise = cpl_propertylist_load(fileData, noise_ext);
          cpl_error_ensure(ifu->header_ext_data, cpl_error_get_code(),
                           return cpl_error_get_code(), "Cannot load input data extension header propertylist from '%s', ext=%lld!", fileData, n_ifu + 1);
      }


      /*** Get input data ***/
      if (strcmp(conf->tag_output, SINGLE_SPECTRA) == 0){

          ifu->vec_data = cpl_vector_load(fileData, data_ext);
          if (!(ifu->vec_data)) {
              /* The extension doesn't have 1D data */
              cpl_errorstate_set(preState);
              cpl_msg_info(cpl_func, "Frame: %s  --> IFU.%02lld : Loaded 1D input data header           (not exist input data).", tag, n_ifu + 1);
          } else {
              cpl_msg_info(cpl_func, "Frame: %s  --> IFU.%02lld : Loaded 1D input data!",   tag, n_ifu + 1);

              /* Get input noise: 48 extensions in the file? */
              if (ext24_raw > 1) {

                  ifu->vec_noise = cpl_vector_load(fileData, noise_ext);
                  if (!(ifu->vec_noise)) {
                      /* The extension doesn't have 1D data */
                      cpl_errorstate_set(preState);
                      cpl_msg_info(cpl_func, "Frame: %s  --> IFU.%02lld : Loaded 1D input noise header          (not exist input noise).", tag, n_ifu + 1);

                  } else {
                      cpl_msg_info(cpl_func, "Frame: %s  --> IFU.%02lld : Loaded 1D input noise!",  tag, n_ifu + 1);
                  }
              }
          }


      } else if (strcmp(conf->tag_output, SINGLE_CUBES) == 0){

          ifu->cube_data = cpl_imagelist_load(fileData, CPL_TYPE_FLOAT, data_ext);
          if (!(ifu->cube_data)) {
              /* The extension doesn't have 1D data */
              cpl_errorstate_set(preState);
              cpl_msg_info(cpl_func, "Frame: %s  --> IFU.%02lld : Loaded 3D input data header           (not exist input data).", tag, n_ifu + 1);
          } else {
              cpl_msg_info(cpl_func, "Frame: %s  --> IFU.%02lld : Loaded 3D input data!",   tag, n_ifu + 1);

              /* Get input noise: 48 extensions in the file? */
              if (ext24_raw > 1) {

                  ifu->cube_noise = cpl_imagelist_load(fileData, CPL_TYPE_FLOAT, noise_ext);
                  if (!(ifu->cube_noise)) {
                      /* The extension doesn't have 1D data */
                      cpl_errorstate_set(preState);
                      cpl_msg_info(cpl_func, "Frame: %s  --> IFU.%02lld : Loaded 3D input noise header          (not exist input noise).", tag, n_ifu + 1);

                  } else {
                      cpl_msg_info(cpl_func, "Frame: %s  --> IFU.%02lld : Loaded 3D input noise!",  tag, n_ifu + 1);
                  }
              }
          }
      }


      /* Get Telluric correction */
      ifu->correction = cpl_vector_load(fileCorrection, correction_ext);
      if (!(ifu->correction)) {
          /* The extension doesn't have correction (telluric/response) */
          return cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
                                       "The extension doesn't have correction spectrum in IFU.%02lld", n_ifu + 1);
      } else {
          cpl_msg_info(cpl_func, "Frame: %s --> IFU.%02lld : Loaded input data spectrum correction (output of kmos_molecfit_calctrans recipe).", tag_correction, n_ifu + 1);
      }
  }


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

  return CPL_ERROR_NONE;
}

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

      if (conf->parms)               cpl_propertylist_delete(conf->parms);
      if (conf->pl_data)             cpl_propertylist_delete(conf->pl_data);
      if (conf->pl_correction)       cpl_propertylist_delete(conf->pl_correction);

      for (cpl_size n_ifu = 0; n_ifu < N_IFUS; n_ifu ++) {
          kmos_molecfit_correct_data *ifu = &(conf->ifus[n_ifu]);

          if (ifu->header_ext_data)  cpl_propertylist_delete(ifu->header_ext_data);
          if (ifu->header_ext_noise) cpl_propertylist_delete(ifu->header_ext_noise);

          if (ifu->cube_data)        cpl_imagelist_delete(   ifu->cube_data);
          if (ifu->cube_noise)       cpl_imagelist_delete(   ifu->cube_noise);

          if (ifu->vec_data)         cpl_vector_delete(      ifu->vec_data);
          if (ifu->vec_noise)        cpl_vector_delete(      ifu->vec_noise);

          if (ifu->correction)       cpl_vector_delete(      ifu->correction);
      }

      cpl_free(conf);
  }
}
