/*                                                                            *
 *   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 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA     *
 *                                                                            */

/*
 * $Author: amodigli $
 * $Date: 2012-12-18 14:12:51 $
 * $Revision: 1.127 $
*/

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup xsh_wavecal   xsh_wavecal
 * @ingroup recipes
 *
 * This recipe calibrates the arc lines.
 * See man-page for details.
 */
/*----------------------------------------------------------------------------*/
/**@{*/

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


/* DRL steps */

/* Error handling */
#include <xsh_error.h>
/* Utility fonctions */
#include <xsh_utils.h>
#include <xsh_model_utils.h>
#include <xsh_msg.h>
/* DFS functions */
#include <xsh_dfs.h>
#include <xsh_pfits.h>
/* DRL functions */
#include <xsh_drl.h>
#include <xsh_drl_check.h>
#include <xsh_data_instrument.h>
#include <xsh_model_kernel.h>
#include <xsh_model_arm_constants.h>

/* Library */
#include <cpl.h>
#include <math.h>

/* Xsh Wavecal */

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

#define RECIPE_ID "xsh_wavecal"
#define RECIPE_AUTHOR "L.Guglielmi"
#define RECIPE_CONTACT "amodigli@eso.org"

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

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

static int xsh_wavecal_create(cpl_plugin *);
static int xsh_wavecal_exec(cpl_plugin *);
static int xsh_wavecal_destroy(cpl_plugin *);

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

/*-----------------------------------------------------------------------------
  Static variables
  ----------------------------------------------------------------------------*/
static char xsh_wavecal_description_short[] =
"Compute arclines tilt and instrument resolution";

static char xsh_wavecal_description[] =
"This recipe detects and follow arc lines in a fully illuminated slit\n\
frame.\n\
Input Frames:\n\
  Raw frame (Tag = ARC_SLIT_arm)\n\
  Arc Line List (Tag = ARC_LINE_LIST_arm)\n\
  Master Bias (Tag = MASTER_BIAS_arm)\n\
  [OPTIONAL] Master Dark (Tag = MASTER_DARK_arm)\n\
  Order Table (Tag = ORDER_TABLE_EDGES_arm)\n\
  [poly mode] Wave Solution (Tag = WAVE_TAB_2D_arm)\n\
  [poly mode] Theoretical Map (Tag = THEO_TAB_spec_arm, spec=SING/IFU)\n\
  [physical model mode] Model cfg tab (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-To compute spectral resolution] Dispersion Table Frame (Tag = DISP_TAB_arm)\n\
Prepare PRE structures.\n\
Subtract the master Dark (UVB, VIS and NIR)\n\
Substract the master Bias (UVB and VIS)\n\
Divide by Flat.\n\
Detect and follow arc lines.\n\
Products:\n\
   Wavelength solution, PRO.CATG = WAVE_TAB_ARC_SLIT_arm [if poly mode]\n\
   Linetilt list, PRO.CATG = TILT_TAB_SLIT_arm\n\
   Residuals table, PRO.CATG = RESID_TAB_GOOD_LINES_arm\n\
   Wave Map, PRO.CATG = WAVE_MAP_arm [if model-wavemap-compute=TRUE]\n\
   In case of IFU mode cdata previous producs repat for each IFU slices\n\
   Arc frame, Bias subtracted in PRE format, PRO.CATG = ARC_BIAS_SUBTRACT_arm\n\
   If arm=UVB/VIS and PRO.CATG=ARC_NIR_ON if arm=NIR\n\
   Arc frame, Bias subtracted, FLAT-FIELDED, PRO.CATG = WAVECAL_FLATFIELDED_arm";
   

