/*                                                                            *
 *   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 F1ITNESS 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:15:45 $
 * $Revision: 1.151 $
*/

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup xsh_rectify  Rectify Image
 * @ingroup drl_functions
 *
 * Function ...
 */
/*----------------------------------------------------------------------------*/
/**@{*/

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

#include <math.h>
#include <xsh_drl.h>

#include <xsh_baryvel.h>
#include <xsh_badpixelmap.h>
#include <xsh_data_rec.h>
#include <xsh_data_localization.h>
#include <xsh_data_pre.h>
#include <xsh_data_order.h>
#include <xsh_data_wavesol.h>
#include <xsh_data_spectralformat.h>
#include <xsh_dfs.h>
#include <xsh_pfits.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_fit.h>
#include <xsh_model_io.h>
#include <xsh_model_kernel.h>
#include <xsh_ifu_defs.h>
#include <xsh_data_dispersol.h>
#include <cpl.h>
#include <xsh_utils.h>
#include <xsh_utils_ifu.h>
#include <xsh_utils_table.h>
#include <xsh_rectify.h>
#include <xsh_model_utils.h>

#define new_bp 1
/*#include <omp.h> */

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

#define SLIT_FRAC_STEP 50

static cpl_vector * rec_profile = NULL ;
static cpl_vector * err_profile = NULL ;

static double compute_shift_with_localization( cpl_frame *loc_frame,
                                          cpl_frame *loc0_frame);

static double compute_shift_with_kw( cpl_propertylist * header,
				     xsh_rectify_param *rectify_par,
                                     double **ref_ra, double **ref_dec, int flag);

static cpl_frame* 
xsh_shift( cpl_frame *rec_frame, xsh_instrument *instrument,
  const char *fname, double slit_shift, cpl_frame** res_frame_ext);

static void xsh_frame_set_shiftifu_ref( cpl_propertylist *header, 
  cpl_frame *shift_frame);
  
/*-----------------------------------------------------------------------------
  Implementation
  -----------------------------------------------------------------------------*/
/*
static void get_errors( float *err, int ix, int iy, int nx, int ny,
			  double radius, float *err_val, int* qual_val)
{
  int xmin, xmax, ymin, ymax ;
  int i, j, x, y, n ;
  double sum2 ;
  double *err_vect = NULL;
  int err_vect_size = 0;

  XSH_ASSURE_NOT_NULL( err);
  XSH_ASSURE_NOT_NULL( err_val);
  XSH_ASSURE_NOT_NULL( qual_val);

  // Get errors around ix, iy
  //   Sum squares
  //   Divide by N
  
  n = 0 ;
  sum2 = 0 ;
  xmin = ix - radius ;
  xmax = ix + radius ;
  ymin = iy - radius ;
  ymax = iy + radius ;

  
  err_vect_size = (2*radius+1)*(2*radius+1);

  XSH_CALLOC( err_vect, double, err_vect_size);

  for( y = ymin ; y < ymax ; y++ ){
    for( x = xmin ; x < xmax ; x++ ) {
      if ( y < 0 || y >= ny || x < 0 || x >= nx ) continue;
        err_vect[n] = err[x+y*nx];
        n++;
    }
  }

  for(i=0; i< n; i++){
    for(j=i; j< n; j++){
      if (i==j){
        sum2 += err_vect[i]*err_vect[j];
      }
      else{
        sum2 += 2*err_vect[i]*err_vect[j];
      }
    }
  }
 
  if (n != 0){
    *err_val = sqrt( sum2 )/(double)n ;
  }
  else{
    *err_val = 1;
    *qual_val = QFLAG_OUTSIDE_DATA_RANGE; 
  }     
  cleanup:
    XSH_FREE( err_vect);
}

*/
/*****************************************************************************/
static float
xsh_fill_gauss(const int j, const float A, const float sigma, const float bkg,
               const float y_c)
{

    /* add Gauss profile positioned where the trace is
     * I=A*exp( -0.5*( (X-Xo)/sigma )^2 ) +bkg
     */
    float gauss;
    float arg = 0;
    float y;

    /* uniform along X (horizontal direction) Gauss along Y (vertical)  */

    y=j;

    arg = (y - y_c) / sigma;
    arg *= arg;
    gauss = (A * expf(-0.5 * arg) + bkg);
    //xsh_msg("y=%g y_c=%g arg=%g gauss=%g",y,y_c,arg,gauss);
    return gauss;
}
/*****************************************************************************/
static void 
xsh_rec_list_rectify( xsh_rec_list *rec_list, int iorder, int irec, 
                      xsh_pre *sci_pre,cpl_image* var_img, 
                      double fx, double fy, double radius, cpl_vector *profile,
                      cpl_vector* profile2,
                      double mult)
{
  int nx,ny;
  float *rec_flux = NULL;
  float *rec_errs = NULL;
  int *rec_qual = NULL;
  int ix,iy;

  int xfirst=0;
  int xlast=0;

  int yfirst=0;
  int ylast=0;

  register int i=0;
  register int j=0;
  register int j_nx=0;

  XSH_ASSURE_NOT_NULL( rec_list);
  XSH_ASSURE_NOT_NULL( sci_pre);
  XSH_ASSURE_NOT_NULL( profile);

  check( rec_flux = xsh_rec_list_get_data1( rec_list, iorder));
  check( rec_errs = xsh_rec_list_get_errs1( rec_list, iorder));
  check( rec_qual = xsh_rec_list_get_qual1( rec_list, iorder));

  check( nx = xsh_pre_get_nx( sci_pre));
  check( ny = xsh_pre_get_ny( sci_pre));

  ix = floor( fx ) ;
  iy = floor( fy ) ;
  xfirst=ceil(fx-radius);
  xlast=floor(fx+radius);

  yfirst=ceil(fy-radius);
  ylast=floor(fy+radius);
  /* to prevent to go outside the image we do the following check */
  xfirst = (xfirst > 0) ? xfirst : 0;
  xlast  = (xlast < nx) ? xlast  : nx;
  yfirst = (yfirst > 0) ? yfirst : 0;
  ylast  = (ylast < ny) ? ylast  : ny;


  /* If outside detector */
  if ( ix <0 || ix >= nx || iy < 0 || iy >= ny ) {
    xsh_msg_dbg_high( "OUT_OF_RANGE at %d,%d", ix, iy);
    rec_flux[irec] = 0;
    rec_errs[irec] = 1;
    rec_qual[irec] = QFLAG_OUTSIDE_DATA_RANGE;
  }

  /* Otherwise if inside detector */
  else{
    cpl_image *sci_img = NULL;
    cpl_image *err_img = NULL;
    cpl_image *qua_img = NULL;
    float *data = NULL;
    float *errs = NULL;
    int *qual = NULL;

    check( sci_img =  xsh_pre_get_data( sci_pre));
    check( err_img =  xsh_pre_get_errs( sci_pre));
    check( qua_img =  xsh_pre_get_qual( sci_pre));

    check( data = cpl_image_get_data_float( sci_img));
    check( errs = cpl_image_get_data_float( err_img));
    check( qual = cpl_image_get_data_int( qua_img));

    int ix_iynx=ix+iy*nx;

    /* If the kernel radius is zero */
    if ( radius == 0.){
      rec_flux[irec] = data[ix_iynx];
      rec_errs[irec] = errs[ix_iynx];
      rec_qual[irec] = qual[ix_iynx];
    }

    /* Otherwise if kernel radius is greater than zero */
    else {

#if new_bp
      double flux_val=0, confidence=0 ;
      check( flux_val = cpl_image_get_interpolated( sci_img, fx, fy,
              profile, radius, profile, radius, &confidence));

        /* Now propagate errors */
        float err_val = 0;
        check( err_val = cpl_image_get_interpolated( var_img, fx, fy,
          profile2, radius, profile2, radius, &confidence));

        /* Now propagate flags */
        int qual_val = 0;
        for(j=yfirst;j<ylast-1;j++) {
            j_nx=j*nx;
            for(i=xfirst;i<xlast-1;i++) {
                qual_val |= qual[j_nx+i]; 
            }
        }

        /* Save the interpolated values */
        rec_flux[irec] = flux_val;
        rec_errs[irec] = sqrt(err_val);
        if (confidence < 1) {
            /* If error or undefined or pixel within kernel radius of edge */
            rec_qual[irec] = qual_val || QFLAG_MISSING_DATA;
        } else {
            rec_qual[irec] = qual_val;
        }

      }
#else
    double flux_val=0, confidence=0 ;
    check( flux_val = cpl_image_get_interpolated( sci_img, fx, fy,
            profile, radius, profile, radius, &confidence));
    /* If error or undefined or pixel within kernel radius of edge */
    if (confidence < 1) {
      //rec_flux[irec] = 0;
      rec_flux[irec] = flux_val;
      rec_errs[irec] = 1;
      rec_qual[irec] = QFLAG_MISSING_DATA;

    /* If flux interpolation ok */
    } else {

      /* Now propagate errors */
      float err_val = 0;
      check( err_val = cpl_image_get_interpolated( var_img, fx, fy,
        profile2, radius, profile2, radius, &confidence));

      /* Now propagate flags */
      int qual_val = 0;
      for(j=yfirst;j<ylast-1;j++) {
          j_nx=j*nx;
          for(i=xfirst;i<xlast-1;i++) {
              qual_val |= qual[j_nx+i]; 
          }
      }

      /* Save the interpolated values */
      rec_flux[irec] = flux_val;
      rec_errs[irec] = sqrt(err_val);
      rec_qual[irec] = qual_val;
    }
  }
#endif

    /* This is to perform flux conservation via the Jacobian */
    rec_flux[irec] *= mult;
    rec_errs[irec] *= mult;
  }

  cleanup: return;
}
/*****************************************************************************/

/*****************************************************************************/
/** 
 * @brief
 *   Fill the rectified structure for one order with the corresponding flux,
 *   using the wavelength solution.
 * 
 * @param[in] pre_sci Science Frame (in PRE format)
 * @param[in] rec_list Rectified structure pointer (to fill)
 * @param[in] idx Index in the rec_list
 * @param[in] wavesol Wave Solution structure pointer
 * @param[in] model_config model configuration structure
 * @param[in] instrument instrument arm structure
 * @param[in] disp_list dispersion structure
 * @param[in] slit_min min slit value
 * @param[in] slit_max max slit value
 * @param[in] lambda_min min lambda value
 * @param[in] skip_low amount of points to skip on low slice
 * @param[in] skip_up amount of points to skip on up slice
 * @param[in] rectify_par rectify parameter
 * @param[in] slit_shift shift nod position on slit
 */
