/*                                                                            *
 *   This file is part of the ESO X-shooter Pipeline                          *
 *   Copyright (C) 2006 European Southern Observatory                         *
 *                                                                            *
 *   This library 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 P1ARTICULAR 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA     *
 *                                                                            */

/*
 * $Author: amodigli $
 * $Date: 2013-05-07 09:14:15 $
 * $Revision: 1.92 $
 *

*/

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup xsh_slit_nod   xsh_respon_slit_nod
 * @ingroup recipes
 *
 * This recipe ...
 * See man-page for details.
 */
/*----------------------------------------------------------------------------*/
/**@{*/

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


/* DRL steps */

/* Error handling */
#include <xsh_error.h>
/* Utility fonctions */
#include <xsh_utils.h>
#include <xsh_utils_table.h>
#include <xsh_utils_image.h>
#include <xsh_utils_scired_slit.h>

#include <xsh_msg.h>
#include <xsh_model_utils.h>
#include <xsh_data_instrument.h>
#include <xsh_data_spectrum1D.h>
/* DFS functions */
#include <xsh_dfs.h>
#include <xsh_pfits.h>
/* DRL functions */
#include <xsh_drl.h>
#include <xsh_drl_check.h>
#include <xsh_model_arm_constants.h>
#include <xsh_utils_efficiency.h>

/* Library */
#include <cpl.h>
/* CRH Remove */

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

#define RECIPE_ID "xsh_respon_slit_nod"
#define RECIPE_AUTHOR "P.Goldoni, L.Guglielmi, R. Haigron, F. Royer, D. Bramich A. Modigliani"
#define RECIPE_CONTACT "amodigli@eso.org"

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

/*
 *   Plugin initalization, execute and cleanup handlers
 */

static int xsh_respon_slit_nod_create(cpl_plugin *);
static int xsh_respon_slit_nod_exec(cpl_plugin *);
static int xsh_respon_slit_nod_destroy(cpl_plugin *);

/* The actual executor function */
static cpl_error_code xsh_respon_slit_nod(cpl_parameterlist *, cpl_frameset *);

/*-----------------------------------------------------------------------------
  Static variables
  ----------------------------------------------------------------------------*/
static char xsh_respon_slit_nod_description_short[] =
"Reduce STD star frames in SLIT configuration and nod mode";

static char xsh_respon_slit_nod_description[] =
"This recipe reduces science exposure in SLIT configuration and NOD mode\n\
Input Frames : \n\
  - A set of n Science frames ( n even ), \
Tag = OBJECT_SLIT_NOD_arm\n\
  - Spectral format table (Tag = SPECTRAL_FORMAT_TAB_arm)\n\
  - [UVB,VIS] A master bias frame (Tag = MASTER_BIAS_arm)\n\
  - A master flat frame (Tag = MASTER_FLAT_SLIT_arm)\n\
  - An order table frame(Tag = ORDER_TABLE_EDGES_SLIT_arm)\n\
  - [poly mode] A wave solution frame(Tag = WAVE_TAB_2D_arm)\n\
  - [poly mode] A wave map frame(Tag = WAVE_MAP_arm)\n\
  - [poly mode] A disp table frame(Tag = DISP_TAB_arm)\n\
  - [physical model mode]A model cfg table (Format = TABLE, Tag = XSH_MOD_CFG_TAB_arm)\n\
  - [OPTIONAL] A telluric model catalog (Tag = TELL_MOD_CAT_arm arm=VIS,NIR)\n\
  - A standard star fluxes catalog (Tag = FLUX_STD_CATALOG_arm Type = FLX)\n\
  - A table to set response sampling points (Tag = RESP_FIT_POINTS_CAT_arm)\n\
  - [OPTIONAL] An atmospheric extinction table (Tag = ATMOS_EXT_arm)\n\
    if provided this is the one used to flux calibrate the spectra\n\
Products : \n\
  - [If STD is in catal] The response function (Tag = PREFIX_RESPONSE_ORDER1D_SLIT_arm)\n\
  - [If STD is in catal] The response function (Tag = PREFIX_RESPONSE_MERGE1D_SLIT_arm)\n\
  - PREFIX_ORDER2D_arm (2 dimension)\n\
  - PREFIX_ORDER1D_arm (1 dimension)\n\
  - PREFIX_MERGE2D_arm (2 dimension)\n\
  - PREFIX_MERGE1D_arm (1 dimension)\n\
  - PREFIX_WAVE_MAP_arm, wave map image\n\
  - PREFIX_SLIT_MAP_arm, slit map image\n\
  - [If STD is in catal] Flux calibrated order-by-order 2D spectrum (Tag = PREFIX_FLUX_ORDER2D_arm)\n\
  - [If STD is in catal] Flux calibrated order-by-order 1D spectrum (Tag = PREFIX_FLUX_ORDER1D_arm)\n\
  - [If STD is in catal] Flux calibrated merged 2D spectrum (Tag = PREFIX_FLUX_MERGE2D_arm)\n\
  - [If STD is in catal] Flux calibrated merged 1D spectrum (Tag = PREFIX_FLUX_MERGE1D_arm)\n\
  - [If STD is in catal] The efficiency (Tag = EFFICIENCY_SLIT_arm)\n\
  - where PREFIX is SCI, FLUX, TELL if input raw DPR.TYPE contains OBJECT or FLUX or TELLURIC";