/*-----------------------------------------------------------------------------
  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_wavecal_description_short,       /* Short help */
                  xsh_wavecal_description,             /* Detailed help */
                  RECIPE_AUTHOR,                     /* Author name */
                  RECIPE_CONTACT,                    /* Contact address */
                  xsh_get_license(),                 /* Copyright */
                  xsh_wavecal_create,
                  xsh_wavecal_exec,
                  xsh_wavecal_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_wavecal_create(cpl_plugin *plugin)
{
  cpl_recipe *recipe = NULL;
  //char paramname[256];
  //cpl_parameter* p=NULL;

  /* Reset library state */
  xsh_init();

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

  /* Window for gaussian fits (at each pixel) */
  check(xsh_parameters_wavecal_range_create( RECIPE_ID,
					     recipe->parameters));

  check(xsh_parameters_wavecal_margin_create( RECIPE_ID,
					      recipe->parameters));

  check( xsh_parameters_wavecal_s_n_create( RECIPE_ID,
					    recipe->parameters));

  check( xsh_parameters_clipping_tilt_create( RECIPE_ID,
					      recipe->parameters ) ) ;

  check( xsh_parameters_clipping_specres_create( RECIPE_ID,
                                              recipe->parameters ) ) ;

 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_wavecal_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_wavecal(recipe->parameters, recipe->frames);

 cleanup:
  if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
    xsh_error_dump(CPL_MSG_ERROR);
    /* RESTORE the error context */
    cpl_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_wavecal_destroy(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;

  xsh_free_parameterlist(&recipe->parameters);

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

static void xsh_wavecal_get_parameters( cpl_parameterlist* parameters,
					xsh_follow_arclines_param * par )
{
  check( par->range = xsh_parameters_wavecal_range_get( RECIPE_ID,
							parameters));
  check( par->margin = xsh_parameters_wavecal_margin_get( RECIPE_ID,
							  parameters));
  check( par->s_n_min = xsh_parameters_wavecal_s_n_get( RECIPE_ID,
							parameters));
  check( par->tilt_clipping = xsh_parameters_clipping_tilt_get( RECIPE_ID,
							   parameters));
  check( par->specres_clipping = xsh_parameters_clipping_specres_get( RECIPE_ID,
                                                           parameters)); 
  cleanup:
    return ;
}








static cpl_error_code 
xsh_params_monitor(xsh_follow_arclines_param follow_param)
{
  xsh_msg_dbg_low("follow arclines: range=%d margin=%d",
	  follow_param.range,follow_param.margin);

  return cpl_error_get_code();

}




static cpl_error_code 
xsh_params_set_defaults(cpl_parameterlist* pars,
                        xsh_instrument* inst)
{
   cpl_parameter* p=NULL;
   check(p=xsh_parameters_find(pars,RECIPE_ID,"followarclines-min-sn"));
   if(cpl_parameter_get_double(p) <= 0) {
      if (xsh_instrument_get_mode(inst) == XSH_MODE_IFU){
         cpl_parameter_set_double(p,6);
      } else {
         cpl_parameter_set_double(p,15);
      }
   } 


 cleanup:
 
  return cpl_error_get_code();

}




/*--------------------------------------------------------------------------*/
/**
  @brief    Scale input parameters
  @param    raws   the frames list
  @param    follow_param parameter structure to control line tilt follow

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

static cpl_error_code 
xsh_params_bin_scale(cpl_frameset* raws,
                     xsh_follow_arclines_param follow_param)
{

  cpl_frame* frame=NULL;
  const char* name=NULL;
  cpl_propertylist* plist=NULL;
  int binx=0;
  int biny=0;

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

  if(biny>1) {

    follow_param.range=follow_param.range/biny;

  }


  if(binx>1) {

    follow_param.margin=follow_param.margin/binx;

  }
 
 cleanup:
  xsh_free_propertylist(&plist);
  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_wavecal(cpl_parameterlist* parameters,
			 cpl_frameset* frameset)
{
  const char* recipe_tags[1] = {XSH_WAVECAL};
  int recipe_tags_size = 1;

  const char *prefix = "ARC_";
  /* Intermediate frames */  
  cpl_frameset* raws = NULL;
  cpl_frameset* calib = NULL;
  cpl_frameset* on = NULL;
  cpl_frameset* off = NULL;
  cpl_frameset* on_off = NULL;

  cpl_frame * raw_frame = NULL ;
  cpl_frame * bpmap = NULL;
  cpl_frame * order_tab_edges = NULL ;
  cpl_frame * arc_line_list = NULL ;
  cpl_frame * model_config_frame = NULL;
  cpl_frame * spectralformat_frame = NULL;
  cpl_frame * wave_tab_2d = NULL ;
  cpl_frame * master_bias = NULL ;
  cpl_frame * master_dark = NULL ;
  cpl_frame * rmbias = NULL ;
  cpl_frame * intFrame = NULL ;
  cpl_frame * resFrame = NULL ;
  cpl_frame *wavemap_frame = NULL;
  cpl_frame *disptab_frame = NULL;
  cpl_frame *slitmap_frame = NULL;

  /* Product frames (SLIT mode) */
  cpl_frame * shiftFrame = NULL ;
  cpl_frame * tilt_list = NULL ;

  /* Product frames (IFU mode) */
  cpl_frameset * tilt_set = NULL ;

  /* Other */
  xsh_instrument* instrument = NULL;
  xsh_follow_arclines_param follow_param;
  int follow_param_init=false;
  //char fname[256];
  //char tag[256];
 
  //char * prefix = NULL ;
  /* used for annealing: but we do not anneal in wavecal
  cpl_frame * opt_model_config_frame = NULL;
  int maxit=200;
  double ann_fac=1.0;
  int scenario=-1;
  char paramname[256];
  cpl_parameter * p =NULL;
  */
  char file_prefix[10];
  char tag[40];
  //int resid_size=0;
  int pre_overscan_corr=0;

  const char* rec_prefix ="xsh_wavecal";

  /**************************************************************************/
  /* DFS management */
  /**************************************************************************/
  xsh_begin( frameset, parameters, &instrument, &raws, &calib, 
    recipe_tags, recipe_tags_size,
    RECIPE_ID, XSH_BINARY_VERSION, xsh_wavecal_description_short );


  xsh_recipe_params_check(parameters,instrument,RECIPE_ID);
  /**************************************************************************/
  /* Recipe frames */
  /**************************************************************************/
  check(bpmap = xsh_check_load_master_bpmap(calib,instrument,RECIPE_ID));
  check(order_tab_edges = xsh_find_order_tab_edges(calib,instrument));
  spectralformat_frame = xsh_find_spectral_format( calib, instrument);
  xsh_error_reset();
  if((model_config_frame = xsh_find_frame_with_tag(calib,
                                                   XSH_MOD_CFG_OPT_2D,
                                               instrument)) == NULL) {

     xsh_error_reset();

     if ((model_config_frame = xsh_find_frame_with_tag(calib,XSH_MOD_CFG_TAB, 
                                               instrument)) == NULL) {
     xsh_error_reset();
     }

  }

  xsh_error_reset();
  check(arc_line_list = xsh_find_frame_with_tag(calib,
					   XSH_ARC_LINE_LIST,
					   instrument));

  check(wave_tab_2d = xsh_find_wave_tab_2d( calib, instrument ));

  check( wavemap_frame = xsh_find_wavemap( calib, instrument));
  if (wavemap_frame){
    wavemap_frame = cpl_frame_duplicate( wavemap_frame);
  }
  check( slitmap_frame = xsh_find_slitmap( calib, instrument));
  if ( slitmap_frame){
    slitmap_frame = cpl_frame_duplicate( slitmap_frame);
  }

  if( NULL==(disptab_frame = xsh_find_disp_tab( calib, instrument))) {
     xsh_msg_warning("No input DISP_TAB_ARM provided. Spectral Resolution will not be computed");
  }

 
  /* In UVB and VIS mode */
  if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
    /* RAWS frameset must have only one file */
     XSH_ASSURE_NOT_ILLEGAL_MSG(cpl_frameset_get_size(raws) == 1,
                                "UVB,VIS arm provide one slit,arc lamp frame");

    if((master_bias = xsh_find_frame_with_tag(calib,XSH_MASTER_BIAS,
					      instrument)) == NULL) {

      xsh_msg_warning("Frame %s not provided",XSH_MASTER_BIAS);
      xsh_error_reset();
    }

    //if((master_dark = xsh_find_frame_with_tag(calib,XSH_MASTER_DARK,
    //				      instrument)) == NULL){
    if ( (master_dark = xsh_find_master_dark(calib,instrument)) == NULL ) {
      xsh_msg_warning("Frame %s not provided",XSH_MASTER_DARK);
      xsh_error_reset();
    }
    else xsh_msg( "Frame %s Found", XSH_MASTER_DARK ) ;
  }
  /* IN NIR mode */
  else {
    /* split on and off files */
    check(xsh_dfs_split_nir(raws,&on,&off));
    XSH_ASSURE_NOT_ILLEGAL_MSG(cpl_frameset_get_size(on) == 1,
                                "NIR arm provide one slit,arc lamp on frame");
    XSH_ASSURE_NOT_ILLEGAL_MSG(cpl_frameset_get_size(off) == 1,
                                "NIR arm provide one slit,arc lamp off frame");
  }
  check( xsh_instrument_update_from_spectralformat( instrument,
						    spectralformat_frame)); 


  /**************************************************************************/
  /* Recipe parameters */
  /**************************************************************************/
  check(xsh_params_set_defaults(parameters,instrument));
  check(xsh_wavecal_get_parameters( parameters, &follow_param ));
  follow_param_init=true;
  xsh_msg_dbg_low( "Parameters: Y range = %d, X Margin = %d", follow_param.range,
	   follow_param.margin ) ;
  xsh_msg_dbg_low( "Clipping Tilt Parameters: sigma = %.2lf, Niter = %d, Frac = %.2lf",
	   follow_param.tilt_clipping->sigma,
	   follow_param.tilt_clipping->niter,
	   follow_param.tilt_clipping->frac ) ;

  /* adjust relevant parameter to binning */
  if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
    check(xsh_params_monitor(follow_param));
    check(xsh_params_bin_scale(raws,follow_param));
  }
  check(xsh_params_monitor(follow_param));
 

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

  /* In UVB and VIS mode */
  if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
    check(xsh_prepare( raws, bpmap, master_bias, XSH_WAVECAL, instrument,pre_overscan_corr,CPL_TRUE));
    check(raw_frame = cpl_frameset_get_frame(raws,0));

    /* Subtract bias if necessary */
    if(master_bias != NULL) {
      xsh_msg( "Subtract bias" );
      sprintf(file_prefix,"ARC_%s_",xsh_instrument_mode_tostring(instrument));
      check(rmbias = xsh_subtract_bias( raw_frame, master_bias, instrument,file_prefix,pre_overscan_corr,0));
    } else {
      rmbias=cpl_frame_duplicate(raw_frame);
    }


    /* Subtract Dark if necessary */
    check( intFrame = xsh_check_subtract_dark( rmbias, master_dark,
      instrument, prefix));
  }
  else{
    /* do ON - OFF */
    check(xsh_prepare(on, bpmap, NULL, "ON", instrument,pre_overscan_corr,CPL_TRUE));
    check(xsh_prepare(off,bpmap, NULL, "OFF", instrument,pre_overscan_corr,CPL_TRUE));

    xsh_msg( "Subtract ON-OFF" ) ;
    check( on_off = xsh_subtract_nir_on_off(on, off, instrument));
    XSH_ASSURE_NOT_ILLEGAL(cpl_frameset_get_size(on_off) == 1);
    check( intFrame = cpl_frame_duplicate( cpl_frameset_get_frame( on_off,0)));
  }



  /* generate wavemap if necessary */
  if ( model_config_frame != NULL && wavemap_frame == NULL){
    char wave_map_tag[256];
    char slit_map_tag[256];
    int found_temp=true;

    xsh_free_frame( &slitmap_frame);
    sprintf(wave_map_tag,"%s_%s_%s",rec_prefix,XSH_WAVE_MAP_MODEL,
              xsh_instrument_arm_tostring( instrument ));
    sprintf(slit_map_tag,"%s_%s_%s",rec_prefix,XSH_SLIT_MAP_MODEL,
              xsh_instrument_arm_tostring( instrument ));

    
    check(xsh_model_temperature_update_frame(&model_config_frame,intFrame,
                                         instrument,&found_temp));
    
    check( xsh_create_model_map( model_config_frame, instrument,
                                   wave_map_tag,slit_map_tag,
                                   &wavemap_frame, &slitmap_frame,1));
  }


