/*                                                                            *
 *   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.59 $
 * $Name: not supported by cvs2svn $
 *
 */

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

/*--------------------------------------------------------------------------*/
/**
 * @defgroup xsh_flexcomp   xsh_flexcomp
 * @ingroup recipes
 *
 * This recipe is used compute flexure compensation 
 * See man-page for details.
 */
/*--------------------------------------------------------------------------*/
/**@{*/

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


//#include <xsh_pfits.h>
/* DRL steps */

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

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

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

#define RECIPE_ID "xsh_flexcomp"
#define RECIPE_AUTHOR "P.Goldoni, L.Guglielmi, R. Haigron, F. Royer"
#define RECIPE_CONTACT "amodigli@eso.org"

#define XSH_AFC_UVB_XMIN 573
#define XSH_AFC_UVB_YMIN 1501
#define XSH_AFC_UVB_XMAX 1572
#define XSH_AFC_UVB_YMAX 2500
#define XSH_AFC_UVB_ORDER 18

#define XSH_AFC_VIS_XMIN 534
#define XSH_AFC_VIS_YMIN 2301
#define XSH_AFC_VIS_XMAX 1533
#define XSH_AFC_VIS_YMAX 3300
#define XSH_AFC_VIS_ORDER 26

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

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

static int xsh_flexcomp_create(cpl_plugin *);
static int xsh_flexcomp_exec(cpl_plugin *);
static int xsh_flexcomp_destroy(cpl_plugin *);

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

/*---------------------------------------------------------------------------
                            Static variables
 ---------------------------------------------------------------------------*/
static char xsh_flexcomp_description_short[] =
"Compute the flexure of the instrument";

static char xsh_flexcomp_description[] =
"This recipe computes the flexure of the instrument and correct CAL files.\n\
  Input Frames :\n\
    - [UVB, VIS] One RAW frame (Format = RAW, Tag = AFF_ATT_arm)\n\
    - [NIR] Two RAW frames ((Format = RAW,  Tag = AFC_ATT_arm_ON,\
AFC_ATT_arm_OFF)\n\
    - An arc line list (Format = TABLE, Tag = ARC_LINE_LIST_AFC_arm)\n\
    - A spectral format table frame (Format = TABLE, Tag = SPECTRAL_FORMAT_TAB_arm)\n\
    - [UVB,VIS] A master bias (Format = PRE, Tag = MASTER_BIAS_arm)\n\
    - [UVB,VIS] A master dark (Format = PRE, Tag = MASTER_DARK_arm)\n\
    - An order table frame (Format = TABLE, Tag = ORDER_TAB_EDGES_IFU_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\
    - [poly mode]  A wave solution frame (Format = TABLE, Tag = WAVE_TAB_2D_arm)\n\
    - [physical model mode] A model cfg table (Format = TABLE, Tag = XSH_MOD_CFG_OPT_2D_arm)\n\
  Products : \n\
    - [poly mode]  An updated wave solution frame (Format = TABLE, Tag = WAVE_TAB_AFC_arm)\n\
    - [physical model mode] An updated  model cfg table (Format = TABLE, Tag = XSH_MOD_CFG_OPT_AFC_arm)\n\
    - An updated order table frame (Format = TABLE, Tag = ORDER_TAB_AFC_IFU_arm)\n\
    - [poly mode] A dispersion table frame (Format = TABLE, Tag =  DISP_TAB_AFC_arm)\n";