/*-----------------------------------------------------------------------------
  Functions code
  ----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/**
   @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 = NULL;
  cpl_plugin *plugin = NULL;

  recipe = cpl_calloc(1, sizeof(*recipe));
  if ( recipe == NULL ){
    return -1;
  }

  plugin = &recipe->interface ;

  cpl_plugin_init(plugin,
                  CPL_PLUGIN_API,                    /* Plugin API */
                  XSH_BINARY_VERSION,             /* Plugin version */
                  CPL_PLUGIN_TYPE_RECIPE,            /* Plugin type */
                  RECIPE_ID,                         /* Plugin name */
                  xsh_respon_slit_nod_description_short,   /* Short help */
                  xsh_respon_slit_nod_description,         /* Detailed help */
                  RECIPE_AUTHOR,                     /* Author name */
                  RECIPE_CONTACT,                    /* Contact address */
                  xsh_get_license(),                 /* Copyright */
                  xsh_respon_slit_nod_create,
                  xsh_respon_slit_nod_exec,
                  xsh_respon_slit_nod_destroy);

  cpl_pluginlist_append(list, plugin);

  return (cpl_error_get_code() != CPL_ERROR_NONE);
}

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

   Create the recipe instance and make it available to the application using the
   interface.

*/
/*---------------------------------------------------------------------------*/