/*****************************************************************************/

static inline void 
fill_rectified( xsh_pre *pre_sci,
		xsh_rec_list *rec_list, 
		int idx,
		xsh_wavesol *wavesol, 
		xsh_xs_3 *model_config, 
		xsh_instrument *instrument,
		xsh_dispersol_list *disp_list, 
		float slit_min, 
		float slit_max,
		double lambda_min,
		int skip_low, 
		int skip_up, 
		xsh_rectify_param *rectify_par, 
		double slit_shift,
                cpl_frame *slit_shiftab_frame)
{

  int ns, nl;
  int nslit, nlambda ;
  float * slit = NULL ;
  double * lambda = NULL ;
  //float * flux1 = NULL ;
  //float * errs1 = NULL ;
  //int * qual1 = NULL ;
  int order, last_slit, last_lambda;
  double *x_data = NULL;
  double *y_data = NULL;

  double *slit_shift_data = NULL;
  const char *slit_shifttab_name = NULL;
  cpl_table *shift_tab = NULL;
  int shift_size;
  double *slit_shifttab_data = NULL; 
  double *wave_shifttab_data = NULL;
  cpl_image* var_img=NULL;

  /* Check input parameters */
  XSH_ASSURE_NOT_NULL( pre_sci);
  XSH_ASSURE_NOT_NULL( rec_list);
  check( slit = xsh_rec_list_get_slit( rec_list, idx));
  XSH_ASSURE_NOT_NULL( slit);
  check( lambda = xsh_rec_list_get_lambda( rec_list, idx));
  XSH_ASSURE_NOT_NULL( lambda);
  //check( flux1 = xsh_rec_list_get_data1( rec_list, idx));
  //check( errs1 = xsh_rec_list_get_errs1( rec_list, idx));
  //check( qual1 = xsh_rec_list_get_qual1( rec_list, idx));

  var_img=cpl_image_duplicate(xsh_pre_get_errs( pre_sci));
  cpl_image_multiply(var_img,var_img);

  /* Create kernel profile (first time only ) */
  if ( rec_profile == NULL && rectify_par->rectif_radius != 0 ) {
    check( rec_profile = cpl_vector_new( CPL_KERNEL_DEF_SAMPLES));

    assure(rectify_par->rectif_radius > 0,CPL_ERROR_ILLEGAL_INPUT,
           "rectify-radius must be positive");
    check( cpl_vector_fill_kernel_profile( rec_profile,
      rectify_par->kernel_type, rectify_par->rectif_radius));
    err_profile=cpl_vector_duplicate(rec_profile);
    cpl_vector_multiply(err_profile,err_profile);
  }

  /* Get the order nb */
  check( order = xsh_rec_list_get_order( rec_list, idx));
  check( nslit = xsh_rec_list_get_nslit( rec_list, idx));
  check( nlambda = xsh_rec_list_get_nlambda( rec_list, idx));

  last_slit = nslit-1;
  last_lambda = nlambda-1;

  XSH_CALLOC( x_data, double,  nslit*nlambda);
  XSH_CALLOC( y_data, double,  nslit*nlambda);

  /* Fill slit and lambda arrays */
  for( ns = 0 ; ns < nslit ; ns++ ) {
    /* center the first pixel */
    /*rec_list->list[idx].slit[ns] = slit_min +
      ((float)ns*rectify_par->rectif_bin_space)+
      rectify_par->rectif_bin_space/2.0;*/
    rec_list->list[idx].slit[ns] = slit_min +
      (double)ns*rectify_par->rectif_bin_space;
  }

  for( nl = 0 ; nl < nlambda ; nl++ ) {
    /* center the first pixel */
    rec_list->list[idx].lambda[nl] = lambda_min +
      (double)nl*rectify_par->rectif_bin_lambda;
  }

  xsh_msg_dbg_low( "Order %d slit (%f,%f):%d lambda (%f,%f):%d",
    order, rec_list->list[idx].slit[0], 
    rec_list->list[idx].slit[last_slit], nslit, 
    rec_list->list[idx].lambda[0], 
    rec_list->list[idx].lambda[last_lambda], nlambda);

  /* Compute shift for wavelength */
  XSH_CALLOC( slit_shift_data, double, nlambda);
  if( slit_shiftab_frame != NULL){
    check( slit_shifttab_name = cpl_frame_get_filename( slit_shiftab_frame));
    xsh_msg_dbg_low("Load slit shift tab %s", slit_shifttab_name);
    XSH_TABLE_LOAD( shift_tab, slit_shifttab_name);
    check( wave_shifttab_data = cpl_table_get_data_double( shift_tab,
      XSH_SHIFTIFU_COLNAME_WAVELENGTH));
    check( slit_shifttab_data = cpl_table_get_data_double( shift_tab,
      XSH_SHIFTIFU_COLNAME_SHIFTSLIT));
    shift_size = cpl_table_get_nrow( shift_tab);

    for( nl = 0 ; nl < nlambda ; nl++ ) {
      double wave = lambda[nl];
      double cshift;
      cshift = xsh_data_interpolate( wave, shift_size, wave_shifttab_data, 
        slit_shifttab_data);
      slit_shift_data[nl] = cshift;
    }

    if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM) {
      FILE *shift_file = NULL;
      char shift_name[256];
      
      sprintf( shift_name, "shift_%d_%.1f_%.1f.dat", order, slit_min, slit_max);
      shift_file = fopen( shift_name, "w+");

      fprintf( shift_file, "# wave delta_s\n");
      
      for( nl = 0 ; nl < nlambda ; nl++ ) {
        fprintf ( shift_file, "%f %f\n", lambda[nl], slit_shift_data[nl]);
      }

      fclose( shift_file);
    }
  }

  /* GET X/Y positions */
  if ( wavesol != NULL){
    for( ns = skip_low ; ns < (nslit - skip_up) ; ns++ ) {
      double xslit = slit[ns]+slit_shift;

      for( nl = 0 ; nl < nlambda ; nl++ ) {
        int irec;
        double xlambda = lambda[nl];
        double xslitrec;

        irec= nl+ns*nlambda;
        xslitrec = xslit+slit_shift_data[nl];
        /* these functions make the conversion according to binning */
        check( x_data[irec] = xsh_wavesol_eval_polx( wavesol, xlambda, order,
          xslitrec));
        check( y_data[irec] = xsh_wavesol_eval_poly( wavesol, xlambda, order,
          xslitrec));
      }
    }
  }
  else{
    XSH_ASSURE_NOT_NULL( model_config);
    for( ns = skip_low ; ns < (nslit - skip_up) ; ns++ ) {
      double xslit = slit[ns]+slit_shift;
      int ns_nl=ns*nlambda;
      for( nl = 0 ; nl < nlambda ; nl++ ) {
        int irec;
        double pm_x=0, pm_y=0;
        double xlambda = lambda[nl];
        double xslitrec;

        irec= nl+ns_nl;
        xslitrec = xslit+slit_shift_data[nl];
        check( xsh_model_get_xy( model_config, instrument, xlambda, order, 
          xslitrec,
          &pm_x, &pm_y));
        x_data[irec] = convert_data_to_bin( pm_x, instrument->binx );
        y_data[irec] = convert_data_to_bin( pm_y, instrument->biny );
      }
    }
  }

  if ( xsh_debug_level_get() > XSH_DEBUG_LEVEL_MEDIUM) {
      FILE *rec_file = NULL;
      char recfile_name[256];

      sprintf( recfile_name, "rec_%d_%.1f_%.1f.dat", order, slit_min ,slit_max);
      rec_file = fopen( recfile_name, "w+");

      fprintf( rec_file, "#irec wave slit x y\n");

      for( ns = skip_low ; ns < (nslit - skip_up) ; ns++ ) {
        double xslit = slit[ns]+slit_shift;

        for( nl = 0 ; nl < nlambda ; nl++ ) {
          int irec;
          double xlambda = lambda[nl];
          double xslitrec;

          irec= nl+ns*nlambda;
          xslitrec = xslit+slit_shift_data[nl];
          fprintf ( rec_file, "%d %f %f %f %f\n", irec, xlambda, xslitrec, x_data[irec], 
            y_data[irec]);
      }
    }
    fclose( rec_file);
  }

  if (disp_list == NULL) {

    for( ns = skip_low ; ns < (nslit - skip_up) ; ns++ ) {
      int ns_nl=ns*nlambda;
      for( nl = 0 ; nl < nlambda ; nl++ ) {
        double fx=0, fy=0;
        int irec=0;

        irec= nl+ns_nl;
        fx = x_data[irec];
	      fy = y_data[irec];
 
        check( xsh_rec_list_rectify( rec_list, idx, irec, pre_sci,var_img,
				     fx, fy, rectify_par->rectif_radius,rec_profile,err_profile,1));
      }
 
    }
  }
  else{
    cpl_polynomial *plambdadx = NULL, *plambdady = NULL;
    cpl_polynomial *pslitdx = NULL, *pslitdy = NULL;
    cpl_vector *val = NULL;
    //int abs_order=0;

    check( plambdadx = cpl_polynomial_duplicate( 
      disp_list->list[idx].lambda_poly));
    check( plambdady = cpl_polynomial_duplicate(   
      disp_list->list[idx].lambda_poly));
    check( pslitdx = cpl_polynomial_duplicate( 
      disp_list->list[idx].slit_poly));
    check( pslitdy = cpl_polynomial_duplicate(        
      disp_list->list[idx].slit_poly));

    //abs_order = disp_list->list[idx].absorder;
 
    check( cpl_polynomial_derivative( plambdadx, 0));
    check( cpl_polynomial_derivative( plambdady, 1));
    check( cpl_polynomial_derivative( pslitdx, 0));
    check( cpl_polynomial_derivative( pslitdy, 1));

    check( val = cpl_vector_new(2));
    double fx, fy;
    double ldx, ldy, sdx, sdy, abs_determinant;
    int irec;
    int ns_nlambda=0;
    double bin_size=rectify_par->rectif_bin_space*rectify_par->rectif_bin_lambda;
    for( ns = skip_low ; ns < (nslit - skip_up) ; ns++ ) {
      ns_nlambda=ns*nlambda;
      for( nl = 0 ; nl < nlambda ; nl++ ) {

        irec= nl+ns_nlambda;
        fx = x_data[irec];
        fy = y_data[irec];

        check( cpl_vector_set( val, 0, fx));
        check( cpl_vector_set( val, 1, fy));

        check( ldx = cpl_polynomial_eval( plambdadx, val));
        check( ldy = cpl_polynomial_eval( plambdady, val));
        check( sdx = cpl_polynomial_eval( pslitdx, val));
        check( sdy = cpl_polynomial_eval( pslitdy, val));
        abs_determinant = bin_size/(fabs(ldx*sdy-sdx*ldy));

        check( xsh_rec_list_rectify( rec_list, idx, irec, pre_sci,var_img,
				     fx, fy,
				     rectify_par->rectif_radius,
				     rec_profile, err_profile,abs_determinant));
      }
    }
    xsh_free_polynomial( &plambdadx);
    xsh_free_polynomial( &plambdady);
    xsh_free_polynomial( &pslitdx);
    xsh_free_polynomial( &pslitdy);
    xsh_free_vector( &val);
  }

  cleanup:
  xsh_free_image(&var_img);
    xsh_free_vector( &rec_profile);
    xsh_free_vector( &err_profile);
    XSH_FREE( x_data);
    XSH_FREE( y_data);
    XSH_FREE( slit_shift_data);
    XSH_TABLE_FREE( shift_tab);
    return ;
}
/*****************************************************************************/

