/*                                                                            *
 *   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-04-02 13:43:34 $
 * $Revision: 1.181 $
 *

*/

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup xsh_slit_nod   xsh_scired_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_msg.h>
#include <xsh_model_utils.h>
#include <xsh_utils_scired_slit.h>
#include <xsh_data_instrument.h>
#include <xsh_data_spectrum1D.h>
#include <xsh_utils_image.h>
#include <xsh_data_spectrum.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>

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

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

#define RECIPE_ID "xsh_scired_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_scired_slit_nod_create(cpl_plugin *);
static int xsh_scired_slit_nod_exec(cpl_plugin *);
static int xsh_scired_slit_nod_destroy(cpl_plugin *);

/* The actual executor function */
static void xsh_scired_slit_nod(cpl_parameterlist *, cpl_frameset *);

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

static char xsh_scired_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 non-linear badpixel map (Tag = BP_MAP_NL_arm)\n\
  - [OPTIONAL] A reference badpixel map (Tag = BP_MAP_RP_arm)\n\
  - [OPTIONAL] The instrument response table (Tag = RESPONSE_MERGE1D_SLIT_arm)\n\
  - [OPTIONAL] An atmospheric extinction table (Tag = ATMOS_EXT_arm)\n\
  - [OPTIONAL] A telluric mask (Tag = TELL_MASK_arm)\n\
Products : \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\
  - [OPTIONAL, if response and atm ext are provided]  PREFIX_FLUX_ORDER2D_arm (2 dimension)\n\
  - [OPTIONAL, if response and atm ext are provided]  PREFIX_FLUX_ORDER1D_arm (1 dimension)\n\
  - [OPTIONAL, if response and atm ext are provided]  PREFIX_FLUX_MERGE2D_arm (2 dimension)\n\
  - [OPTIONAL, if response and atm ext are provided]  PREFIX_FLUX_MERGE1D_arm (1 dimension)\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_scired_slit_nod_description_short,   /* Short help */
                  xsh_scired_slit_nod_description,         /* Detailed help */
                  RECIPE_AUTHOR,                     /* Author name */
                  RECIPE_CONTACT,                    /* Contact address */
                  xsh_get_license(),                 /* Copyright */
                  xsh_scired_slit_nod_create,
                  xsh_scired_slit_nod_exec,
                  xsh_scired_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_scired_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,-1);
  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,
              "correct-sky-by-median", TRUE,
   "TRUE if the resampled spectrum at each wavelength is median subtracted to remove sky lines"));

  check( xsh_parameters_new_boolean( recipe->parameters, RECIPE_ID,
      "cut-uvb-spectrum", TRUE,
      "TRUE if recipe cuts the UVB spectrum at 556 nm (dichroich)"));
/*
  check( xsh_parameters_new_boolean( recipe->parameters, RECIPE_ID,
    "do-flatfield", TRUE, 
    "TRUE if we do the flatfielding"));
*/

  /* Flag for generation of data in SDP format. */
  check( xsh_parameters_new_boolean( recipe->parameters, RECIPE_ID,
    "generate-SDP-format", FALSE,
    "TRUE if additional files should be generated in Science Data Product"
    " (SDP) format."));
  
  /* Flag for adding dummy ASSO[NCM]i keywords. */
  check( xsh_parameters_new_int( recipe->parameters, RECIPE_ID,
    "dummy-association-keys", 0,
    "Sets the number of dummy (empty) ASSONi, ASSOCi and ASSOMi keywords to"
    " create."));

 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_scired_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_scired_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_scired_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;
    }
}
#if 0
static void change_file_name( cpl_frame * frame, const char * name )
{
  const char * old_name ;
  char * cmd ;

  old_name = cpl_frame_get_filename( frame ) ;
  cmd = xsh_stringcat_any( "mv ", old_name, " ", name, (void*)NULL ) ;
  cpl_frame_set_filename( frame, name ) ;

  system( cmd ) ;
  XSH_FREE( cmd ) ;
}
#endif


/** 
  @brief set binning
  @param raws input raw frames
  @param binx  x binning
  @param biny  y binning
 */

static void
xsh_get_binning(cpl_frameset* raws,int* binx, int* biny)
{
  cpl_frame* frame=NULL;
  const char* name=NULL;
  cpl_propertylist* plist=NULL;
  int nraw=0;

  XSH_ASSURE_NOT_NULL_MSG(raws,"Null pointer for input raw frameset");
  nraw=cpl_frameset_get_size(raws);
  /* xsh_msg("nraw=%d",nraw); */
  XSH_ASSURE_NOT_ILLEGAL_MSG(nraw > 0,"nraw = 0 Provide at least a raw frame");
 
  check(frame=cpl_frameset_get_frame(raws,0));
  check(name=cpl_frame_get_filename(frame));
  check(plist=cpl_propertylist_load(name,0));
  check(*binx=xsh_pfits_get_binx(plist));
  check(*biny=xsh_pfits_get_biny(plist));

 cleanup:
  xsh_free_propertylist(&plist);

  return;
}