static int xsh_respon_slit_nod_create(cpl_plugin *plugin){

  cpl_recipe *recipe = NULL;
  xsh_remove_crh_single_param crh_single = { 0.1, 20.0, 2.0, 4} ;
  xsh_rectify_param rectify = { "tanh",
                                 CPL_KERNEL_DEFAULT, 
                                 2,
                                 -1.0, 
                                 -1.0,
                                 1,
				0,0. };
  xsh_localize_obj_param loc_obj = 
    {10, 0.1, 0, 0, LOC_MANUAL_METHOD, 0, 2.0,3,3, FALSE};
  xsh_extract_param extract_par = 
    { NOD_METHOD};
  xsh_combine_nod_param nod_param = { 5, TRUE, 5, 2, 0.1, "throwlist.asc", COMBINE_MEAN_METHOD} ;
  xsh_slit_limit_param slit_limit_param = { MIN_SLIT, MAX_SLIT, 0, 0 } ;

  xsh_stack_param stack_param = {"median",5.,5.};
  xsh_interpolate_bp_param ipol_par = {30 };
  /* Reset library state */
  xsh_init();

  /* parameters default */
  nod_param.nod_min = 5;
  nod_param.nod_clip = TRUE;
  nod_param.nod_clip_sigma = 5.;
  nod_param.nod_clip_niter = 2;
  nod_param.nod_clip_diff = 0.1;
  nod_param.throwname = "throwlist.asc";

  /* Check input */
  assure( plugin != NULL, CPL_ERROR_NULL_INPUT, "Null plugin");

  /* Get the recipe out of the plugin */
  assure( cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE,
          CPL_ERROR_TYPE_MISMATCH,
          "Plugin is not a recipe");

  recipe = (cpl_recipe *)plugin;

  /* Create the parameter list in the cpl_recipe object */
  recipe->parameters = cpl_parameterlist_new();
  assure( recipe->parameters != NULL,
          CPL_ERROR_ILLEGAL_OUTPUT,
          "Memory allocation failed!");

  /* Set generic parameters (common to all recipes) */
  check( xsh_parameters_generic( RECIPE_ID, recipe->parameters ) ) ;
  xsh_parameters_decode_bp(RECIPE_ID,recipe->parameters,DECODE_BP_FLAG_NOD);
  check( xsh_parameters_pre_overscan( RECIPE_ID, recipe->parameters ) ) ;
  check(xsh_parameters_stack_create(RECIPE_ID,recipe->parameters,stack_param));

    /* remove_crh_single */
  check( xsh_parameters_remove_crh_single_create( RECIPE_ID,
    recipe->parameters, crh_single));

  /* xsh_rectify */
  check(xsh_parameters_rectify_create(RECIPE_ID,recipe->parameters,
					    rectify )) ;
  check( xsh_parameters_new_boolean( recipe->parameters, RECIPE_ID,
    "rectify-fast", TRUE,
    "Fast if TRUE (Rect[B-A] = -Rect[A-B]), in that case only entire pixel shifts are applied. "));


  /* xsh_localize_object */
  check(xsh_parameters_localize_obj_create(RECIPE_ID,recipe->parameters,
                                           loc_obj )) ;

  check( xsh_parameters_new_double( recipe->parameters, RECIPE_ID,
    "localize-nod-throw", loc_obj.nod_step,
    "Step (arcsec) between A and B images in nodding mode."));
  
  /* xsh_subtract_sky_single */
  /* trivial extract (Just temporary) */
  check(xsh_parameters_extract_create(RECIPE_ID,
				      recipe->parameters,
				      extract_par,NOD_METHOD )) ;

  check(xsh_parameters_interpolate_bp_create(RECIPE_ID,
                recipe->parameters,ipol_par)) ;

  check(xsh_parameters_combine_nod_create(RECIPE_ID,
					  recipe->parameters,
					  nod_param )) ;

  check(xsh_parameters_slit_limit_create(RECIPE_ID,
					 recipe->parameters,
					 slit_limit_param )) ;
/*
  check( xsh_parameters_new_boolean( recipe->parameters, RECIPE_ID,
    "do-flatfield", TRUE, 
				     "TRUE if we do the flatfielding"));
*/
    check( xsh_parameters_new_boolean( recipe->parameters, RECIPE_ID,
				       "correct-tellurics", TRUE,
    "TRUE if during response computation we apply telluric correction"));

    check( xsh_parameters_new_boolean( recipe->parameters, RECIPE_ID,
				       "correct-sky-by-median", TRUE,
    "TRUE if the resampled spectrum at each wavelength is median subtracted to remove sky lines"));

/*
    check( xsh_parameters_new_int( recipe->parameters, RECIPE_ID,
       "scale-stack-method",4,
       "frame stacking scaling method (<=3): 0 (on-scaling); 1 (scaling-uniform on slit); 2 (scaling-slice on slit); 3 (pix-pix scaling)"));
*/

    check( xsh_parameters_new_int( recipe->parameters, RECIPE_ID,
       "scale-combine-nod-method",1,
       "frame scaling when nod frames are combined: 0 (no-scaling); 1 (scaling)"));


 cleanup:
  if ( cpl_error_get_code() != CPL_ERROR_NONE ){
    xsh_error_dump(CPL_MSG_ERROR);
    return 1;
  }
  else {
    return 0;
  }
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Execute the plugin instance given by the interface
   @param    plugin  the plugin
   @return   0 if everything is ok
*/
/*----------------------------------------------------------------------------*/

static int xsh_respon_slit_nod_exec(cpl_plugin *plugin) {
  cpl_recipe *recipe = NULL;

  /* Check parameter */

  assure( plugin != NULL, CPL_ERROR_NULL_INPUT, "Null plugin" );

  /* Get the recipe out of the plugin */
  assure( cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE,
          CPL_ERROR_TYPE_MISMATCH, "Plugin is not a recipe");

  recipe = (cpl_recipe *)plugin;

  /* Check recipe */
  xsh_respon_slit_nod(recipe->parameters, recipe->frames);

 cleanup:
  if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
    xsh_error_dump(CPL_MSG_ERROR);
    xsh_error_reset();
    return 1;
  }
  else {
    return 0;
  }
}

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

  /* reset error state before detroying recipe */
  xsh_error_reset(); 
  /* Check parameter */
  assure( plugin != NULL, CPL_ERROR_NULL_INPUT, "Null plugin" );

  /* Get the recipe out of the plugin */
  assure( cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE,
	  CPL_ERROR_TYPE_MISMATCH, "Plugin is not a recipe");

  recipe = (cpl_recipe *)plugin;

  xsh_free_parameterlist(&recipe->parameters);

 cleanup:
  if (cpl_error_get_code() != CPL_ERROR_NONE)
    {
      return 1;
    }
  else
    {
      return 0;
    }
}