/*****************************************************************************/
/*****************************************************************************/
/**
  @brief Trace slit edges in a master flat
  @param[in] slitmap_frame slit map frame
  @param[in] sdown down SLIT trace estimate  
  @param[in] sup   upp  SLIT trace estimate  
  @param[in] sldown down IFU slice estimate  
  @param[in] slup down IFU slice estimate  
  @param[in] instrument instrument arm setting
 */
void 
xsh_get_slit_edges( cpl_frame *slitmap_frame, double *sdown, double *sup,
  double *sldown, double *slup, xsh_instrument *instrument)
{
  const char *slitmap_name = NULL;
  cpl_propertylist *slitmap_header = NULL;

  if ( slitmap_frame != NULL) {
    XSH_ASSURE_NOT_NULL( sdown);
    XSH_ASSURE_NOT_NULL( sup);

    check( slitmap_name = cpl_frame_get_filename( slitmap_frame));
    check( slitmap_header = cpl_propertylist_load( slitmap_name, 0));
    
    *sdown = xsh_pfits_get_slitmap_median_edglo( slitmap_header);
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_msg_warning( 
        "Keyword 'MEDIAN EDGLO SLIT' not found in SLIT_MAP %s. Using default value %f", 
        slitmap_name, MIN_SLIT);
      xsh_error_reset();
    }

    *sup = xsh_pfits_get_slitmap_median_edgup( slitmap_header);
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_msg_warning( 
        "Keyword 'MEDIAN EDGUP SLIT' not found in SLIT_MAP %s. Using default value %f",
        slitmap_name, MAX_SLIT);
      xsh_error_reset();
    }
    if ( xsh_instrument_get_mode(instrument) == XSH_MODE_IFU){
      XSH_ASSURE_NOT_NULL( sldown);
      XSH_ASSURE_NOT_NULL( slup);
      *sldown = xsh_pfits_get_slitmap_median_sliclo( slitmap_header);
      if ( cpl_error_get_code() != CPL_ERROR_NONE){
        xsh_msg_warning(
          "Keyword 'MEDIAN SLICLO SLIT' not found in SLIT_MAP %s. Using default value %f",
            slitmap_name, SLITLET_CEN_CENTER-2);
        xsh_error_reset();
      }
      *slup = xsh_pfits_get_slitmap_median_slicup( slitmap_header);
      if ( cpl_error_get_code() != CPL_ERROR_NONE){
        xsh_msg_warning(
          "Keyword 'MEDIAN SLICUP SLIT' not found in SLIT_MAP %s. Using default value %f",
            slitmap_name, SLITLET_CEN_CENTER+2);
        xsh_error_reset();
      }
    }
  }
  else {
    xsh_msg_warning( "No provided SLIT_MAP. Using default values: 'MEDIAN EDGLO SLIT' %f - 'MEDIAN EDGUP SLIT' %f",
      MIN_SLIT, MAX_SLIT);
    *sdown = MIN_SLIT;
    *sup = MAX_SLIT;
    if ( xsh_instrument_get_mode(instrument) == XSH_MODE_IFU){
      xsh_msg_warning( "Using default values: 'MEDIAN SLICLO SLIT' %f - 'MEDIAN SLICUP SLIT' %f",
        SLITLET_CEN_CENTER-2, SLITLET_CEN_CENTER+2);
      xsh_error_reset();
      XSH_ASSURE_NOT_NULL( sldown);
      XSH_ASSURE_NOT_NULL( slup);
      *sldown = SLITLET_CEN_CENTER-2;
      *slup = SLITLET_CEN_CENTER+2;
    }
  }

  if ( xsh_instrument_get_mode(instrument) == XSH_MODE_IFU){
    xsh_msg( "IFU limits: slitlet DOWN [%f %f], size: %f arcsec", 
      *sdown, *sldown, *sldown-*sdown);
    xsh_msg( "IFU limits: slitlet CEN  [%f %f], size: %f arcsec", 
      *sldown, *slup, *slup-*sldown);
    xsh_msg( "IFU limits: slitlet UP   [%f %f], size: %f arcsec",      
      *slup, *sup, *sup-*slup);
  }
  else{
    xsh_msg( "SLIT limits: [%f %f], total size: %f arcsec", *sdown, *sup, *sup-*sdown);
  }
  cleanup:
    xsh_free_propertylist( &slitmap_header);
    return ;
}
/*****************************************************************************/

/**
@brief rectify frame
@param[in]  rectify_par parameters structure to control rectification
@param[out] slit_min    min slit value
@param[out] nslit       number of slit sampling points
@param[in]  mode        mode to control if we are in slit mode
 */
void xsh_rec_slit_size( xsh_rectify_param *rectify_par, 
  double *slit_min, int *nslit, XSH_MODE mode)
{
  double smin= MIN_SLIT, smax=MAX_SLIT;
  double slit_step;

  XSH_ASSURE_NOT_NULL( rectify_par);
  XSH_ASSURE_NOT_NULL( slit_min);
  XSH_ASSURE_NOT_NULL( nslit);

  slit_step = rectify_par->rectif_bin_space;

  if ( mode == XSH_MODE_SLIT){
    if ( rectify_par->rectify_full_slit != 1 ) {
      xsh_msg_warning(" Option not READY go to FULL_SLIT");
    }
    *nslit = (smax - smin)/slit_step;
    *slit_min = smin;
    smax = smin+(*nslit-1)*slit_step;

    xsh_msg( "SLIT : (%.3f,%.3f) used only (%.3f,%.3f) in %d elts", 
      MIN_SLIT, MAX_SLIT, smin, smax, *nslit);
  }

  cleanup:
    return;
}
/*****************************************************************************/

static void 
adjust_lambdas( xsh_spectralformat_list * spec_list,
                xsh_rectify_param * rectify_par )
{
  int i ;
  double step = rectify_par->rectif_bin_lambda;

  xsh_msg_dbg_high( "Adjust Lambdas: %d orders", spec_list->size ) ;

  /* Adjust min/max according to lambda step */
  for ( i = 0 ; i<spec_list->size ; i++ ) {
    double min, max ;
    int imin, imax ;

    min = spec_list->list[i].lambda_min ;
    max = spec_list->list[i].lambda_max ;
    imin = ceil( min/step) ;
    min = (double)imin * step;
    imax = floor( max / step);
    max = (double)imax*step ;
    xsh_msg_dbg_high( "--- test lambdamin - %d : %.4lf -> %.4lf, %lf -> %lf", i,
		      spec_list->list[i].lambda_min, min,
		      spec_list->list[i].lambda_max, max ) ;
    spec_list->list[i].lambda_min = min;
    spec_list->list[i].lambda_max = max;
  }
}

/* AMO: function not used
static float 
get_step_slit( float * slit, int nslit )
{
  float result = 0. ;
  float min, max ;

  max = *(slit+nslit-1) ;
  min = *slit ;
  result = (max-min)/(float)nslit ;

  xsh_msg( "    Step Slit = %f", result ) ;
  return result ;
  
}
*/

/*****************************************************************************/
/** 
  @brief
    Create a grid in wavelength with the step in lambda and slit. Steps are
    defined in the parameters. Lambda min and max are taken from the
    spectralformat list. Slit min/max are hard coded.  
  @param sci_frame 
    The input Science frame after sky subtaraction
  @param orderlist_frame  
    The input Order Table Frame
  @param wavesol_frame  
    The input Wavesolution frame
  @param model_frame  
    The input model frame
  @param instrument  
    Pointer to instrument description structure
  @param rectify_par 
    Parameters for rectification
  @param spectralformat_frame 
    The frame for the spectral format
  @param disp_tab_frame
    The dispersion table frame (for flux conservation) 
  @param res_name
    The file name of the resampled frame
  @param res_frame_ext
    The resampled frame in ESO format 
  @param res_frame_tab
    A table associated to the resampled frame
  @param rec_prefix
    The recipe prefix (to build proper products PRO.CATG)

 * @return Rectified frame
 */