/*  check( resFrame = xsh_check_divide_flat( CPL_TRUE, intFrame, master_flat,
    instrument, prefix));
*/

  /* xsh_follow_arclines */
  /* Separate SLIT and IFU handling */
  if ( xsh_instrument_get_mode( instrument) == XSH_MODE_SLIT) {
    xsh_msg( "Call xsh_follow_arclines, SLIT mode" );
    check( xsh_follow_arclines_slit( intFrame, arc_line_list,
				wave_tab_2d, order_tab_edges,
				spectralformat_frame, model_config_frame,
                                wavemap_frame, slitmap_frame,
                                disptab_frame,
				&follow_param, instrument,
				&tilt_list,
				&shiftFrame));

    /*********************************************************************/
    /* Products SLIT mode */
    /*********************************************************************/
    xsh_msg("Saving products, SLIT mode");

    XSH_ASSURE_NOT_NULL( tilt_list);
    xsh_msg_dbg_low( "Tilt List Product from '%s'", 
      cpl_frame_get_filename(tilt_list));
    check( xsh_add_product_table( tilt_list, frameset, parameters, RECIPE_ID,
                                  instrument,NULL));
  }
  else {
    int i ;

    xsh_msg( "Call xsh_follow_arclines, IFU mode" ) ;
    check( tilt_set = cpl_frameset_new() ) ;
    check( xsh_follow_arclines_ifu( intFrame, arc_line_list,
				    wave_tab_2d, order_tab_edges,
				    spectralformat_frame, model_config_frame,
                                    wavemap_frame, slitmap_frame, disptab_frame,
                                    &follow_param, instrument,
				    tilt_set, &shiftFrame));
    /*********************************************************************/
    /* Products IFU mode */
    /*********************************************************************/
    xsh_msg("Saving products, IFU mode");
    /* In IFU mode we have 3 products per slitlet */
    for( i = 0 ; i<3 ; i++ ) {
      //const char * wname = NULL ;
      cpl_frame *tilt_frame = NULL;

      check( tilt_frame = cpl_frameset_get_frame( tilt_set, i));
      xsh_msg_dbg_low( "Product from '%s'",
	       cpl_frame_get_filename( tilt_frame));
      check( xsh_add_product_table( tilt_frame, frameset, parameters,
				    RECIPE_ID, instrument,NULL));
    }
  }

  /*********************************************************************/
  /* Others Products  */
  /*********************************************************************/
  if ( xsh_instrument_get_mode( instrument) == XSH_MODE_SLIT) {
     sprintf(tag,"ARC_SLIT_ON");
  } else {
     sprintf(tag,"ARC_IFU_ON");
  }
  if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
    xsh_msg_dbg_low( "Product from '%s'",
	       cpl_frame_get_filename( rmbias));

    check(xsh_add_product_image(rmbias,frameset,parameters,
   			      RECIPE_ID,instrument,tag));
    /*
    check(xsh_add_product_pre(rmbias,frameset,parameters,
			      RECIPE_ID,instrument));
    */
  } else {
    xsh_msg_dbg_low( "Product from '%s'",
	       cpl_frame_get_filename( intFrame));
    check(xsh_add_product_image(intFrame,frameset,parameters,
   			      RECIPE_ID,instrument,tag));
    /*
    check(xsh_add_product_pre(intFrame,frameset,parameters,
			      RECIPE_ID,instrument));
    */
  }

  xsh_msg_dbg_low( "Product from '%s'",
	       cpl_frame_get_filename( shiftFrame));
  check( xsh_add_product_table( shiftFrame, 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_frame( &wavemap_frame);
    xsh_free_frame( &slitmap_frame);
    xsh_free_frame( &rmbias);
    xsh_free_frame( &intFrame);
    xsh_free_frameset( &on);
    xsh_free_frameset( &off);
    xsh_free_frameset( &on_off);
    xsh_free_frame( &resFrame);
    xsh_free_frame( &shiftFrame);
    if(follow_param_init) {
       XSH_FREE( follow_param.tilt_clipping);
       XSH_FREE( follow_param.specres_clipping);
    }
    xsh_free_frame( &tilt_list);
    xsh_free_frameset( &tilt_set);

    return;
}
/**@}*/