/*---------------------------------------------------------------------------
                              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_flexcomp_description_short,      /* Short help */
                  xsh_flexcomp_description,            /* Detailed help */
                  RECIPE_AUTHOR,                    /* Author name */
                  RECIPE_CONTACT,                   /* Contact address */
                  xsh_get_license(),                /* Copyright */
                  xsh_flexcomp_create,
                  xsh_flexcomp_exec,
                  xsh_flexcomp_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_flexcomp_create(cpl_plugin *plugin){
  cpl_recipe *recipe = NULL;
  xsh_clipping_param detarc_clip_param = { 2.0, 10, 0.7, 0, 0.3};
  xsh_detect_arclines_param detarc_param = { 6, 3, 0, 5, 5, 0, 2, 5.0, 
    XSH_GAUSSIAN_METHOD, FALSE};
  xsh_dispersol_param dispsol_param = { 4, 5 } ;
  char paramname[256];
  cpl_parameter* p=NULL;
  int ival=DECODE_BP_FLAG_DEF;

  /* 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,ival);
  check( xsh_parameters_pre_overscan( RECIPE_ID, recipe->parameters ) ) ;
  /* General 2dmap parameters 
  check(  xsh_parameters_new_boolean( recipe->parameters, RECIPE_ID, 
    "compute-disptab",
    TRUE,
    "Compute the wavemap and the slitmap and the dispersion table."));
  */
  /* detect arclines params */
  check(xsh_parameters_detect_arclines_create(RECIPE_ID,recipe->parameters, 
    detarc_param));
  check(xsh_parameters_clipping_detect_arclines_create(RECIPE_ID, 
    recipe->parameters, detarc_clip_param));

  check(xsh_parameters_dispersol_create(RECIPE_ID,
                                 recipe->parameters,
                                 dispsol_param ));

  /* Annealing parameter */
  check(  xsh_parameters_new_int( recipe->parameters, RECIPE_ID, 
                                      "model-maxit",1000,
                                  "Number/10 of annealing iterations "
                                  "if in physical model mode."));

  check(  xsh_parameters_new_double( recipe->parameters, RECIPE_ID, 
                                      "model-anneal-factor",1.0,
                                  "Multiplier applied to the automatic "
                                  "parameter ranges (i.e. when scenario!=0). "
				  "For routine operations should be 1.0. "
                                  "(physical model mode)."));

  
  sprintf(paramname,"xsh.%s.%s",RECIPE_ID,"model-scenario");

  check(p=cpl_parameter_new_enum(paramname,CPL_TYPE_INT,
				 "selects preset flag and range combinations "
                                 "appropriate to common scenarios: "
				 "-1 - Only the position across the slit and"
				 "     camera focal length are open\n"
				 " 0 - No scenario, input cfg flags and limits"
				 "     used."
				 " 1 - scenario appropriate for the startup"
				 "     recipe (large ranges for parameters "
				 "     affecting single ph exposures, dist "
				 "     coeff fixed)"
				 " 2 - Like 1, but includes parameters "
				 "     affecting all ph positions"
				 " 3 - Scenario for use in fine tuning cfg"
				 "     to match routine wavecal exposures. All"
				 "     parameters affecting 1ph exposures"
				 "     except dist coeffs are included and"
				 "     parameter ranges are small. (For use by"
				 "     flexcomp in 1ph case)."
				 " 4 - Like 3 but includes parameters"
				 "     affecting all ph positions (Standard for"
				 "     use by flexcomp in 9ph case and 2dmap)."
				 " 5 - Like 4 but includes also dist coeffs"
				 " 6 - Just dist coeffs (and chipx, chipy)",
                                 RECIPE_ID,3,9,-1,0,1,2,3,4,5,6,8));


  check(cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,
				"model-scenario"));
  check(cpl_parameterlist_append(recipe->parameters,p));


  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_flexcomp_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_flexcomp(recipe->parameters, recipe->frames);

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
      xsh_error_dump(CPL_MSG_ERROR);
      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_flexcomp_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 cpl_error_code 
xsh_params_set_defaults(cpl_parameterlist* pars,
                        xsh_instrument* inst,
                        xsh_detect_arclines_param* detect_arclines_p)
{
   cpl_parameter* p=NULL;

  check(p=xsh_parameters_find(pars,RECIPE_ID,"detectarclines-min-sn"));
  if(cpl_parameter_get_double(p) <= 0) {
    if (xsh_instrument_get_arm(inst) == XSH_ARM_NIR){
      /* if the user has not set the parameter use default */
      cpl_parameter_set_double(p,4);
      detect_arclines_p->min_sn=4;
    } else {
      cpl_parameter_set_double(p,3);
      detect_arclines_p->min_sn=3;
    }
  }

 cleanup:
 
  return cpl_error_get_code();

}