/*****************************************************************************/
cpl_frame * 
xsh_rectify( cpl_frame *sci_frame,
	     cpl_frame * orderlist_frame, 
	     cpl_frame *wavesol_frame,
	     cpl_frame * model_frame, 
	     xsh_instrument *instrument,
	     xsh_rectify_param *rectify_par,
	     cpl_frame *spectralformat_frame,
	     cpl_frame *disp_tab_frame, 
	     const char * res_name,
	     cpl_frame** res_frame_ext, 
	     cpl_frame** res_frame_tab,
	     const char* rec_prefix)
{
  cpl_frame *result = NULL;
  xsh_order_list *orderlist = NULL ;
  int nslit;
  double slit_min;
  char tag[256];

  XSH_ASSURE_NOT_NULL( orderlist_frame);

  check( orderlist = xsh_order_list_load ( orderlist_frame, instrument));

  sprintf(tag,"%s_%s",rec_prefix,
	  XSH_GET_TAG_FROM_ARM( XSH_ORDER2D, instrument));

  xsh_rec_slit_size( rectify_par, &slit_min, &nslit, XSH_MODE_SLIT);

  check( result = xsh_rectify_orders( sci_frame, orderlist, 
    wavesol_frame, model_frame, instrument, rectify_par, spectralformat_frame,
                                      disp_tab_frame, res_name, tag, 
                                      res_frame_ext, res_frame_tab,
                                      0, 100, slit_min, nslit,0, NULL));

  cleanup:
    xsh_order_list_free( &orderlist);
    return result;
}
/*****************************************************************************/

/** 
 * This function rectify AND shift the input frame. The shift is calculated
 * for each order and later used in the fill_rectify function. 
 * 
 * @param sci_frame Science Frame (PRE format)
 * @param orderlist_frame Order table
 * @param wavesol_frame Wave table (can be NULL)
 * @param model_frame Model Frame (can be NULL)
 * @param instrument Instrument structure
 * @param rectify_par Rectify parameters
 * @param spectralformat_frame Spectral format table frame
 * @param loc_frame Localization of this frame
 * @param loc0_frame Localization of the reference frame (first A frame)
 * @param throw_shift  shift in nod throw
 * @param disp_tab_frame Dispersion table
 * @param res_name Result frame name
 * @param res_frame_ext Ext result frame
 * @param res_frame_tab Ext result frame table
 * 
 * @return The rectified (and shifted) frame
 */
cpl_frame * xsh_rectify_and_shift( cpl_frame *sci_frame,
				   cpl_frame * orderlist_frame,
				   cpl_frame *wavesol_frame,
				   cpl_frame * model_frame,
				   xsh_instrument *instrument,
				   xsh_rectify_param *rectify_par,
				   cpl_frame *spectralformat_frame,
				   cpl_frame * loc_frame,
				   cpl_frame * loc0_frame,
                                   double *throw_shift,
				   cpl_frame *disp_tab_frame,
				   const char * res_name,
				   cpl_frame** res_frame_ext,
                                   cpl_frame** res_frame_tab)
{
  cpl_frame *result = NULL, *shift_rec = NULL;
  cpl_frame * shift_rec_ext = NULL;
  xsh_order_list *orderlist = NULL ;
  int nslit;
  double slit_min;
  //const char *filename = NULL;
  const char *tag = NULL;
  cpl_propertylist *header = NULL;
  double bin_space;
  double shift_arcsec=0, shift_pix=0, shift_bin_arcsec=0;
  double rectify_shift;
#if 0
  /* For test purpose */
  cpl_frame * result0 = NULL;
  char * res0_name = NULL ;
#endif
  xsh_msg( "===> xsh_rectify_and_shift" ) ;

  XSH_ASSURE_NOT_NULL( orderlist_frame);
  XSH_ASSURE_NOT_NULL( rectify_par);

  check( orderlist = xsh_order_list_load ( orderlist_frame, instrument));

  tag = XSH_GET_TAG_FROM_ARM( XSH_ORDER2D, instrument);
  xsh_rec_slit_size( rectify_par, &slit_min, &nslit, XSH_MODE_SLIT);
  bin_space = rectify_par->rectif_bin_space;

  if ( throw_shift == NULL) {
    if ( loc0_frame != NULL){
      XSH_ASSURE_NOT_NULL( loc_frame ) ;
      xsh_msg("Compute shift with localization");
      shift_arcsec = compute_shift_with_localization( loc0_frame, loc_frame);
    }
    else{
      xsh_msg("Fixed shift with localization to 0");
      shift_arcsec = 0;
    }
  }
  else {
    shift_arcsec = *throw_shift;
    xsh_msg("Use throw shift %f", shift_arcsec);
  }

  shift_pix = xsh_round_double(shift_arcsec/bin_space);
  shift_bin_arcsec = shift_pix*bin_space;
  rectify_shift =  shift_bin_arcsec-shift_arcsec;

  xsh_msg(" Mesured Shift for rectify : %f", rectify_shift);
 
  check( shift_rec = xsh_rectify_orders( sci_frame, orderlist, 
                                      wavesol_frame, model_frame, instrument,
                                      rectify_par, spectralformat_frame,
                                      disp_tab_frame, res_name, tag, 
                                      &shift_rec_ext, res_frame_tab, 
                                      0, 100, slit_min, nslit, rectify_shift,
                                      NULL));

  check( result = xsh_shift( shift_rec, instrument, res_name, shift_bin_arcsec,
    res_frame_ext));

  cleanup:
    xsh_free_frame( &shift_rec);
    xsh_free_frame( &shift_rec_ext);
    xsh_order_list_free( &orderlist);
    xsh_free_propertylist( &header);
    return result;
}
/*****************************************************************************/

/*****************************************************************************/
/*****************************************************************************/
cpl_frameset* xsh_rectify_ifu(cpl_frame *sci_frame,
  cpl_frame *orderlist_frame, cpl_frameset *wavesol_frameset,
  cpl_frameset *shiftifu_frameset,
  cpl_frame *model_frame, xsh_instrument *instrument,
  xsh_rectify_param *rectify_par, cpl_frame *spectralformat_frame,
  cpl_frame * slitmap_frame,
			      cpl_frameset** rec_frameset_ext,
			      cpl_frameset** rec_frameset_tab,
			      const char* rec_prefix )
{
  cpl_frameset *result = NULL;
  xsh_order_list *orderlist = NULL ;

  XSH_ASSURE_NOT_NULL( orderlist_frame);
  check( orderlist = xsh_order_list_load ( orderlist_frame, instrument));


  XSH_REGDEBUG("TODO : ADD disp_tab frameset, res_frame_ext frameset");

  check( result = xsh_rectify_orders_ifu( sci_frame, orderlist,
    wavesol_frameset, shiftifu_frameset, model_frame, instrument, rectify_par, 
    spectralformat_frame,
    slitmap_frame,rec_frameset_ext,rec_frameset_tab, 0, 100, rec_prefix));

  cleanup:
    xsh_order_list_free( &orderlist);
    return result;
}
/*****************************************************************************/

/*****************************************************************************/
/** 
  @brief
    Create a grid in wavelength with the step in lambda and slit. Steps are
    defined in the parameters. Lambda min and max are taken from the
    spectralformat list. Slit min/max are hard coded. Range orders arve given 
  @param sci_frame 
    The input Science frame after sky subtaraction
  @param orderlist 
    The input Order Table Frame
  @param wavesol_frame  
    The input Wavesolution frame
  @param model_frame  
    The input model frame
  @param instrument  
    Pointer to instrument description structure
  @param rectify_par 
    Parameters for rectification
  @param spectralformat_frame spectral format table 
  @param disp_tab_frame disp tab frame
  @param res_name resampled frame filename 
  @param tag resampled frame tag
  @param res_frame_ext resampled frame in ESO format
  @param res_frame_tab resampled frame in ESO format table
  @param min_index min index
  @param max_index max index 
  @param slit_min  slit min 
  @param nslit number of slit sampling points 
  @param slit_shift shift of nod position on slit

 * @return Rectified frame
 */