/*----------------------------------------------------------------------------*/
/**
   @brief    Interpret the command line options and execute the data processing
   @param    parameters     the parameters list
   @param    frameset   the frames listxsh_utils_image.c

   In case of failure the cpl_error_code is set.
*/
/*----------------------------------------------------------------------------*/
static cpl_error_code 
xsh_respon_slit_nod( cpl_parameterlist* parameters,
                                 cpl_frameset* frameset)
{
  const char* recipe_tags[3] = {XSH_OBJECT_SLIT_NOD,XSH_STD_TELL_SLIT_NOD,XSH_STD_FLUX_SLIT_NOD};
  int recipe_tags_size = 3;

  /* Input frames */
  cpl_frameset *raws = NULL;
  cpl_frameset *raws_ord_set = NULL;
  cpl_frameset *calib = NULL;
  /* Beware, do not "free" the following input frames, they are part
     of the input frameset */
  cpl_frame* bpmap = NULL;
  cpl_frame *master_bias = NULL;
  cpl_frame* master_flat = NULL;
  cpl_frame* order_tab_edges = NULL;
  cpl_frame * wave_tab = NULL ;
  cpl_frame * model_config_frame = NULL;
  cpl_frame * wavemap = NULL;
  cpl_frame * spectral_format = NULL;

  /* Parameters */
  int rectify_fast = 0 ;
  xsh_instrument* instrument = NULL;
  xsh_remove_crh_single_param * crh_single_par = NULL;
  xsh_rectify_param * rectify_par = NULL;
  xsh_localize_obj_param * loc_obj_par = NULL;
  xsh_extract_param * extract_par = NULL;
  xsh_combine_nod_param * combine_nod_par = NULL;
  xsh_slit_limit_param * slit_limit_par = NULL;
  xsh_stack_param* stack_par=NULL;

  char comb_tag[256];
  //int binx=0;
  //int biny=0;

  int nb_raw_frames;

  /* Intermediate frames */
  cpl_frameset* raws_avg=NULL;
  cpl_frame* disp_tab_frame=NULL;
  cpl_frame* slitmap=NULL;
  cpl_frame *skymask_frame = NULL;

  int do_computemap=1;
  int do_flatfield = CPL_TRUE;

  char *rec_prefix = NULL;


  cpl_frameset *nod_set = NULL;
  cpl_frameset *comb_set = NULL;
  cpl_frameset *comb_eff_set = NULL;
  cpl_frame *comb_frame = NULL;
  cpl_frame *comb_eff_frame = NULL;
  cpl_frame *combeso_frame = NULL;
  cpl_frame *combeffeso_frame = NULL;
  cpl_frame *res2D_frame = NULL;
  cpl_frame *loc_table_frame = NULL;
  cpl_frame *res1D_frame = NULL;
  cpl_frame *res1D_eff_frame = NULL;
  cpl_frame *res1Deso_frame = NULL;
  cpl_frame *res1Deso_eff_frame = NULL;
  cpl_frame *s1D_frame = NULL;
  cpl_frame *frm_eff = NULL;
  cpl_frame* response_ord_frame=NULL;
  cpl_frame* response_frame=NULL;
  cpl_frame * fluxcal_1D_frame = NULL ;	/**< Final (result) frame */
  cpl_frame * fluxcal_2D_frame = NULL ;	/**< Final (result) frame */
  cpl_frame * fluxcal_rect_1D_frame = NULL ;	/**< Final (result) frame */
  cpl_frame * fluxcal_rect_2D_frame = NULL ;	/**< Final (result) frame */
  cpl_frame* frm_atmext=NULL;
  cpl_frame * nrm_1D_frame = NULL ; /**< normalized (T,gain,atm-ext) 1D frame */
  cpl_frame * nrm_2D_frame = NULL ; /**< normalized (T,gain,atm-ext) 2D frame */
  int pre_overscan_corr=0;
  int generate_sdp_format=0;
  cpl_frame* frm_std_cat=NULL;
  cpl_frame* high_abs_win=NULL;
  cpl_frame* tell_mod_cat=NULL;
  cpl_frameset* crh_clean = NULL;
  cpl_frame* resp_fit_points_cat_frame=NULL;
  cpl_frame* sky_map_frm = NULL;
  //double airm=0;
  double exptime = 1. ;
  int merge_par=0;
  xsh_interpolate_bp_param *ipol_bp=NULL;
  int corr_tell=0;
  int corr_sky=0;
  int scale_nod=0;
  cpl_frame* frm_rejected=NULL;
  /**************************************************************************/
  /* DFS management */
  /**************************************************************************/
  check( xsh_begin( frameset, parameters, &instrument, &raws, &calib,
		    recipe_tags, recipe_tags_size, RECIPE_ID, 
		    XSH_BINARY_VERSION,
		    xsh_respon_slit_nod_description_short));

  if(instrument->arm == XSH_ARM_NIR) {
    xsh_instrument_nir_corr_if_JH(raws,instrument);
    xsh_calib_nir_respon_corr_if_JH(calib,instrument);
  }

  assure( instrument->mode == XSH_MODE_SLIT, CPL_ERROR_ILLEGAL_INPUT,
          "Instrument NOT in Slit Mode");

  check(frm_rejected=xsh_ensure_raws_number_is_even(raws));
  xsh_recipe_params_check(parameters,instrument,RECIPE_ID);
  //check(airm=xsh_utils_compute_airm_eff(raws));
 


  /**************************************************************************/
  /* Recipe frames */
  /**************************************************************************/
  check( rec_prefix = xsh_set_recipe_file_prefix(raws,"xsh_respon_slit_nod"));

  /* One should have a multiple of 2 input frames: A1,B1[,B2,A2,...] */
  check( nb_raw_frames = cpl_frameset_get_size( raws));
  check( raws_ord_set = xsh_order_frameset_by_date( raws));

  xsh_msg_dbg_low( "Nb of Raw frames: %d", nb_raw_frames);


  /* prepare RAW frames in XSH format: NB we moved this piece of REDUCTION here
      * because we need to have the calib frameset with input frames already with correct binning
      * that is done by xsh_set_recipe_file_prefix (which needs frames in PRE format)
      */
   check( xsh_prepare( raws_ord_set, bpmap, master_bias, XSH_OBJECT_SLIT_NOD,
            instrument,pre_overscan_corr,CPL_TRUE));

    /* in case the input master flat/bias do not match in bin size with the raw data,
      * correct the binning so that :
      *
      * 1x1 std 2x2 mflat,mbias --> correct bin of std
      * 2x2 std 1x1 mflat,mbias --> correct bin of mflat,mbias
      *
      */

  /**************************************************************************/
  /* Recipe parameters */ 
  /**************************************************************************/
/*
  check( do_flatfield = xsh_parameters_get_boolean( parameters, RECIPE_ID,
    "do-flatfield"));
    */
  check( stack_par = xsh_stack_frames_get( RECIPE_ID, parameters));
  /*
  if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
    binx=instrument->binx;
    biny=instrument->biny;
  } else {
    binx=1;
    biny=1;
  }
  */
  check( xsh_scired_nod_get_parameters( parameters,instrument,
                                        &crh_single_par, &rectify_par, 
                                        &extract_par, &combine_nod_par,
                                        &slit_limit_par, &loc_obj_par, 
                                        &rectify_fast, &pre_overscan_corr,
                                        &generate_sdp_format,
                                        RECIPE_ID));

  check(ipol_bp = xsh_parameters_interpolate_bp_get(RECIPE_ID,parameters));
  check( corr_tell = xsh_parameters_get_boolean( parameters, RECIPE_ID,
                                                      "correct-tellurics"));
  check( corr_sky = xsh_parameters_get_boolean( parameters, RECIPE_ID,
                                                      "correct-sky-by-median"));
  check( scale_nod = xsh_parameters_get_int( parameters, RECIPE_ID,
                                                 "scale-combine-nod-method"));
  /*
  check( scale_stack = xsh_parameters_get_int( parameters, RECIPE_ID,
                                                   "scale-stack-method"));
  */
  check(xsh_frameset_uniform_bin(&raws_ord_set, &calib,instrument));
  check( xsh_respon_slit_nod_get_calibs(calib,instrument,
                                       &bpmap,&master_bias,&master_flat,
                                       &order_tab_edges,&wave_tab,
                                       &model_config_frame,&wavemap,&slitmap,
                                       &disp_tab_frame,
					&spectral_format,
					&skymask_frame,
					&response_ord_frame,
					&frm_atmext,
					do_computemap,
					loc_obj_par->use_skymask,
                                        pre_overscan_corr,
					rec_prefix,RECIPE_ID));

  rectify_par->conserve_flux=FALSE;
  if ( rectify_fast == CPL_FALSE && loc_obj_par->method == LOC_MANUAL_METHOD){
    xsh_error_msg( "Mode accurate can not be use with localize-method MANUAL");
  }

  /* QC extra frames */
  resp_fit_points_cat_frame=xsh_find_frame_with_tag(calib,XSH_RESP_FIT_POINTS_CAT,instrument);
  if(resp_fit_points_cat_frame==NULL) {
     xsh_msg_warning("Missing input %s_%s response will not be computed",
                     XSH_RESP_FIT_POINTS_CAT,
                     xsh_instrument_arm_tostring( instrument ));
     xsh_msg_warning("and data will not be flux calibrated");
  }
 /* response extra frames */
  frm_atmext=xsh_find_frame_with_tag(calib,XSH_ATMOS_EXT,instrument);
  if(frm_atmext==NULL) {
     xsh_msg_error("Provide atmospheric extinction frame");
     return CPL_ERROR_DATA_NOT_FOUND;
  }

  frm_std_cat=xsh_find_frame_with_tag(calib,XSH_FLUX_STD_CAT,instrument);
  if(frm_std_cat==NULL) {
     xsh_msg_error("Provide std star catalog frame");
     return CPL_ERROR_DATA_NOT_FOUND;
  }

  //high_abs_win=xsh_find_frame_with_tag(calib,XSH_HIGH_ABS_WIN,instrument);

  tell_mod_cat=xsh_find_frame_with_tag(calib,XSH_TELL_MOD_CAT,instrument);


  /**************************************************************************/
  /* Recipe code */
  /**************************************************************************/



  int recipe_use_model=FALSE;
  cpl_frame* ref_pre_frame=NULL;
  if (xsh_mode_is_physmod(calib,instrument)){
     /*raws_ord_set contains frames in PRE format */
     recipe_use_model=TRUE;
     ref_pre_frame=cpl_frameset_get_frame(raws_ord_set,0);
   }
   else{
     ref_pre_frame = master_flat;
   }

  /* create proper size wave and slit maps */
  check( xsh_check_get_map( disp_tab_frame, order_tab_edges,
                     ref_pre_frame, model_config_frame, calib, instrument,
                     do_computemap, recipe_use_model, rec_prefix,
                     &wavemap, &slitmap));

  /* make sure each input raw frame has the same exp time */
  check(xsh_frameset_check_uniform_exptime(raws_ord_set,instrument));
  /* remove crh on each obj or sky frame */
  sky_map_frm = xsh_find_frame_with_tag(calib,XSH_SKY_MAP, instrument);
  crh_clean = xsh_frameset_crh_single(raws_ord_set, crh_single_par, sky_map_frm,
                  instrument,rec_prefix,"NOD");


  check( raws_avg = xsh_nod_group_by_reloff(crh_clean,instrument,stack_par));


  check( nod_set = xsh_subtract_sky_nod( raws_avg, instrument, rectify_fast));
  if ( rectify_fast ){


     check(comb_set=xsh_scired_slit_nod_fast(
              nod_set,
              spectral_format,
              master_flat,
              order_tab_edges,
              wave_tab,
              model_config_frame,
              disp_tab_frame,
              wavemap,
              instrument,
              crh_single_par,
              rectify_par,
              do_flatfield,corr_sky,1,
              rec_prefix,
              &comb_eff_set));

   }
  else {
    check( comb_set = xsh_scired_slit_nod_accurate(
				 nod_set,
				 spectral_format,
				 master_flat,
				 order_tab_edges,
				 wave_tab,
				 model_config_frame,
				 disp_tab_frame,
				 wavemap,
				 skymask_frame,
				 instrument,
                                 crh_single_par,
                                 rectify_par,
                                 loc_obj_par,
                                 combine_nod_par->throwname,
                                 do_flatfield,
                                 rec_prefix
                                 ));
       comb_eff_set=cpl_frameset_duplicate(comb_set);

  }

  /***************************************************************************/
  /* merge at combine nod part */
  /***************************************************************************/
  sprintf( comb_tag,"%s_%s",
    rec_prefix, XSH_GET_TAG_FROM_ARM( XSH_ORDER2D, instrument));
  check( comb_frame = xsh_combine_nod( comb_set, combine_nod_par,
    comb_tag, instrument,&combeso_frame,scale_nod));


  /* 2D product */
  check( res2D_frame = xsh_merge_ord( comb_frame, instrument,
                                      merge_par,rec_prefix));

  /* 1D product */
  if ( extract_par->method == LOCALIZATION_METHOD ||
       extract_par->method == CLEAN_METHOD  ) {
    xsh_msg( "Re-Localize before extraction" ) ;
    check( loc_table_frame = xsh_localize_obj( comb_frame, skymask_frame,instrument,
      loc_obj_par, slit_limit_par, "LOCALIZE.fits"));
  }
  xsh_msg( "Extract 1D order-by-order spectrum" ) ;
  check( res1D_frame = xsh_extract_clean( comb_frame, loc_table_frame,
    instrument, extract_par, ipol_bp,&res1Deso_frame, rec_prefix));
  xsh_msg( "Merge orders with 1D frame" ) ;
  check( s1D_frame = xsh_merge_ord( res1D_frame, instrument,
                                    merge_par,rec_prefix));


 /* Get Exptime */
  {
    cpl_propertylist * plist = NULL ;
    plist=cpl_propertylist_load(cpl_frame_get_filename(comb_frame),0) ;
    exptime = xsh_pfits_get_exptime(plist); ;
    xsh_msg_dbg_medium( "EXPTIME: %lf", exptime ) ;

    xsh_free_propertylist( &plist ) ;
  }

 
  /* Response computation */
  if(frm_std_cat!=NULL && frm_atmext!=NULL 
     && resp_fit_points_cat_frame!= NULL) {
     xsh_msg( "Calling xsh_compute_response" ) ;
     if( (response_ord_frame = xsh_compute_response_ord( res1Deso_frame,
                                                         frm_std_cat,
                                                         frm_atmext,
                                                         high_abs_win,
                                                         instrument,
                                                         exptime )) == NULL) {
        xsh_msg_warning("Some error occurred during response computation");
        xsh_print_rec_status(0);
        cpl_error_reset();
     }

     if( (response_frame = xsh_compute_response2( s1D_frame,
                                                 frm_std_cat,
                                                 frm_atmext,
						  high_abs_win,
						  resp_fit_points_cat_frame,
                                                 tell_mod_cat,
                                                 instrument,
						  exptime,
                                                  corr_tell )) == NULL) {
        xsh_msg_warning("Some error occurred during response computation");
        xsh_print_rec_status(0);
        cpl_error_reset();
     } else {

        check(xsh_frame_table_monitor_flux_qc(response_frame,"LAMBDA",
                                              "RESPONSE","RESP",instrument));
     }
  }

  if (response_frame != NULL && frm_atmext != NULL) {
     check(xsh_flux_calibrate(combeso_frame,res1Deso_frame,frm_atmext,
			      response_frame,merge_par,instrument,rec_prefix,
			     &fluxcal_rect_2D_frame,&fluxcal_rect_1D_frame,
			     &fluxcal_2D_frame,&fluxcal_1D_frame));
  }


  /* efficiency computation */
  if(response_frame != NULL && frm_atmext != NULL && 
     disp_tab_frame != NULL && comb_eff_set != NULL) {
     check( comb_eff_frame = xsh_combine_nod( comb_eff_set, combine_nod_par,
                                              comb_tag, instrument,
                                              &combeffeso_frame,scale_nod));

     xsh_msg( "Extract 1D order-by-order spectrum" ) ;
     check( res1D_eff_frame = xsh_extract_clean( comb_eff_frame, loc_table_frame,
                                           instrument, extract_par, ipol_bp,
                                           &res1Deso_eff_frame, rec_prefix));

     frm_eff=xsh_compute_efficiency(res1Deso_eff_frame,frm_std_cat,
					 frm_atmext,high_abs_win,
					 instrument);
  }


  if(model_config_frame!=NULL && wavemap != NULL&& slitmap != NULL) {

     check(xsh_compute_resampling_accuracy(wavemap,slitmap,order_tab_edges,model_config_frame,res2D_frame,instrument));
    
     check(xsh_compute_resampling_accuracy(wavemap,slitmap,order_tab_edges,model_config_frame,combeso_frame,instrument));
     
     check(xsh_compute_wavelength_resampling_accuracy(wavemap,order_tab_edges,model_config_frame,res1Deso_frame,instrument));
     
     check(xsh_compute_wavelength_resampling_accuracy(wavemap,order_tab_edges,model_config_frame,s1D_frame,instrument));
     
     xsh_add_afc_info(model_config_frame,wavemap);
     xsh_add_afc_info(model_config_frame,slitmap);

     if(fluxcal_rect_2D_frame != NULL) {

        check(xsh_compute_resampling_accuracy(wavemap,slitmap,order_tab_edges,model_config_frame,fluxcal_rect_2D_frame,instrument));
        check(xsh_compute_resampling_accuracy(wavemap,slitmap,order_tab_edges,model_config_frame,fluxcal_2D_frame,instrument));

        check(xsh_compute_wavelength_resampling_accuracy(wavemap,order_tab_edges,model_config_frame,fluxcal_rect_1D_frame,instrument));
        check(xsh_compute_wavelength_resampling_accuracy(wavemap,order_tab_edges,model_config_frame,fluxcal_1D_frame,instrument));

     }

  }

  /* Save products */
  if(frm_rejected != NULL ) {
       //cpl_frameset_erase_frame(frameset,frm_rejected);
  }
  if(response_ord_frame!=NULL) {
    check( xsh_add_product_table(response_ord_frame, frameset, parameters,
                                       RECIPE_ID, instrument,NULL));
  }
  if(response_frame!=NULL) {
    check( xsh_add_product_table( response_frame, frameset, parameters,
				  RECIPE_ID, instrument,NULL));

  }


  check( xsh_add_product_image( combeso_frame, frameset,
                                parameters, RECIPE_ID, instrument,NULL));

  check( xsh_add_product_orders_spectrum( res1Deso_frame, frameset, parameters,
                                RECIPE_ID, instrument,NULL));

  check( xsh_add_product_pre( res2D_frame, frameset, parameters,
                              RECIPE_ID, instrument));
  check(xsh_monitor_spectrum1D_flux(s1D_frame,instrument));

  check( xsh_add_product_spectrum( s1D_frame, frameset, parameters,
                                   RECIPE_ID, instrument, NULL));

  if ( do_computemap){

    check( xsh_add_product_image( wavemap, frameset,
                                parameters, RECIPE_ID, instrument,NULL));


    check( xsh_add_product_image( slitmap, frameset,
                                parameters, RECIPE_ID, instrument,NULL));
  }

  if(fluxcal_2D_frame != NULL) {
     check( xsh_add_product_image(fluxcal_rect_2D_frame,frameset,parameters, 
                                  RECIPE_ID, instrument,NULL));
     check( xsh_add_product_orders_spectrum(fluxcal_rect_1D_frame,frameset,parameters,
                                  RECIPE_ID, instrument,NULL));

     check( xsh_add_product_spectrum( fluxcal_2D_frame, frameset, parameters, 
                                      RECIPE_ID, instrument, NULL));
     check( xsh_add_product_spectrum( fluxcal_1D_frame, frameset, parameters, 
                                      RECIPE_ID, instrument, NULL));
  }

  if(frm_eff!=NULL) {
     check(xsh_add_product_table(frm_eff, frameset,parameters, 
                                       RECIPE_ID, instrument,NULL));
  }

  cleanup:

    xsh_end( RECIPE_ID, frameset, parameters);

    xsh_instrument_free( &instrument);
    xsh_free_frameset( &raws);
    xsh_free_frameset( &calib);
    XSH_FREE( rec_prefix);
    XSH_FREE(ipol_bp);

    xsh_free_frameset( &raws_ord_set);
    xsh_free_frameset( &raws_avg);
    xsh_free_frame( &wavemap);
    xsh_free_frame( &slitmap);
    xsh_free_frame( &comb_frame);
    xsh_free_frameset(&crh_clean);

    XSH_FREE( rectify_par);
    XSH_FREE( crh_single_par);
    XSH_FREE( loc_obj_par);
    XSH_FREE( slit_limit_par);
    XSH_FREE( combine_nod_par);
    XSH_FREE( extract_par);
    XSH_FREE( stack_par);

    xsh_free_frameset( &nod_set);
    xsh_free_frameset( &comb_set);
    xsh_free_frameset( &comb_eff_set);
    xsh_free_frame( &comb_eff_frame);
    xsh_free_frame( &bpmap);
    xsh_free_frame( &response_ord_frame);
    xsh_free_frame( &response_frame);
    xsh_free_frame( &combeso_frame);
    xsh_free_frame( &combeffeso_frame);
    xsh_free_frame( &res2D_frame);
    xsh_free_frame( &loc_table_frame);
    xsh_free_frame( &res1D_frame);
    xsh_free_frame( &res1D_eff_frame);

    xsh_free_frame( &res1Deso_eff_frame);
    xsh_free_frame( &res1Deso_frame);
    xsh_free_frame( &s1D_frame);
    xsh_free_frame(&fluxcal_rect_1D_frame) ;
    xsh_free_frame(&fluxcal_rect_2D_frame) ;
    xsh_free_frame(&fluxcal_1D_frame) ;
    xsh_free_frame(&fluxcal_2D_frame) ;
    xsh_free_frame(&nrm_1D_frame) ;
    xsh_free_frame(&nrm_2D_frame) ;
    xsh_free_frame(&frm_eff) ;

    return cpl_error_get_code();
}

/**@}*/