static cpl_error_code
xsh_frame_check_is_right_afcatt(cpl_frame* afcatt_frame,
                                xsh_instrument* instrument)
{
   cpl_propertylist* plist=NULL;
   const char* name=NULL;
   char* slit_value=NULL;
   name=cpl_frame_get_filename(afcatt_frame);
   plist=cpl_propertylist_load(name,0);
   check(slit_value=xsh_pfits_get_slit_value (plist,instrument));
   if (strcmp(slit_value,"Pin_0.5") != 0 ) {
      xsh_msg_error("INS OPTIi NAME value=%s",slit_value);
      xsh_msg_error("Your input AFC frame should be the 1st of the AFC sequence");
      xsh_msg_error("Its INS OPTIi NAME key value should be 'Pin_0.5'");
      cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
   }

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_flexcomp(cpl_parameterlist* parameters, cpl_frameset* frameset)
{
  const char* recipe_tags[1] = {XSH_AFC_ATT};
  int recipe_tags_size = 1;

  /* RECIPES parameters */
  xsh_clipping_param* detect_arclines_clipping = NULL;
  xsh_detect_arclines_param* detect_arclines_p = NULL;
  xsh_dispersol_param *dispsol_param = NULL ;

  /* ALLOCATED locally */
  cpl_frameset* raws = NULL;
  cpl_frameset* calib = NULL;
  xsh_instrument* instrument = NULL;

  cpl_frame* arclist_frame = NULL;
  cpl_frame* afcatt_frame = NULL;
  cpl_frame* spectralformat_frame = NULL;
  cpl_frame* order_tab_edges_frame = NULL;
  cpl_frame* wave_tab_frame = NULL;
  /* Not used */
  cpl_frame* wave_map_frame = NULL;
  cpl_frame* slit_map_frame = NULL;
 
  cpl_frame* model_config_frame = NULL;
  cpl_frame* masterbias_frame = NULL;
  cpl_frame* masterdark_frame = NULL;
  cpl_frame* bpmap_frame = NULL;

  cpl_frame *subbpmap_frame = NULL; 
  cpl_frame *subbias_frame = NULL; 
  cpl_frame *subdark_frame = NULL;
  int afc_xmin=1;

  int afc_ymin=1;

  int afc_order=0;
  cpl_frame* afcatt_rmbias = NULL;
  cpl_frame* afcatt_rmdark = NULL;
  cpl_frame* att_cleanlines = NULL;
  cpl_frame* att_resid_tab = NULL;
  cpl_frame *afcthetab_frame = NULL;
  const char *tag = NULL;
  char filename [256];
  cpl_frame *afc_wave_tab_frame = NULL;
  cpl_frame *afc_order_tab_edges_frame = NULL;
  cpl_frame *afc_disp_tab_frame = NULL;
  cpl_frame *afc_config_tab_frame = NULL;
  XSH_ARM arm = XSH_ARM_UNDEFINED;
  int pre_overscan_corr=0;

  char wave_map_tag[256];
  char slit_map_tag[256];
  int nraws=0;
  cpl_boolean mode_phys;

  check( xsh_begin( frameset, parameters, &instrument, &raws, &calib,
		    recipe_tags, recipe_tags_size,
		    RECIPE_ID, XSH_BINARY_VERSION,
		    xsh_flexcomp_description_short));
  nraws=cpl_frameset_get_size(raws);
  assure(nraws > 0, CPL_ERROR_NULL_INPUT, "Provide an input AFC_ATT_ARM frame");

  xsh_recipe_params_check(parameters,instrument,RECIPE_ID);
  /**************************************************************************/
  /* Recipe frames */
  /**************************************************************************/
  mode_phys=xsh_mode_is_physmod(calib,instrument);
  check(bpmap_frame=xsh_check_load_master_bpmap(calib,instrument,RECIPE_ID));
 check( arclist_frame = xsh_find_frame_with_tag( calib,
                                                 XSH_ARC_LINE_LIST_AFC, 
                                                 instrument));
  XSH_ASSURE_NOT_NULL_MSG( arclist_frame, 
    "No input ARC_LINE_AFC_LIST_ARM arclines list provided");
  check( spectralformat_frame = xsh_find_frame_with_tag( calib, 
    XSH_SPECTRAL_FORMAT, instrument));
  XSH_ASSURE_NOT_NULL_MSG( spectralformat_frame, 
    "No spectral format provided");
  check( order_tab_edges_frame = xsh_find_order_tab_edges( calib,instrument));
  XSH_ASSURE_NOT_NULL_MSG( order_tab_edges_frame,
    "No input ORDER_TABLE_EDGES_MODE_ARM (MODE=IFU or SLIT) table provided");
  
  /* Could be in sof */
  if(!mode_phys) {
    wave_tab_frame = xsh_find_wave_tab( calib, instrument);
  } else {

    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();
      }
    }
  }
  if( (model_config_frame!=NULL) && (wave_tab_frame != NULL) ) {

     xsh_msg_error("You cannot provide both a %s and a %s frame. Decide if you are in poly or physical model mode. We exit",
                   XSH_WAVE_TAB_2D , XSH_MOD_CFG_TAB);
     goto cleanup;
  }

  if((model_config_frame==NULL) && ( wave_tab_frame == NULL) ) {
     xsh_msg_error("You must provide either a %s or a %s frame",
                   XSH_WAVE_TAB_2D, XSH_MOD_CFG_TAB);
     goto cleanup;
  }
  
  check( arm = xsh_instrument_get_arm(instrument));
  /* In UVB and VIS mode */
  if ( arm != XSH_ARM_NIR){
    if((masterbias_frame = 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((masterdark_frame = xsh_find_frame_with_tag(calib,XSH_MASTER_DARK,
					      instrument)) == NULL){
      xsh_msg_warning("Frame %s not provided",XSH_MASTER_DARK);
      xsh_error_reset();
    }
  }
  check( xsh_instrument_update_from_spectralformat( instrument,
    spectralformat_frame));
  check( pre_overscan_corr = xsh_parameters_get_int( parameters, RECIPE_ID,
						     "pre-overscan-corr"));

  /**************************************************************************/
  /* Recipe parameters */
  /**************************************************************************/
  check(detect_arclines_clipping = 
    xsh_parameters_clipping_detect_arclines_get(RECIPE_ID, parameters));
  check(detect_arclines_p = xsh_parameters_detect_arclines_get(RECIPE_ID,
    parameters));
  check( dispsol_param = xsh_parameters_dispersol_get( RECIPE_ID, parameters));
  check(xsh_params_set_defaults(parameters,instrument,detect_arclines_p));

  /**************************************************************************/
  /* Recipe code */
  /**************************************************************************/
  int afc_xmax=1;
  int afc_ymax=1;
  if (arm != XSH_ARM_NIR){
    /* In UVB and VIS mode */
    if ( arm == XSH_ARM_VIS){
      afc_xmin = XSH_AFC_VIS_XMIN-10;
      afc_xmax = XSH_AFC_VIS_XMAX-10;
      afc_ymin = XSH_AFC_VIS_YMIN;
      afc_ymax = XSH_AFC_VIS_YMAX;
      afc_order = XSH_AFC_VIS_ORDER;
    }
    else {
      afc_xmin = 2145-XSH_AFC_UVB_XMAX-48;
      afc_xmax = 2145-XSH_AFC_UVB_XMIN-48;
      afc_ymin = 3001-XSH_AFC_UVB_YMAX;
      afc_ymax = 3001-XSH_AFC_UVB_YMIN;
      afc_order = XSH_AFC_UVB_ORDER;
    }
    xsh_msg("AFC pre coordinates (%d,%d, %d,%d)",afc_xmin,afc_ymin,
      afc_xmax,afc_ymax);
    /* prepare bpmap */
    if ( bpmap_frame != NULL){
      check( subbpmap_frame = xsh_badpixelmap_extract( bpmap_frame, 
      afc_xmin, afc_ymin, afc_xmax, afc_ymax));
    }
    /* prepare bias */
    if ( masterbias_frame != NULL){
      check( subbias_frame = xsh_preframe_extract( masterbias_frame, 
      afc_xmin, afc_ymin, afc_xmax, afc_ymax,
      "TEST_SUBBIAS.fits", instrument));
    }
    /* prepare dark */
    if ( masterdark_frame != NULL){
      check( subdark_frame = xsh_preframe_extract( masterdark_frame,
        afc_xmin, afc_ymin, afc_xmax, afc_ymax,
        "TEST_SUBDARK.fits", instrument));

    }
  }
  check( xsh_prepare( raws, subbpmap_frame, subbias_frame, XSH_AFC_ATT, 
		      instrument,pre_overscan_corr,CPL_TRUE));

  check( afcatt_frame = cpl_frameset_get_frame( raws,0));
  check( xsh_frame_check_is_right_afcatt(afcatt_frame,instrument));

  if( subbias_frame != NULL) {
    /* subtract bias */
    check( afcatt_rmbias = xsh_subtract_bias( afcatt_frame, subbias_frame, 
                                              instrument,"AFC_ATT_",
                                              pre_overscan_corr,1));
  } 
  else {
    afcatt_rmbias =cpl_frame_duplicate( afcatt_frame);
  }
        
  if( subdark_frame != NULL) {
    /* subtract dark */
    sprintf( filename, "AFCATT_DARK_%s.fits", 
      xsh_instrument_arm_tostring( instrument));
    check( afcatt_rmdark = xsh_subtract_dark( afcatt_rmbias, subdark_frame,
					     filename, instrument));
  } 
  else {
    afcatt_rmdark =cpl_frame_duplicate( afcatt_rmbias);
  }
  check( afcthetab_frame = xsh_afcthetab_create( wave_tab_frame,
    model_config_frame, afc_order, spectralformat_frame,
    arclist_frame, afc_xmin, afc_ymin, 
                                                 instrument,1));

  /* detect arclines */
  check( xsh_detect_arclines( afcatt_rmdark, afcthetab_frame,
    arclist_frame, NULL, NULL,
    NULL, spectralformat_frame, NULL,
    &att_cleanlines, NULL, &att_resid_tab, XSH_SOLUTION_RELATIVE,
                              detect_arclines_p, 
                              detect_arclines_clipping, 
                              instrument,RECIPE_ID,1,0));

  /* compute the shift between CAl_RESID_TAB and ATT_RESID_TAB */
  check( afc_wave_tab_frame = xsh_flexcor( afcatt_frame, wave_tab_frame, 
                                           model_config_frame,
                                           order_tab_edges_frame, att_resid_tab,
                                           afc_xmin, afc_ymin, instrument, 
                                           &afc_order_tab_edges_frame, 
                                           &afc_config_tab_frame));
 
    if(model_config_frame == NULL) {


      if ( wave_tab_frame != NULL){
	check( xsh_create_poly_wavemap( NULL, wave_tab_frame, 
					afc_order_tab_edges_frame,
					spectralformat_frame, 
					dispsol_param, 
					instrument,
					"AFC_", 
					&afc_disp_tab_frame, NULL));

	tag = XSH_GET_TAG_FROM_ARM( XSH_DISP_TAB_AFC, instrument);
	check( cpl_frame_set_tag( afc_disp_tab_frame, tag));
      }

    } else {
      /* Suppressed to prevent seg faults */
      sprintf(slit_map_tag,"SLIT_MAP_%s",
	      xsh_instrument_arm_tostring( instrument )) ;
    
      sprintf(wave_map_tag,"WAVE_MAP_%s",
	      xsh_instrument_arm_tostring( instrument )) ;
      check(xsh_create_model_map(model_config_frame,instrument,
				 wave_map_tag,slit_map_tag,
				 &wave_map_frame, &slit_map_frame,1));

      check(afc_disp_tab_frame=xsh_create_dispersol_physmod(afcatt_rmdark,
							    afc_order_tab_edges_frame,
							    model_config_frame,
							    wave_map_frame,
							    slit_map_frame,
							    dispsol_param,
							    spectralformat_frame,
							    instrument,1));

    }
  
  /**************************************************************************/
  /* Products */
  /**************************************************************************/
  xsh_msg("Saving products");

  if ( afc_wave_tab_frame != NULL){
    check(xsh_add_product_table( afc_wave_tab_frame, frameset,
                                       parameters, RECIPE_ID, instrument,NULL));
  }
  if ( afc_config_tab_frame != NULL){
    check(xsh_add_product_table( afc_config_tab_frame, frameset,
                                       parameters, RECIPE_ID, instrument,NULL));
  }
 
  if ( afc_order_tab_edges_frame != NULL){
    check(xsh_add_product_table( afc_order_tab_edges_frame, frameset,
                                       parameters, RECIPE_ID, instrument,NULL));
  }
  if (afc_disp_tab_frame != NULL){
    check( xsh_add_product_table( afc_disp_tab_frame, frameset,
                                        parameters,RECIPE_ID,instrument,NULL));
  }

  xsh_msg("xsh_flexcomp success !!");

  cleanup:
    xsh_end( RECIPE_ID, frameset, parameters);

    XSH_FREE( detect_arclines_clipping);
    XSH_FREE( detect_arclines_p);
    XSH_FREE( dispsol_param);

    xsh_free_frameset( &raws);
    xsh_free_frameset( &calib);
    xsh_free_frame( &bpmap_frame);
    xsh_free_frame( &wave_map_frame);
    xsh_free_frame( &slit_map_frame);
    xsh_free_frame( &subbpmap_frame);
    xsh_free_frame( &subbias_frame);
    xsh_free_frame( &subdark_frame);
    xsh_free_frame( &afcatt_rmbias);
    xsh_free_frame( &afcatt_rmdark);
    xsh_free_frame( &att_resid_tab);
    xsh_free_frame( &att_cleanlines);
    xsh_free_frame( &afc_wave_tab_frame);
    xsh_free_frame( &afc_config_tab_frame);
    xsh_free_frame( &afc_order_tab_edges_frame);
    xsh_free_frame( &afc_disp_tab_frame);
    xsh_free_frame( &afcthetab_frame);
    xsh_instrument_free( &instrument);
    return;
}

/**@}*/