/*****************************************************************************/
/* TODO: clarify this function */
cpl_frame* 
xsh_rectify_orders( cpl_frame *sci_frame, 
		    xsh_order_list *orderlist,
		    cpl_frame *wavesol_frame,  
		    cpl_frame *model_frame, 
		    xsh_instrument *instrument,
		    xsh_rectify_param *rectify_par, 
		    cpl_frame *spectralformat_frame,
		    cpl_frame *disp_tab_frame, 
		    const char *res_name, 
		    const char* tag,
		    cpl_frame** res_frame_ext,
		    cpl_frame** res_frame_tab,
		    int min_index,
		    int max_index, 
		    double slit_min, 
		    int nslit, 
		    double slit_shift,
                    cpl_frame *slitshift_tab)
{
  xsh_rec_list * rec_list = NULL ;
  xsh_xs_3 config_model ;
  //xsh_xs_3* p_xs_3;
  int solution_type = 0;
  xsh_wavesol * wavesol = NULL ;
  cpl_frame * res_frame = NULL ;      
  xsh_pre * pre_sci = NULL ;
  int i, nlambda, order ;
  float slit_max ;
  xsh_spectralformat_list *spec_list = NULL ;
  char tag_drl[256];
  char res_name_drl[256];
  //cpl_mask* qual_mask = NULL;
  xsh_dispersol_list* disp_list = NULL;
  int rec_min_index, rec_max_index;
  const char* solution_type_name[2] = { "POLY", "MODEL"};
  int found_line=true;
  double barycor=0;
  double helicor=0;

  XSH_ASSURE_NOT_NULL( sci_frame);
  XSH_ASSURE_NOT_NULL( orderlist);
  XSH_ASSURE_NOT_NULL( spectralformat_frame);
  XSH_ASSURE_NOT_NULL( rectify_par);
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_ILLEGAL( min_index <= max_index);

  if (rectify_par->conserve_flux){
     assure( disp_tab_frame != NULL,CPL_ERROR_ILLEGAL_INPUT,
             "If -rectify-conserve-flux=TRUE "
             "you must provide an input dispersion table "
             "tagged as DISP_TAB_%s or DISP_TAB_AFC_%s .",
             xsh_instrument_arm_tostring(instrument),
             xsh_instrument_arm_tostring(instrument));
    check( disp_list = xsh_dispersol_list_load( disp_tab_frame, instrument));
  }

  check( pre_sci = xsh_pre_load( sci_frame, instrument));
  check( rec_list = xsh_rec_list_create( instrument));

  check( xsh_baryvel(pre_sci->data_header, &barycor,&helicor));
  cpl_propertylist_append_double(pre_sci->data_header,XSH_QC_VRAD_BARYCOR,barycor);
  cpl_propertylist_set_comment(pre_sci->data_header,XSH_QC_VRAD_BARYCOR,
                               XSH_QC_VRAD_BARYCOR_C);
  cpl_propertylist_append_double(pre_sci->data_header,XSH_QC_VRAD_HELICOR,helicor);
  cpl_propertylist_set_comment(pre_sci->data_header,XSH_QC_VRAD_HELICOR,
                               XSH_QC_VRAD_HELICOR_C);

  if ( model_frame != NULL) {
    solution_type = XSH_RECTIFY_TYPE_MODEL;
    check(xsh_model_temperature_update_frame(&model_frame,sci_frame,
                                             instrument,&found_line));
    check( xsh_model_config_load_best( model_frame, &config_model));
  }
  else if ( wavesol_frame != NULL ) {
    solution_type = XSH_RECTIFY_TYPE_POLY;
    check( wavesol = xsh_wavesol_load(  wavesol_frame, instrument));
  }
  else {
    xsh_error_msg(
      "Undefined solution type (POLY or MODEL). See your input sof");
  }

  xsh_msg("===== Solution type %s", solution_type_name[solution_type]);

  check( spec_list = xsh_spectralformat_list_load( spectralformat_frame,
    instrument));

  /* Parameters */
  xsh_msg_dbg_high( "Kernel: %s, Radius: %lf", rectify_par->rectif_kernel,
	   rectify_par->rectif_radius);
  xsh_msg_dbg_high( "Slit Binning: %f, Lambda Binning: %f",
	   rectify_par->rectif_bin_space,
	   rectify_par->rectif_bin_lambda);

  /* Populate list */
  adjust_lambdas( spec_list, rectify_par);

  /* Should make sure that we have the same nb of slits */
  rec_list->nslit = nslit;
  rec_list->slit_min = slit_min;
  slit_max = slit_min+rectify_par->rectif_bin_space*nslit;
  rec_list->slit_max = slit_max;

  if (min_index >= 0){
    rec_min_index = min_index;
  }
  else{
    rec_min_index = 0;
  }
  if ( max_index < rec_list->size){
    rec_max_index = max_index;
  }
  else{
    rec_max_index = rec_list->size-1;
  }

  /* AMO added to make sure bad pixels are flagged before re-sampling 

    //xsh_msg("ok1");
    //int decode_bp=2147483647;
    int decode_bp=instrument->decode_bp;
    cpl_mask* mask = xsh_qual_to_cpl_mask(pre_sci->qual,decode_bp);
    cpl_image_reject_from_mask(pre_sci->data,mask);
    //cpl_detector_interpolate_rejected(pre_sci->data);
    xsh_free_mask(&mask);
  */


  for( i = rec_min_index; i <= rec_max_index; i++ ) {
    double lambda_min, lambda_max ;
    double n;

    order = spec_list->list[i].absorder ;
    rec_list->list[i].nslit = nslit ;
    lambda_min = spec_list->list[i].lambda_min ;
    lambda_max = spec_list->list[i].lambda_max ;

    /* Calculate nlambda using lambda min/max and rectif_lambda-space */
    n = (lambda_max-lambda_min)/rectify_par->rectif_bin_lambda+1.0;
    nlambda = (int)xsh_round_double(n);
    assure(nlambda>0,CPL_ERROR_ILLEGAL_INPUT,
           "Number of wavelength sampling points is %d. Must be > 0. "
           "You may have to decrease rectify-bin-lambda param value.",
           nlambda);

    assure(nslit>0,CPL_ERROR_ILLEGAL_INPUT,
           "Number of spatial sampling points is %d. Must be > 0. "
           "You may have to decrease rectify-bin-slit param value.",
           nslit);

    rec_list->list[i].nlambda = nlambda ;


    check( xsh_rec_list_set_data_size( rec_list, i, order, nlambda, nslit));
    /*
      For each lambda/slit calculate X and Y and fill the grid
      with flux, errs, qual
      Needs wavesol
    */
    xsh_msg_dbg_medium( "Order %d, Nslit: %d, Nlambda: %d", order, nslit, nlambda ) ;
    xsh_msg_dbg_medium( "    Lambda (%f,%f) Slit(%f,%f)", 
      lambda_min, lambda_max , slit_min, slit_max);

    //check( qual_mask = xsh_pre_get_bpmap( pre_sci));
    /* in case of simple data re-sampling we do not need to declare bad pixels in the image data extension
     * because we simply re-map from one space to another
       check( cpl_image_reject_from_mask( pre_sci->data, qual_mask));
     */
    check( fill_rectified( pre_sci, rec_list, i, wavesol, 
			   &config_model, instrument, disp_list,
			   slit_min, slit_max, lambda_min, 0, 0, rectify_par, 
                           slit_shift, slitshift_tab));
  }

  rec_list->slit_min = rec_list->list[rec_min_index].slit[0];
  rec_list->slit_max = rec_list->list[rec_min_index].slit[nslit-1];

  xsh_msg_dbg_medium( "Saving Rectified Frame '%s'", res_name);

  sprintf( tag_drl ,"%s_DRL", tag);
  sprintf( res_name_drl ,"DRL_%s", res_name);

   
  check( xsh_rec_list_update_header( rec_list, pre_sci, rectify_par, tag_drl));
  
  if ( slitshift_tab != NULL){
    xsh_frame_set_shiftifu_ref( rec_list->header, slitshift_tab);
  }
  
  check( res_frame = xsh_rec_list_save( rec_list, res_name_drl, tag_drl, CPL_TRUE));
  xsh_add_temporary_file(res_name_drl);

  if ( res_frame_ext != NULL){

    check( *res_frame_ext = xsh_rec_list_save2(rec_list,res_name,tag));
    sprintf( tag_drl ,"%s_TAB", tag);
    sprintf( res_name_drl ,"TAB_%s", res_name); 
     check( *res_frame_tab = xsh_rec_list_save_table(rec_list,res_name_drl,tag_drl, 
                                                     CPL_TRUE));

     xsh_add_temporary_file(res_name_drl);
  }

  cleanup :  
  xsh_dispersol_list_free( &disp_list);
    xsh_rec_list_free( &rec_list);
    xsh_wavesol_free( &wavesol);
    xsh_spectralformat_list_free( &spec_list);
    xsh_pre_free( &pre_sci);
    return res_frame;
}
/*****************************************************************************/

static void xsh_get_shift_ref( cpl_frameset *set, 
  double *down, double *cen, double *up)
{
  cpl_frame *offsetdown_frame = NULL;
  cpl_frame *offsetcen_frame = NULL;
  cpl_frame *offsetup_frame = NULL;
  const char *offsetdown_name = NULL;
  const char *offsetcen_name = NULL;
  const char *offsetup_name = NULL;
  cpl_propertylist *offsetdown_list = NULL;
  cpl_propertylist *offsetcen_list = NULL;
  cpl_propertylist *offsetup_list = NULL;

  XSH_ASSURE_NOT_NULL( down);
  XSH_ASSURE_NOT_NULL( cen);
  XSH_ASSURE_NOT_NULL( up);

  check( offsetdown_frame = cpl_frameset_get_frame( set, 0));
  check( offsetcen_frame = cpl_frameset_get_frame( set, 1));
  check( offsetup_frame = cpl_frameset_get_frame( set, 2));
  check( offsetdown_name = cpl_frame_get_filename( offsetdown_frame));
  check( offsetcen_name = cpl_frame_get_filename( offsetcen_frame));
  check( offsetup_name = cpl_frame_get_filename( offsetup_frame));
  check( offsetdown_list = cpl_propertylist_load( offsetdown_name, 0));
  check( offsetcen_list = cpl_propertylist_load( offsetcen_name, 0));
  check( offsetup_list = cpl_propertylist_load( offsetup_name, 0));

  check( *down = xsh_pfits_get_shiftifu_slitref( offsetdown_list));
  check( *cen = xsh_pfits_get_shiftifu_slitref( offsetcen_list));
  check( *up = xsh_pfits_get_shiftifu_slitref( offsetup_list));
  
  cleanup:
    xsh_free_propertylist( &offsetdown_list);
    xsh_free_propertylist( &offsetcen_list);
    xsh_free_propertylist( &offsetup_list);
    return;
}
/*****************************************************************************/


/*****************************************************************************/
static void xsh_frame_set_shiftifu_ref( cpl_propertylist *header, 
  cpl_frame *shift_frame)
{
  cpl_propertylist *shift_header = NULL;
  const char *shift_name = NULL;  
  double waveref, slitref;
  
  XSH_ASSURE_NOT_NULL( header);
  XSH_ASSURE_NOT_NULL( shift_frame);
  
  check( shift_name = cpl_frame_get_filename( shift_frame));
  check( shift_header = cpl_propertylist_load( shift_name, 0));
  
  check( waveref = xsh_pfits_get_shiftifu_lambdaref( shift_header));
  check( slitref = xsh_pfits_get_shiftifu_slitref( shift_header));
  
  check( xsh_pfits_set_shiftifu_lambdaref( header, waveref));
  check( xsh_pfits_set_shiftifu_slitref( header, slitref));
    
  cleanup:
    xsh_free_propertylist( &shift_header);
    return;  
}
/*****************************************************************************/

/*****************************************************************************/
/** 
 * Computes with the OFFSET_TAB frameset the slitlet limits in order to build 
 *  the IFU cube
 * @param[in] shift_set The OFFSET_TAB frameset
 * @param[in] sdown Limit down of the slit
 * @param[in] sldown Limit down of the central slitlet
 * @param[in] slup   Limit up of then central slitlet
 * @param[in] sup   Limit up of the slit
 * @param[in] slit_bin the slit binning use in rectify process
 * @param[in,out] slitmin_tab The compute position of the lower limit of 
 *   each slitlet
 * @param[in,out] nslit_tab The number of bins of each slitlet
 * @param[in,out] slitcen_tab The center of each slitlet
 */ 