/*--------------------------------------------------------------------------*/
/**
  @brief    Corrects input parameters for binning
  @param    binx   x bin
  @param    biny   y bin

  In case of failure the cpl_error_code is set.
 */
/*--------------------------------------------------------------------------*/

/*
static cpl_error_code 
xsh_params_bin_scale(int binx,int biny)
{

  if(biny>1) {

    rectify_par->rectif_radius=rectify_par->rectif_radius/biny;
    //Rectify Interpolation radius.
    //For the moment not bin dependent, but for optimal results probably yes
   
    rectify_par->rectif_bin_lambda=rectify_par->rectif_bin_lambda/biny;
    //Rectify Wavelength Step.
    //For the moment not bin dependent, but for optimal results probably yes
      
    loc_obj_par->loc_chunk_nb=loc_obj_par->loc_chunk_nb/biny;
    //Localization Nb of chunks.
    //Not bin dependent
   
  }

  if(binx>1) {

    rectify_par->rectif_bin_space=rectify_par->rectif_bin_space/binx;
    //Rectify Position Step.
    //For the moment not bin dependent, but for optimal results probably yes
   
    loc_obj_par->nod_step=loc_obj_par->nod_step/binx;
    //Step (arcsec) between A and B images in nodding mode.
    //For the moment not bin dependent, but for optimal results probably yes
   
  }
 
  return cpl_error_get_code();

}
*/

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

   In case of failure the cpl_error_code is set.