void xsh_compute_slitlet_limits( cpl_frameset *shift_set, double sdown, 
  double sldown, double slup, double sup, double slit_bin,
  double *slitmin_tab, int *nslit_tab, double *slitcen_tab)
{
  double offset_down=0, offset_cen=0, offset_up=0;
  double cen_slitlet_down_size;
  double cen_slitlet_up_size=0;
  double down_slitlet_down_size;
  double down_slitlet_up_size;
  double up_slitlet_down_size;
  double up_slitlet_up_size=0;
  double slitlet_up_size;
  double slitlet_down_size;
  double cen_ref_down, cen_ref_up;
  double cen_pixpos = 0.0;
  int ref_slit_size;
  int down_nslit;
  int up_nslit;

  XSH_ASSURE_NOT_NULL( shift_set);
  XSH_ASSURE_NOT_NULL( slitmin_tab);
  XSH_ASSURE_NOT_NULL( nslit_tab);  
  XSH_ASSURE_NOT_NULL( slitcen_tab);

  check( xsh_get_shift_ref( shift_set, &offset_down, &offset_cen,
    &offset_up));

  
  xsh_msg( "Shift reference: down %f arcsec, center %f arcsec, up %f arcsec", 
    offset_down, offset_cen, offset_up);

  slitcen_tab[0] = offset_down;
  slitcen_tab[1] = offset_cen;
  slitcen_tab[2]  =offset_up;

  down_slitlet_down_size = offset_down-sdown;
  down_slitlet_up_size = sldown-offset_down;

  xsh_msg_dbg_medium("In down slitlet [%f,%f] size lo %f up %f", sdown, sldown,
    down_slitlet_down_size, down_slitlet_up_size);

  cen_slitlet_down_size = offset_cen-sldown;
  cen_slitlet_up_size = slup-offset_cen;

  xsh_msg_dbg_medium("In cen slitlet [%f,%f] size lo %f up %f", sldown, slup,
    cen_slitlet_down_size, cen_slitlet_up_size);

  up_slitlet_down_size = offset_up-slup;
  up_slitlet_up_size = sup-offset_up;

  xsh_msg_dbg_medium("In up slitlet [%f,%f] size lo %f up %f", slup, sup,
    up_slitlet_down_size, up_slitlet_up_size);

  /* Slitlet down size : minimum between DOWN of center sliltet and UP of DOWN and up slitlets */
  if ( cen_slitlet_down_size < up_slitlet_up_size){
    slitlet_down_size = cen_slitlet_down_size;
  }
  else{
    slitlet_down_size = up_slitlet_up_size;
  }
  if ( slitlet_down_size > down_slitlet_up_size){
    slitlet_down_size = down_slitlet_up_size;
  }
  /* Slitlet up size : minimum between UP of center sliltet and DOWN of DOWN and up slitlets */
  if ( cen_slitlet_up_size < up_slitlet_down_size){
    slitlet_up_size = cen_slitlet_up_size;
  }
  else{
    slitlet_up_size = up_slitlet_down_size;
  }
  if ( slitlet_up_size > down_slitlet_down_size){
    slitlet_up_size = down_slitlet_down_size;
  }

  xsh_msg_dbg_medium( "Slitlet size DOWN %f UP %f", slitlet_down_size, slitlet_up_size);
  
  /* New version */
  cen_ref_down =  offset_cen-slitlet_down_size;
  cen_ref_up = offset_cen+slitlet_up_size;
  if (cen_ref_down > 0){
    down_nslit = ceil( cen_ref_down/slit_bin);
  }
  else{
    down_nslit = floor( cen_ref_down/slit_bin);
  }
  if ( cen_ref_up > 0){
    up_nslit = ceil( cen_ref_up/slit_bin);
  }
  else{
    up_nslit = floor( cen_ref_up/slit_bin);
  }

  xsh_msg( "Adjust central reference slitlet [%f %f] with bin %f arcsec: [%f %f]",
    cen_ref_down, cen_ref_up, slit_bin, down_nslit*slit_bin, up_nslit*slit_bin);

  cen_pixpos = (offset_cen-down_nslit*slit_bin)/slit_bin;
  ref_slit_size = up_nslit-down_nslit+1;
  xsh_msg( "Center position in pixel %f (Total %d pix)", cen_pixpos, ref_slit_size);
  slitmin_tab[1] = down_nslit*slit_bin;
  nslit_tab[1] = ref_slit_size;

  slitmin_tab[0] = offset_down-(ref_slit_size-1-cen_pixpos)*slit_bin;
  nslit_tab[0] = ref_slit_size;

  slitmin_tab[2] = offset_up-(ref_slit_size-1-cen_pixpos)*slit_bin;
  nslit_tab[2] = ref_slit_size;

  xsh_msg("Prepare the cube bin %f arcsec", slit_bin);
  xsh_msg("DOWN [%f, %f]", slitmin_tab[0], slitmin_tab[0]+nslit_tab[0]*slit_bin);
  xsh_msg("CEN  [%f, %f]", slitmin_tab[1], slitmin_tab[1]+nslit_tab[1]*slit_bin);
  xsh_msg("UP   [%f, %f]", slitmin_tab[2], slitmin_tab[2]+nslit_tab[2]*slit_bin);

  cleanup:
    return;
}
/*****************************************************************************/

/*****************************************************************************/
/** 
 * Create a grid in wavelength with the step in lambda and slit. Steps are
 * defined in the parameters. Lambda min and max are taken from the
 * spectralformat list. Slit min/max are hard coded.
 * 
 * @param[in] sci_frame Science frame after sky subtaraction
 * @param[in] orderlist  Order traces structure 
 * @param[in] wavesol_frameset  Input Wavesolution frameset (containing
                             the 3 slitlet wave solution: DOWN, CEN, UP)
 * @param[in] model_frame physical model cfg table frame
 * @param[in] instrument  Pointer to instrument description structure
 * @param[in] rectify_par Parameters for rectification
 * @param[in] spectralformat_frame The frame for the spectral format
 * @param[in] sliceoffset_frame frame with measured offsets of each IFU slices
 * @param[in] slitmap_frame The SLIT MAP Image Frame
 * @param[out] res_frameset_ext resampled frame in ESO format
 * @param[out] res_frameset_tab resampled frame in ESO format (tab)
 * @param[in] min_index min index
 * @param[in] max_index max index
 * @param[in] rec_prefix recipe prefix (for proper PRO.CATG values)
 * @return Rectified frame
 */
/*****************************************************************************/
cpl_frameset * 
xsh_rectify_orders_ifu(cpl_frame *sci_frame, 
                       xsh_order_list *orderlist, 
                       cpl_frameset *wavesol_frameset, 
                       cpl_frameset *shift_frameset,
                       cpl_frame *model_frame, 
                       xsh_instrument *instrument, 
                       xsh_rectify_param *rectify_par, 
                       cpl_frame *spectralformat_frame,
                       cpl_frame * slitmap_frame,
                       cpl_frameset ** res_frameset_ext,
                       cpl_frameset ** res_frameset_tab,
                       int min_index, 
                       int max_index, 
                       const char* rec_prefix)
{
  cpl_frameset *res_frameset = NULL ;

  cpl_frame *wavesolup_frame = NULL;
  cpl_frame *wavesolcen_frame = NULL;
  cpl_frame *wavesoldown_frame = NULL;
  cpl_frame *shiftup_frame = NULL;
  cpl_frame *shiftcen_frame = NULL;
  cpl_frame *shiftdown_frame = NULL;

  int slitlet=0;
  double sdown=0, sldown=0, slup=0, sup=0;
  //double slitlet_down_l=0;
  double slitlet_down_u=0;

  double slitlet_cen_l=0, slitlet_cen_u=0;
  double slitlet_cen_u_bin=0;
  //double slitlet_up_u=0;
  double down_offset=0.0;
  int nslit=0;
  double bin_space;
  //XSH_ARM arm ;
  int nslit_tab[3];
  double slitmin_tab[3];
  double slitcen_tab[3];
  int cut_edges = 0;

  XSH_ASSURE_NOT_NULL( sci_frame);
  XSH_ASSURE_NOT_NULL( orderlist);
  XSH_ASSURE_NOT_NULL( rectify_par);
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( spectralformat_frame);

  bin_space = rectify_par->rectif_bin_space;
  /*cut_edges = rectify_par->cut_edges;*/
  
  //arm = xsh_instrument_get_arm( instrument ) ;

  if ( wavesol_frameset != NULL){
    check( wavesoldown_frame = cpl_frameset_get_frame( wavesol_frameset, 0));
    check( wavesolcen_frame = cpl_frameset_get_frame( wavesol_frameset, 1));
    check( wavesolup_frame = cpl_frameset_get_frame( wavesol_frameset, 2));
  }

  if ( shift_frameset != NULL){
    check( shiftdown_frame = cpl_frameset_get_frame( shift_frameset, 0));
    check( shiftcen_frame = cpl_frameset_get_frame( shift_frameset, 1));
    check( shiftup_frame = cpl_frameset_get_frame( shift_frameset, 2));
  }

  /* Create result frameset */
  check( res_frameset = cpl_frameset_new());

  check( xsh_get_slit_edges( slitmap_frame, &sdown,
    &sup, &sldown, &slup, instrument));

  if ( shift_frameset != NULL){
    check( xsh_compute_slitlet_limits( shift_frameset, sdown,
      sldown, slup, sup, bin_space, 
      slitmin_tab, nslit_tab, slitcen_tab));
  }
  else{
    slitlet_cen_l = sldown;
    slitlet_cen_u = slup;

    //slitlet_down_l = sdown;
    slitlet_down_u = slitlet_cen_l;
    //slitlet_up_u = sup;

    /* center */
    slitmin_tab[1] = slitlet_cen_l;
    nslit_tab[1] = ceil((slitlet_cen_u-slitlet_cen_l)/rectify_par->rectif_bin_space);
    slitlet_cen_u_bin = slitmin_tab[1]+nslit_tab[1]*rectify_par->rectif_bin_space;

    /* down */
    nslit_tab[0] = nslit_tab[1];
    slitmin_tab[0] = slitlet_down_u-down_offset-
        nslit_tab[0]*rectify_par->rectif_bin_space;

    /* up */
    slitmin_tab[2] = slitlet_cen_u_bin;
    nslit_tab[2] = nslit_tab[1];
  }

  for ( slitlet = LOWER_IFU_SLITLET ; slitlet <= UPPER_IFU_SLITLET ;
	slitlet++ ) {

    cpl_frame *wavesol_frame = NULL;
    cpl_frame *shift_frame = NULL;
    cpl_frame *res_frame = NULL;
    cpl_frame *resext_frame = NULL;
    cpl_frame *restab_frame = NULL;
    double slitlet_slit_min = 0.0;
    char tag[256];
    char res_name[256];
    double rec_shift=0.0;
    double rec_min=0.0;
    const char *slitlet_name = NULL;

    switch( slitlet ) {
      case LOWER_IFU_SLITLET:

      slitlet_name = "DOWN";      
      nslit = nslit_tab[0];
      slitlet_slit_min = slitmin_tab[0];
      wavesol_frame = wavesoldown_frame;
      shift_frame  = shiftdown_frame;
      sprintf(tag,"%s_%s",rec_prefix,
	      XSH_GET_TAG_FROM_ARM(XSH_ORDER2D_DOWN_IFU,instrument));
      sprintf(res_name,"%s.fits",tag);
      break ;
     
      case CENTER_IFU_SLITLET:
      slitlet_name = "CEN";
      slitlet_slit_min = slitmin_tab[1];
      nslit = nslit_tab[1];
      wavesol_frame = wavesolcen_frame;
      shift_frame  = shiftcen_frame;
      sprintf(tag,"%s_%s",rec_prefix,
	      XSH_GET_TAG_FROM_ARM(XSH_ORDER2D_CEN_IFU,instrument));
      sprintf(res_name,"%s.fits",tag);
      break ;
    
      case UPPER_IFU_SLITLET:
      slitlet_name = "UP";
      slitlet_slit_min = slitmin_tab[2];
      nslit = nslit_tab[2];

      wavesol_frame = wavesolup_frame;
       shift_frame  = shiftup_frame;
      sprintf(tag,"%s_%s",rec_prefix,
	      XSH_GET_TAG_FROM_ARM(XSH_ORDER2D_UP_IFU,instrument));
      sprintf(res_name,"%s.fits",tag);
      break ;
    }
    /* compute rec_shift */
    rec_min = slitlet_slit_min;
    rec_shift = 0;

    xsh_msg("Cut edges with %d pix", cut_edges);
    rec_min += cut_edges*rectify_par->rectif_bin_space;
    nslit -= cut_edges*2;
     
    XSH_CMP_INT( nslit, >, 0, "Check size in slit",);
    
    
    xsh_msg( "%s [%f %f] in %d pix of %f arcsec" ,slitlet_name,
      slitlet_slit_min,
      slitlet_slit_min+nslit*rectify_par->rectif_bin_space,
      nslit, rectify_par->rectif_bin_space);

    check( res_frame = xsh_rectify_orders( sci_frame, orderlist,
      wavesol_frame, model_frame, instrument, rectify_par,
      spectralformat_frame, NULL, res_name, tag, &resext_frame, &restab_frame,
      min_index, max_index, rec_min, nslit, rec_shift,shift_frame));

    check( cpl_frameset_insert( res_frameset, res_frame));

    if(res_frameset_ext !=NULL && res_frameset_tab !=NULL) {
    check( cpl_frameset_insert(*res_frameset_ext, resext_frame));
    check( cpl_frameset_insert(*res_frameset_tab, restab_frame));
    }
  }

  cleanup:
    return res_frameset ;
}
/*****************************************************************************/


/*****************************************************************************/
/** 
 * This function creates a structure containing for each order the shift
 * to be applied.
 * 
 * @param loc_frame Localization of the current rectified frame
 * @param loc0_frame Localization of the reference frame (first A frame)
 */
/*****************************************************************************/
static double 
compute_shift_with_localization( cpl_frame *loc_frame,
				 cpl_frame *loc0_frame)
{
  double shift_a=0, shift_b=0, shift=0;
  xsh_localization *loc = NULL, *loc0 = NULL;

  XSH_ASSURE_NOT_NULL( loc_frame);
  XSH_ASSURE_NOT_NULL( loc0_frame);

  check( loc = xsh_localization_load( loc_frame));
  check( loc0 = xsh_localization_load( loc0_frame));

  /* We assume here that the degree of the localization polynomial is 0 */
  check( shift_a = cpl_polynomial_eval_1d( loc0->cenpoly, 0., NULL));
  check( shift_b = cpl_polynomial_eval_1d( loc->cenpoly, 0., NULL));

  xsh_msg_dbg_medium("Shift A %f B %f", shift_a, shift_b)
  shift = shift_b-shift_a;

  xsh_msg( "Shift (localization) = %lf",  shift);

  cleanup:
    xsh_localization_free( &loc);
    xsh_localization_free( &loc0);
    return shift;
}
/*****************************************************************************/
/* AMO: function not used. For the moment commented out
static cpl_frame * 
shift_with_localization( cpl_frame * rec_frame,
			 cpl_frame * loc_frame,
			 cpl_frame * loc0_frame,
			 xsh_instrument * instrument,
			 const char * fname,
			 cpl_frame** res_frame_ext )
{
  cpl_frame * result = NULL ; //< The result frame (the
			      //shifted B-A rectified image)
  xsh_rec_list * shifted_rec_list = NULL ; ///< The result list. local 
  xsh_rec_list * rec_list = NULL;
  xsh_localization *loc_list = NULL;
  xsh_localization *loc0_list = NULL;
  float step_slit ;			//< A-B object position ( ... ) 
  int nb_orders ;		        //< Nb of orders in B-A 
  int order ;
  int absorder, nlambda, nslit ;
  float * flux1 = NULL ;
  float * errs1 = NULL ;
  int * qual1 = NULL ;
  float * shifted_flux1 = NULL ;
  float * shifted_errs1 = NULL ;
  int * shifted_qual1 = NULL ;
  float * slit = NULL ;
  double * lambda = NULL ;
  float * shifted_slit = NULL ;
  double * shifted_lambda = NULL ;
  const char* tag=NULL;
  const char* tag_drl=NULL;
  const char* fname_drl=NULL;

  XSH_ASSURE_NOT_NULL( rec_frame ) ;
  XSH_ASSURE_NOT_NULL( loc_frame ) ;
  XSH_ASSURE_NOT_NULL( loc0_frame ) ;
  XSH_ASSURE_NOT_NULL( instrument ) ;

  xsh_msg_dbg_low( "Entering shift_with_localization" ) ;

  // Load needed frames into lists 
  check( rec_list = xsh_rec_list_load( rec_frame, instrument));
  check( loc_list = xsh_localization_load( loc_frame));
  check( loc0_list = xsh_localization_load( loc0_frame));
  // Create shifted rectified list 
  check( shifted_rec_list = xsh_rec_list_duplicate( rec_list, instrument ) ) ;
  xsh_msg( "Nb of orders in new rec list: %d", shifted_rec_list->size ) ;
  // Get the nb of orders 
  check( nb_orders = rec_list->size ) ;

  // Loop over orders 
  for( order = 0 ; order < nb_orders ; order++ ) {
    int ns, nl ;
    int nshift, max_size ;

    check( absorder = xsh_rec_list_get_order( rec_list, order ) ) ;
    check( nslit = xsh_rec_list_get_nslit( rec_list, order ) ) ;
    check( nlambda = xsh_rec_list_get_nlambda( rec_list, order ) ) ;
    xsh_msg( " Absolute order: %d, Nslit: %d, Nlambda: %d", absorder,
	     nslit, nlambda ) ;
    max_size = nslit*nlambda ;

    // Prepare shifting 
    check( flux1 = xsh_rec_list_get_data1( rec_list, order ) ) ;
    check( qual1 = xsh_rec_list_get_qual1( rec_list, order ) ) ;
    check( errs1 = xsh_rec_list_get_errs1( rec_list, order ) ) ;
    check( slit = xsh_rec_list_get_slit( rec_list, order ) ) ;
    check( lambda = xsh_rec_list_get_lambda( rec_list, order ) ) ;

    check( shifted_slit = xsh_rec_list_get_slit( shifted_rec_list,
						 order ) ) ;
    check( shifted_lambda = xsh_rec_list_get_lambda( shifted_rec_list,
						     order ) ) ;
    check( shifted_flux1 = xsh_rec_list_get_data1( shifted_rec_list,
						   order ) ) ;
    check( shifted_qual1 = xsh_rec_list_get_qual1( shifted_rec_list,
						   order ) ) ;
    check( shifted_errs1 = xsh_rec_list_get_errs1( shifted_rec_list,
						   order ) ) ;
    // Set shifted list slits and lambda 
    for( ns = 0 ; ns<nslit ; ns++ )
      *(shifted_slit+ns) = *(slit+ns) ;
    for( nl = 0 ; nl<nlambda ; nl++ )
      *(shifted_lambda+nl) = *(lambda+nl) ;

    // Calculate nshift from BA_pos, AB_pos and nslit 
    step_slit = get_step_slit( slit, nslit ) ;

    // Now fill the rectified shifted image (data, errs and qual) 
    // Initialize qual with "QFLAG_OUT_OF_NOD" (TBV) 
    for( ns = 0 ; ns < nslit ; ns++ )
      for( nl = 0 ; nl < nlambda ; nl++ )
	*(shifted_qual1+nl+ns*nlambda) = QFLAG_OUT_OF_NOD ;

    for( ns = 0 ; ns < nslit ; ns++ ) {
      int shifted_idx =0;

      for( nl = 0 ; nl<nlambda ; nl++ ) {
	double ab_slit, ba_slit ;

	ab_slit = cpl_polynomial_eval_1d( loc0_list->cenpoly,
					  *(lambda+nl), NULL ) ;
	ba_slit = cpl_polynomial_eval_1d( loc_list->cenpoly,
					  *(lambda+nl), NULL ) ;
	nshift = (float)(ab_slit-ba_slit)/step_slit ;
	xsh_msg_dbg_medium( "  nl: %d, ns: %d, nshift: %d, ab: %lf, ba: %lf",
		 nl, ns, nshift, ab_slit, ba_slit ) ;

	shifted_idx = nl+(ns+nshift)*nlambda ;

	if ( shifted_idx < 0 ||  shifted_idx > max_size ) {
	  xsh_msg_dbg_high( "  Out of Bound: nl=%d, ns=%d, shifted=%d, max=%d", nl, ns,
		   shifted_idx, max_size ) ;
	  break ;
	}
	*(shifted_flux1+nl+(ns+nshift)*nlambda) =
	  *(flux1+nl+ns*nlambda) ;
	*(shifted_errs1+nl+(ns+nshift)*nlambda) =
	  *(errs1+nl+ns*nlambda) ;
	*(shifted_qual1+nl+(ns+nshift)*nlambda) =
	  *(qual1+nl+ns*nlambda) ;
      }
      if ( shifted_idx > max_size ) break ;
    }
  }

  // Save shifted frame 
  tag =  xsh_stringcat_any( cpl_frame_get_tag( rec_frame ), "", (void*)NULL );
  tag_drl = xsh_stringcat_any( cpl_frame_get_tag( rec_frame ), "_DRL",
                               (void*)NULL ) ;
  fname_drl = xsh_stringcat_any( tag_drl, ".fits", (void*)NULL ) ;
  check(*res_frame_ext=xsh_rec_list_save2(shifted_rec_list,fname,tag)) ;


  check( result =xsh_rec_list_save( shifted_rec_list, fname_drl, 
  tag_drl, CPL_TRUE));

  xsh_add_temporary_file(fname_drl);

  xsh_msg( "    Shifted Frame SAVED [%s]", cpl_frame_get_tag( result ) ) ;

 cleanup:
  xsh_localization_free( &loc_list ) ;
  xsh_localization_free( &loc0_list ) ;
  xsh_rec_list_free( &rec_list ) ;
  xsh_rec_list_free( &shifted_rec_list ) ;

  return result ;
}
*/