*/
/*----------------------------------------------------------------------------*/
static void xsh_scired_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;
  cpl_frameset *usedframes = 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;
  cpl_frame *tellmask_frame = 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;
  int merge_par = 0;
  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;
  //cpl_frame *throw_frame = NULL;
  char comb_tag[256];
  int binx=0;
  int biny=0;

  int nb_raw_frames;

  /* Intermediate frames */

  //cpl_vector* offsets=NULL;
  //cpl_vector* offsets_dif=NULL;
  //int ngroups=0;
  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;
  //int noffs=0;
  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 *combeso_frame = NULL;
  cpl_frame *res2D_frame = NULL;
  cpl_frame *loc_table_frame = NULL;
  cpl_frame *res1D_frame = NULL;
  cpl_frame *res1Deso_frame = NULL;
  cpl_frame *s1D_frame = NULL;
  cpl_frame* response_ord_frame=NULL;
  cpl_frame * fluxcal_1D_frame = NULL ;	
  cpl_frame * fluxcal_2D_frame = NULL ;	
  cpl_frame * fluxcal_rect_1D_frame = NULL ;	
  cpl_frame * fluxcal_rect_2D_frame = NULL ;	
  cpl_frame * fluxframe = NULL;
  cpl_frame * uncalframe = NULL;
  cpl_frame* sky_map_frm = NULL;
  cpl_frame* frm_atmext=NULL;
  int pre_overscan_corr=0;
  int generate_sdp_format=0;
  xsh_interpolate_bp_param *ipol_bp=NULL;
  int corr_sky=0;
  int cut_uvb_spectrum=0;
  /**************************************************************************/
  /* DFS management */
  /**************************************************************************/
  check( xsh_begin( frameset, parameters, &instrument, &raws, &calib,
		    recipe_tags, recipe_tags_size, RECIPE_ID, 
		    XSH_BINARY_VERSION,
		    xsh_scired_slit_nod_description_short));


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

  check(xsh_ensure_raws_number_is_even(raws));
  xsh_recipe_params_check(parameters,instrument,RECIPE_ID);
  if(instrument->arm == XSH_ARM_NIR) {
    xsh_instrument_nir_corr_if_JH(raws,instrument);
  }
  /**************************************************************************/
  /* Recipe frames */
  /**************************************************************************/
  check( rec_prefix = xsh_set_recipe_file_prefix(raws,"xsh_scired_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);
 
  /**************************************************************************/
  /* Recipe parameters */ 
  /**************************************************************************/
  cut_uvb_spectrum=xsh_parameters_cut_uvb_spectrum_get(RECIPE_ID,parameters);
  check( stack_par = xsh_stack_frames_get( RECIPE_ID, parameters));
/*
  check( do_flatfield = xsh_parameters_get_boolean( parameters, RECIPE_ID,
    "do-flatfield"));
*/

  if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
    xsh_get_binning(raws, &binx, &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_sky = xsh_parameters_get_boolean( parameters, RECIPE_ID,
                                                      "correct-sky-by-median"));

  check( xsh_scired_slit_nod_get_calibs(raws,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));

  tellmask_frame = xsh_find_frame_with_tag(calib,XSH_TELL_MASK, instrument);
  
  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");
  }

  /**************************************************************************/
  /* Recipe code */
  /**************************************************************************/
  rectify_par->conserve_flux=FALSE;
  check( xsh_prepare( raws_ord_set, bpmap, master_bias, XSH_OBJECT_SLIT_NOD,
		      instrument,pre_overscan_corr,CPL_TRUE));

 /* make sure each input raw frame has the same exp time */
  check(xsh_frameset_check_uniform_exptime(raws_ord_set,instrument));
  /* we fist flag CRH from each raw frame */
  cpl_frameset* crh_clean=NULL;
  sky_map_frm = xsh_find_frame_with_tag(calib,XSH_SKY_MAP, instrument);
  check(crh_clean = xsh_frameset_crh_single(raws_ord_set, crh_single_par,
                  sky_map_frm, instrument, rec_prefix, "NOD"));

  /* combine nod frames: AAA BBB BBB AAA AAAAA BBBBB BBBBB AAAAAA--> A B B A A B B A */
  check( raws_avg = xsh_nod_group_by_reloff( crh_clean,instrument,stack_par));
  xsh_free_frameset(&crh_clean);
  /* creates pairs [A-B]s and [B-A]s */
  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
                                 ));


  }

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

  /* 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));
  check( xsh_mark_tell( s1D_frame, tellmask_frame));



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



  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));

     }

  }

  if(cut_uvb_spectrum) {
      if(instrument->arm == XSH_ARM_UVB) {
          check(xsh_image_cut_dichroic_uvb(combeso_frame));
          check(xsh_spectrum_orders_cut_dichroic_uvb(res1Deso_frame,instrument));
          check(xsh_spectrum_cut_dichroic_uvb(res2D_frame));
          check(xsh_spectrum_cut_dichroic_uvb(s1D_frame));
          if(fluxcal_rect_2D_frame != NULL) {
              check(xsh_image_cut_dichroic_uvb(fluxcal_rect_2D_frame));
              check(xsh_spectrum_orders_cut_dichroic_uvb(fluxcal_rect_1D_frame,instrument));
              check(xsh_image_cut_dichroic_uvb(fluxcal_2D_frame));
              check(xsh_spectrum_cut_dichroic_uvb(fluxcal_1D_frame));
          }
      }
  }


  /* Save products */
  usedframes = cpl_frameset_duplicate(frameset);
  
  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, &uncalframe));

  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, &fluxframe));
  }

  if (generate_sdp_format) {
    cpl_frame * fluxframe_copy = fluxframe;
    cpl_frame * uncalframe_copy = uncalframe;
    if (fluxframe != NULL) {
      check( cpl_frameset_insert(usedframes, fluxframe) );
      fluxframe = NULL;  /* prevent direct deletion in cleanup section,
                            will be deleted together with usedframes. */
    }
    if (uncalframe != NULL) {
      check( cpl_frameset_insert(usedframes, uncalframe) );
      uncalframe = NULL;  /* prevent direct deletion in cleanup section,
                             will be deleted together with usedframes. */
    }
    check( xsh_add_sdp_product_spectrum(fluxframe_copy, uncalframe_copy,
                    frameset, usedframes, parameters, RECIPE_ID, instrument) );
    /* Restore usedframes to what it was before in case we want to reuse it */
    if (fluxframe_copy != NULL) {
      check( cpl_frameset_erase_frame(usedframes, fluxframe_copy) );
    }
    if (uncalframe_copy != NULL) {
      check( cpl_frameset_erase_frame(usedframes, uncalframe_copy) );
    }
  }

  cleanup:

    xsh_end( RECIPE_ID, frameset, parameters);
    xsh_instrument_free( &instrument);
    xsh_free_frameset( &raws);
    xsh_free_frameset( &calib);
    xsh_free_frameset(&usedframes);
    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( stack_par);
    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_frameset( &nod_set);
    xsh_free_frameset( &comb_set);
    xsh_free_frameset( &comb_eff_set);
    xsh_free_frame( &comb_frame);
    xsh_free_frame( &combeso_frame);
    xsh_free_frame( &res2D_frame);
    xsh_free_frame( &loc_table_frame);
    xsh_free_frame( &res1D_frame);

    xsh_free_frame( &res1Deso_frame);
    xsh_free_frame( &s1D_frame);
    xsh_free_frame(&fluxcal_1D_frame) ;
    xsh_free_frame(&fluxcal_2D_frame) ;
    xsh_free_frame(&fluxcal_rect_1D_frame) ;
    xsh_free_frame(&fluxcal_rect_2D_frame) ;

    xsh_free_frame(&fluxframe);
    xsh_free_frame(&uncalframe);

    return;
}

/**@}*/