static double 
compute_shift_with_kw( cpl_propertylist *header,
		       xsh_rectify_param *rectify_par,
		       double **ref_ra, 
		       double **ref_dec, 
		       int flag)
{
  double ref_ra_cumoff, ref_dec_cumoff;
  double ra_cumoff, dec_cumoff;
  double ra_reloff, dec_reloff, ra_off, dec_off;
  double posang=0; 
  /* double bin_space=0; */
  double shift_arcsec=0;



  xsh_msg_dbg_high( "==> compute_shift_with_kw" ) ;
  XSH_ASSURE_NOT_NULL( header);
  XSH_ASSURE_NOT_NULL( rectify_par);

  check( posang = xsh_pfits_get_posang( header));
  posang = posang/180.0* M_PI;

  if ( *ref_ra != NULL){
    ref_ra_cumoff = **ref_ra;
  }
  else{
    check( ref_ra_cumoff = xsh_pfits_get_ra_cumoffset( header));
    XSH_MALLOC( *ref_ra, double, 1);
    **ref_ra = ref_ra_cumoff;
  }
  if ( *ref_dec != NULL){
    ref_dec_cumoff = **ref_dec;
  }
  else{
    check( ref_dec_cumoff = xsh_pfits_get_dec_cumoffset( header));
    XSH_MALLOC( *ref_dec, double, 1);
    **ref_dec = ref_dec_cumoff;
  }

  if ( flag == 0){
    check( ra_cumoff = xsh_pfits_get_ra_cumoffset( header));
    check( dec_cumoff = xsh_pfits_get_dec_cumoffset( header));
  }
  else{
    check( ra_cumoff = xsh_pfits_get_b_ra_cumoffset( header));
    check( dec_cumoff = xsh_pfits_get_b_dec_cumoffset( header));
  }

  check( ra_reloff = xsh_pfits_get_b_ra_reloffset( header));
  check( dec_reloff = xsh_pfits_get_b_dec_reloffset( header));

  /* compute the move in slit */
  xsh_msg( "POSANG %f (rad) REF CUM_(RA DEC) : %f %f ", posang, ref_ra_cumoff,
    ref_dec_cumoff);
  xsh_msg( "OBJ CUM_(RA DEC) : %f %f ", ra_cumoff, dec_cumoff);
  xsh_msg( "REL_(RA DEC) :  %f %f", ra_reloff, dec_reloff);

  ra_off = ra_cumoff-ref_ra_cumoff;
  dec_off = dec_cumoff-ref_dec_cumoff;

  xsh_msg( "COMPUTE OFF_(RA DEC) : %f %f", ra_off, dec_off);

  shift_arcsec = cos(-posang)*dec_off+sin(-posang)*ra_off;

  xsh_msg( "COMPUTE shift in arcsec %f :", shift_arcsec);

  cleanup:
    return shift_arcsec;
}

/*****************************************************************************/
/*****************************************************************************/
/** 
 * @brief This function creates a structure containing for each order the shift
 * to be applied.
 * 
 * @param[in] rec_frame Science Frame. The kwywords are taken from the header.
 * @param[in] instrument Instrument structure
 * @param[in] rectify_par Rectification parameters
 * @param[in] fname filename
 * @param[out] res_frame_ext resampled frame in ESO format
 * @param[out] ref_ra RA of reference frame
 * @param[out] ref_dec DEG of reference frame
 * @param[in] flag flag
 * @return resampled frame in DRL frame
 */
cpl_frame* 
shift_with_kw( cpl_frame *rec_frame, 
	       xsh_instrument *instrument,
	       xsh_rectify_param *rectify_par, 
	       const char *fname,
	       cpl_frame** res_frame_ext, 
	       double **ref_ra, 
	       double **ref_dec, 
	       int flag)
{
  cpl_frame *result = NULL ;
  int shift_pix;
  double bin_space, shift_arcsec;
  const char *filename = NULL;
  cpl_propertylist* header = NULL;


  XSH_ASSURE_NOT_NULL( rec_frame);
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( fname);
  XSH_ASSURE_NOT_NULL( res_frame_ext);
  XSH_ASSURE_NOT_NULL( ref_ra);
  XSH_ASSURE_NOT_NULL( ref_dec);

  check( filename = cpl_frame_get_filename( rec_frame));
  check( header = cpl_propertylist_load( filename, 0));
 
  check( bin_space = xsh_pfits_get_rectify_bin_space( header));
 
  check( shift_arcsec = compute_shift_with_kw( header, 
    rectify_par, ref_ra, ref_dec, flag));

  /* here we compute integer shifts not to resample twice in fast mode */
  shift_pix = xsh_round_double(shift_arcsec/bin_space);

  shift_arcsec = shift_pix*bin_space;

  xsh_msg( "SHIFT A-->B : %f in arcsec", shift_arcsec);

  check( result =  xsh_shift( rec_frame, instrument, fname, shift_arcsec, 
    res_frame_ext));

  cleanup:
    if ( cpl_error_get_code() !=CPL_ERROR_NONE){
      xsh_free_frame( &result);
    }
    xsh_free_propertylist( &header);
    return result ;
}
/*****************************************************************************/

/*****************************************************************************/
/*****************************************************************************/
static cpl_frame* 
xsh_shift( cpl_frame *rec_frame, 
           xsh_instrument *instrument,
           const char *fname, 
           double slit_shift, 
           cpl_frame** res_frame_ext)
{
  xsh_rec_list *rec_list = NULL ;
  cpl_frame *result = NULL ;
  int nb_orders, order = 0 ;
  float *slit = NULL ;
  //int shift_pix;
  char* fname_drl=NULL;
  char* tag_drl=NULL;
  char* tag=NULL;
  int nslit=0;

  XSH_ASSURE_NOT_NULL( rec_frame);
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( fname);
  XSH_ASSURE_NOT_NULL( res_frame_ext);

  check( rec_list = xsh_rec_list_load( rec_frame, instrument));

  nb_orders = rec_list->size ;
  check( nslit = xsh_rec_list_get_nslit( rec_list, 0));

  /* Loop over orders */
  for( order = 0 ; order < nb_orders ; order++ ) {
    int ns;

    check( slit = xsh_rec_list_get_slit( rec_list, order));
    for( ns = 0 ; ns < nslit ; ns++ ) {
      slit[ns]+=slit_shift;
    }
  }
  rec_list->slit_min = slit[0];
  rec_list->slit_max = slit[nslit-1];
 
  check( xsh_pfits_set_rectify_space_min( rec_list->header,
    rec_list->slit_min));
  check( xsh_pfits_set_rectify_space_max( rec_list->header,
    rec_list->slit_max));
 
  /* Save shifted frame */
  tag= xsh_stringcat_any(  cpl_frame_get_tag( rec_frame ), "", (void*)NULL ) ;
  tag_drl = xsh_stringcat_any(  cpl_frame_get_tag( rec_frame ), "_DRL",
                                (void*)NULL ) ;
  fname_drl = xsh_stringcat_any( "DRL_", fname, (void*)NULL);

  check( *res_frame_ext=xsh_rec_list_save2( rec_list, fname,tag)) ;
  check( result = xsh_rec_list_save( rec_list, fname_drl,
    tag_drl, CPL_TRUE));
  xsh_add_temporary_file(fname_drl);

  cleanup:
    if ( cpl_error_get_code() !=CPL_ERROR_NONE){
      xsh_free_frame( &result);
    }
    XSH_FREE( fname_drl);
    XSH_FREE( tag_drl);
    XSH_FREE( tag);
    xsh_rec_list_free( &rec_list);
    return result ;
}

/** 
 * This function is used to shift a rectified frame to the position
 * of the first frame (nodding observation). The shift is calculated either
 * by using the localization table of the current and the first frame, or
 * by using the NODTHROW, RELOFFSET and CUMOFFSET Keywords available in the
 * header of the rectified frame.
 * 
 * @param[in] rec_frame Input rectified frame to be shifted
 * @param[in] loc_frame Localization frame used (or not) for shifting
 * @param[in] loc0_frame Localization of reference frame used (or not) for shifting
 * @param[in] combine_nod_param Parameters
 * @param[in] rectif_par rectify parameters
 * @param[in] instrument Instrument structure
 * @param[out] res_frame_ext resampled frame in ESO format
 * @return The shifted frame
 */
cpl_frame * xsh_shift_rectified( cpl_frame * rec_frame,
				 cpl_frame * loc_frame,
				 cpl_frame * loc0_frame,
				 const char * file_name,
				 xsh_combine_nod_param * combine_nod_param,
				 xsh_rectify_param * rectif_par,
				 xsh_instrument * instrument,
				 cpl_frame** res_frame_ext )
{
  cpl_frame * result = NULL ;
  double *ra = NULL, *dec = NULL;

  xsh_msg( "===> xsh_shift_rectified" ) ;

  XSH_ASSURE_NOT_NULL( combine_nod_param ) ;
  XSH_ASSURE_NOT_NULL( rec_frame ) ;

  check( result = shift_with_kw( rec_frame, instrument,
				   rectif_par, file_name, res_frame_ext, &ra, &dec, 1));

 cleanup:
  return result ;
}
/*----------------------------------------------------------------------------*/

/**@}*/
