/*                                                                            *
 *   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 16:08:02 $
 * $Revision: 1.169 $
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#define REGDEBUG_WAVECAL 0
/*----------------------------------------------------------------------------*/
/**
 * @defgroup drl_functions    DRL Functions
 *
 * This modules contains main DRL functions
 */
/**
 * @defgroup xsh_follow_arclines    xsh_follow_arclines
 * @ingroup drl_functions
 *
 * Functions used to detect and follow arc lines, compute center, width and
 * tilt parameters.
 */
/*----------------------------------------------------------------------------*/
/**@{*/

/*-----------------------------------------------------------------------------
  Includes
  -----------------------------------------------------------------------------*/
#include <math.h>
#include <xsh_drl.h>
#include <xsh_data_pre.h>
#include <xsh_dfs.h>
#include <xsh_pfits.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_utils_wrappers.h>
#include <xsh_badpixelmap.h>
#include <xsh_data_order.h>
#include <xsh_data_the_map.h>
#include <xsh_data_arclist.h>
#include <xsh_data_wavesol.h>
#include <xsh_data_wavemap.h>
#include <xsh_data_resid_tab.h>
#include <xsh_data_linetilt.h>
#include <xsh_utils.h>
#include <xsh_ifu_defs.h>
#include <xsh_model_io.h>
#include <xsh_model_kernel.h>
#include <xsh_model_utils.h>
#include <xsh_data_spectralformat.h>
#include <xsh_data_shift_tab.h>
#include <xsh_data_dispersol.h>

#define XSH_SPECRES_CLIP_KAPPA 3.
#define XSH_SPECRES_CLIP_NITER 2
#define XSH_SPECRES_CLIP_FRAC 0.5

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

//static const char* fwhm_debug_mode = NULL;

enum {
  LAMBDA_FOUND, LAMBDA_TOO_SMALL, LAMBDA_NOT_FOUND
} ;

enum {
  FIND_TILT_UNKNOW_ORDER = 1, FIND_TILT_BAD_EDGES, FIND_TILT_BAD_CENTER,
  FIND_TILT_CLIPPED, FIND_TILT_BAD_FIT
} ;

typedef struct {
  double xpos ;
  double ypos ;
  double fwhm ;
  double area;
  int good ;                    /**< 0 if good point, 1 otherwise */
} CENTROIDS ;

static int detect_centroid( xsh_pre * pre,
                            float lambda, int ordnum, double xpix,
                            double ypix,
                            xsh_follow_arclines_param * follow_param,
                            int is_center,
                            XSH_GAUSSIAN_FIT * fit_res );

/*-----------------------------------------------------------------------------
  Implementation
  -----------------------------------------------------------------------------*/

/** 
 * Along the X axis, detect the centers of the line, fit the centers to
 * get the tilt.
 * 
 * @param pre Scientific Frame (xsh_pre format)
 * @param lambda Wavelength
 * @param ordnum Order number
 * @param xpix X position of the initial center (in [1,nx])
 * @param y Y position of the initial center (in [1,ny])
 * @param follow_param Parameters used to follow the line
 * @param is_center parameter NOT USED!! 
 * @param fit_res Result of the tilt fit
 * 
 * @return 0 if OK, 1 otherwise (can not fit)
 */
// FIXME is_center parameter NOT USED!! 
static int 
detect_centroid( xsh_pre * pre,
		 float lambda, 
                 int ordnum, 
                 double xpix,
		 double y,
		 xsh_follow_arclines_param * follow_param,
                 int is_center,
		 XSH_GAUSSIAN_FIT * fit_res )
{
  cpl_vector * pixpos = NULL ;
  cpl_vector * pixval = NULL ;
  int first, last, i, nelem, ny, ix ;
  double dy, ypix;
  int ret = 1 ;

  if ( xpix <= 0 || xpix > pre->nx || y <= 0 || y > pre->ny )
    return ret ;

  XSH_ASSURE_NOT_ILLEGAL( xpix > 0 && xpix <= pre->nx &&
			  y > 0 && y <= pre->ny );

  ix = xsh_round_double(xpix);
  ypix = xsh_round_double( y);

  first = ypix - follow_param->range;

  if ( first < 0 ) {
    xsh_msg_dbg_high( "%lf - %d : Y pixel < 0 (%d)", lambda, ordnum, first ) ;
    first = 1 ;
  }
  last = ypix + follow_param->range ;
  if( last > pre->ny ) {
    xsh_msg_dbg_low( "Y = %d too high, ignore", last ) ;
    return ret ; /* Y too high, ignore */
  }

  dy = ypix - follow_param->range ;
  nelem = (follow_param->range*2) + 1 ;

  check( pixpos = cpl_vector_new( nelem ) ) ;
  check( pixval = cpl_vector_new( nelem ) ) ;

  for( i = 0, ny = first ; ny <= last ; ny++, i++, dy += 1. ) {
    int rej ;
    double value ;

    cpl_error_reset() ;
    value = cpl_image_get( pre->data, ix, ny, &rej ) ;
    if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
      xsh_msg_dbg_high( "       *** X,Y out of range %d,%d", ix, ny ) ;
      cpl_error_reset() ;
      continue ;
    }
    cpl_vector_set( pixval, i, value ) ;
    cpl_vector_set( pixpos, i, dy ) ;
  }

  cpl_error_reset() ;
  xsh_vector_fit_gaussian( pixpos, pixval, fit_res ) ;
  if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
    xsh_msg_dbg_high("Failed to fit Y centroid for x %d y [%d, %d], wavelength %f", 
      ix, first, last, lambda);
    cpl_error_reset() ;
  }
  else {
    if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_HIGH){
      FILE *debug_file = NULL;
      char name[256];
      int idebug = 0;
 

      sprintf( name, "profil_%d_%f_%f.dat", ordnum, lambda, xpix);

      debug_file = fopen( name, "w");
      fprintf( debug_file, "# ypos yflux yfit\n");
      for( idebug=0; idebug< nelem; idebug++){
        double gauss;
        double x;

        x =  cpl_vector_get( pixpos, idebug);
        gauss = fit_res->area / sqrt(2 *M_PI*fit_res->sigma*fit_res->sigma)* 
         exp( -(x-fit_res->peakpos)*(x-fit_res->peakpos)/(2*fit_res->sigma*fit_res->sigma)) + fit_res->offset;

        fprintf( debug_file, "%f %f %f\n", x, cpl_vector_get( pixval, idebug), gauss);
      }
      fclose( debug_file);
    }
    xsh_msg_dbg_medium( "For x %d, y [%d, %d] centroid fit in Y = %lf", 
      ix, first, last, fit_res->peakpos);
    ret = 0;
  }

  cleanup:
    xsh_free_vector( &pixpos ) ;
    xsh_free_vector( &pixval ) ;
    return ret ;
}

static cpl_polynomial * 
get_slit_ifu_lo_poly( xsh_order * porder,int ifu_flag )
{
  switch ( ifu_flag ) {
  case CENTER_SLIT:
    return porder->edglopoly ;
  case UPPER_IFU_SLITLET:
    return porder->edglopoly ;
  case CENTER_IFU_SLITLET:
    return porder->sliclopoly ;
  case LOWER_IFU_SLITLET:
    return porder->slicuppoly ;
  default:
    return NULL ;
  }
}

static cpl_polynomial * 
get_slit_ifu_up_poly( xsh_order * porder,int ifu_flag )
{
  switch ( ifu_flag ) {
  case CENTER_SLIT:
    return porder->edguppoly ;
  case UPPER_IFU_SLITLET:
    return porder->sliclopoly ;
  case CENTER_IFU_SLITLET:
    return porder->slicuppoly ;
  case LOWER_IFU_SLITLET:
    return porder->edguppoly ;
  default:
    return NULL ;
  }
}

/*****************************************************************************/

/*****************************************************************************/
/*****************************************************************************/
static int 
find_tilt( double yp0, double xc, float lambda, int ordnum,
		      xsh_follow_arclines_param * follow_param,
		      xsh_pre * pre, xsh_order_list * orders,
		      xsh_instrument * instrument, double * slope,
		      double * chisq, double * minx, double * maxx,
		      int * nt, int * ng, double * fwhm_center,
		      double * good_center, int ifu_flag )
{
  int result=0;
  cpl_polynomial * lo_poly = NULL;
  cpl_polynomial * up_poly = NULL;

  CENTROIDS * centers = NULL ;
  double x0, x, ycenter ;
  XSH_GAUSSIAN_FIT fit_res ;
  cpl_vector *pos_vect =NULL, *val_vect = NULL;
  int *flags = NULL;
  cpl_polynomial * poly_tilt = NULL ;
  double xmin, xmax ;
  int iorder ;
  int i, j, k, ngood;
  int csize ;
  cpl_vector *fwhm_median_vect = NULL;
  double *median_data = NULL;
  double fwhm_median = 0.0;
  int tilt_niter;
  double tilt_kappa, tilt_frac, good_frac;
  int ntot, no_centroid =0; 
  cpl_size deg1 = 1;

  XSH_ASSURE_NOT_NULL( follow_param);
  XSH_ASSURE_NOT_NULL( follow_param->tilt_clipping);
  
  tilt_niter = follow_param->tilt_clipping->niter;
  tilt_kappa = follow_param->tilt_clipping->sigma;
  tilt_frac = follow_param->tilt_clipping->frac;

  /*  Extract from order table lower and upper edge of the arc line */
  check( iorder = xsh_order_list_get_order( orders, ordnum));

  if ( iorder < 0 ) {
    xsh_msg( "Unknown Absolute Order %d", ordnum);
    return FIND_TILT_UNKNOW_ORDER ;
  }

  lo_poly = get_slit_ifu_lo_poly( &orders->list[iorder], ifu_flag ) ;
  up_poly = get_slit_ifu_up_poly( &orders->list[iorder], ifu_flag ) ;

  check( xmin = xsh_order_list_eval( orders, lo_poly, yp0));
  check( xmax = xsh_order_list_eval( orders, up_poly, yp0));

  /* Suppress points too close to the order edges */
  xmin = *minx;
  xmax =  *maxx;

  xmin += follow_param->margin ;
  xmax -= follow_param->margin ;

  *minx = xmin ;
  *maxx = xmax ;

  xsh_msg_dbg_medium( "  Find_tilt - order %d, idx %d, xmin = %.2lf, xmax = %.2lf, yc = %.2lf",
    ordnum, iorder, xmin, xmax, yp0 ) ;

  if ( xmax <= xmin ) {
    xsh_msg( "******* Something Wrong in Edges!" ) ;
    return FIND_TILT_BAD_EDGES ;
  }

  if ( xc <= xmin || xc >= xmax ) {
    xsh_msg( "lambda %f, order %d: Center (from wavesol) %lf not in ] %lf, %lf [",
      lambda, ordnum, xc, xmin, xmax ) ;
    return FIND_TILT_BAD_CENTER ;
  }


  /* Beware !!! (int)(xmax - xmin + 1 ) may lead to problems ! */
  csize = ceil(xmax) - floor(xmin);
  XSH_CALLOC( centers, CENTROIDS, csize);

  x0 = xsh_round_double(xc);
  ngood = 0 ;
  ntot = 0;

  ycenter = xsh_round_double(yp0);

  /* First go from center to lower X edge */
  for( x = x0 ; x >= xmin ; x--) {
    int ok;

    centers[ntot].xpos = x;
    ok = detect_centroid( pre, lambda, ordnum, x, ycenter,
      follow_param, 0, &fit_res);
    if ( ok == 0 && fit_res.peakpos > 0. ) {
      centers[ntot].ypos = fit_res.peakpos ;
      centers[ntot].fwhm = CPL_MATH_FWHM_SIG*fit_res.sigma;
      centers[ntot].area = fit_res.area;
      centers[ntot].good = 0 ;
      ycenter = fit_res.peakpos ;
      ngood++ ;
    }
    else {
      no_centroid++ ;
      centers[ntot].good = 1 ;
    }
    ntot++;
  }
  ycenter = xsh_round_double( yp0);

  /* Last go from center(+1) to upper X edge */
  for( x = x0+1 ; x <= xmax ; x++) {
     int ok;

    centers[ntot].xpos = x;
    ok = detect_centroid( pre, lambda, ordnum, x, ycenter,
      follow_param, 0, &fit_res);
    if ( ok == 0 && fit_res.peakpos > 0. ) {
      centers[ntot].ypos = fit_res.peakpos ;
      centers[ntot].fwhm = CPL_MATH_FWHM_SIG*fit_res.sigma ;
      centers[ntot].area = fit_res.area;
      centers[ntot].good = 0 ;
      ycenter = fit_res.peakpos ;
      ngood++ ;
    }
    else {
      no_centroid++ ;
      centers[ntot].good = 1 ;
    }
    ntot++;
  }
  xsh_msg_dbg_medium(" For lambda %f in order %d : Fitted %d/%d (%d no centroid)", 
    lambda, ordnum, ngood, ntot, no_centroid);

  good_frac = (double)ngood/(double) ntot;

  if ( ngood >= 2 && good_frac >= tilt_frac){
    /* Try to fit tilt */
    xsh_msg_dbg_medium("---Clipping on tilt");

    check( pos_vect = cpl_vector_new( ngood));
    check( val_vect = cpl_vector_new( ngood));
  
    j = 0;
    for( i = 0 ; i< ntot; i++ ) {
      if ( centers[i].good == 0){
        cpl_vector_set( pos_vect, j, centers[i].xpos);
        cpl_vector_set( val_vect, j, centers[i].ypos);
        j++;
      }
    }

    check( xsh_array_clip_poly1d( pos_vect, val_vect, tilt_kappa, tilt_niter,
      tilt_frac, 1, &poly_tilt, chisq, &flags));

    /* compute fwhm median on good lines */
    j = 0;
    k = 0;
    XSH_CALLOC( median_data, double, ngood);

    for( i = 0 ; i< ntot; i++ ) {
      if ( centers[i].good == 0){
        if (flags[j] == 0){
          median_data[k] = centers[i].fwhm;
          k++;
        }
        else{
          centers[i].good = 2;
        }
        j++;
      }
    }

    check( fwhm_median_vect = cpl_vector_wrap( k, median_data));

    check( fwhm_median = cpl_vector_get_median( fwhm_median_vect));
    *fwhm_center = fwhm_median;

    /* get slope and center */
    check( *good_center = cpl_polynomial_eval_1d( poly_tilt, x0, NULL));
    check( *slope = cpl_polynomial_get_coeff( poly_tilt, &deg1));

    xsh_msg_dbg_medium(" find_tilt : slope %f fwhm_median %f tilt at center %f %f ",
      *slope, fwhm_median, x0, *good_center);

    *nt = ntot;
    *ng = ngood;

#if REGDEBUG_WAVECAL
    /* REGDEBUG */
    {
      FILE *fwhm_debug_file = NULL;
      char name[256];
      int idebug = 0;

      sprintf( name, "fwhm_%d_%f.dat", ordnum, lambda);

      if ( fwhm_debug_mode == NULL){
        fwhm_debug_file = fopen( name, "w");
        fwhm_debug_mode = "a+";
        fprintf( fwhm_debug_file,
          "# wavelength order xcen xcen-x0 ygauss ytilt fwhm area good\n");
      }
      else{
        fwhm_debug_file = fopen( name, fwhm_debug_mode);
      }
      for( idebug=0; idebug< ntot; idebug++){
        double fit;
        double xpos;

        xpos = xsh_round_double( centers[idebug].xpos );

        fit = cpl_polynomial_eval_1d( poly_tilt, xpos, NULL);
        fprintf( fwhm_debug_file, "%f %d %f %f %f %f %f %f %d\n",  
          lambda, ordnum, 
          xpos, xpos-x0,
          centers[idebug].ypos, fit, centers[idebug].fwhm, 
          centers[idebug].area, centers[idebug].good);
      }
      fclose( fwhm_debug_file);
    }
#endif
  }
  else{
    result = FIND_TILT_BAD_FIT;
    xsh_msg( "Not enough points to do the fit: Good fraction points (%f < %f)", 
      good_frac, tilt_frac);
  }

  cleanup:
    xsh_free_vector( &pos_vect);
    xsh_free_vector(&val_vect);
    xsh_free_polynomial( &poly_tilt);
    XSH_FREE( flags);
    xsh_unwrap_vector( &fwhm_median_vect);
    XSH_FREE( median_data);
    XSH_FREE( centers);
    return result;
}
/*****************************************************************************/

static float linear_interpol( float xa, float ya, float xb, float yb, float x )
{
  float xb_xa ;

  xb_xa = xb-xa ;
  return (ya*(xb-x))/(xb_xa) + (yb*(x-xa))/(xb_xa) ;

}

static float get_lambda( float * data, float x, float y, int nx, int ny )
{
  /* Should calculate lambda with interpolation, because in the wavemap
     image there are only integer pixels
  */
  float lambda, lm, lp;
  int ym, yp;
  int xpos;

  if(y>ny) return 0;

  /* take pixels around value */
  yp = (int) ceil( y);
  ym = (int) floor( y);

  xpos = (int) xsh_round_double( x);
  lm = data[(ym-1)*nx+xpos];
  lp = data[(yp-1)*nx+xpos];
  
  //  Now interpolate
  lambda = linear_interpol( ym, lm, yp, lp, y ) ;
  xsh_msg_dbg_high( "   ym: %d, lm: %f, yp: %d, lp: %f, y: %f ==> %f",
		    ym, lm, yp, lp, y, lambda )  ;

  return lambda ;
}

/*****************************************************************************/
/** 
 * Compute the spectral resolution using the wave map.
 *
 * @param wavemap_frame wave map frame
 * @param disptab_frame dispersion table frame 
 * @param instr Instrument structure
 * @param tilt_list Line tilt list frame
 * @param niter number of iterations for kappa sigma clip of outliers
 * @param kappa kappa value used in kappa sigma clip of outliers
 * @param frac minimum fraction of detector's good pixels
 * @param specres_med vector with median values of spectral resolution
 * @param specres_stdev vector with stdev values of spectral resolution
 */
/*****************************************************************************/
static void 
compute_specres( cpl_frame *wavemap_frame, 
		 cpl_frame *disptab_frame,
		 xsh_instrument* instr,
		 xsh_linetilt_list *tilt_list,
		 int niter, 
                 double kappa, 
                 double frac,
		 double *specres_med, 
		 double *specres_stdev)
{
  cpl_array *a_specres = NULL ;
  int  i=0, j=0, status=0 ;
  xsh_dispersol_list* displist = NULL;
  cpl_vector* positions = NULL;
  const char *wavemap_name = NULL;
  cpl_image *wavemap_img = NULL;
  float *wavemap_data = NULL ;
  int nx=0, ny=0;
  int tilt_list_size =0;

  XSH_ASSURE_NOT_NULL( tilt_list);

  /* Load Wavemap image */
  if (disptab_frame != NULL){
    xsh_msg("Use the DISP_TAB");
    check( displist = xsh_dispersol_list_load( disptab_frame, instr));
    check( positions = cpl_vector_new(2));
  }
  else{
    xsh_msg("Use the WAVE_MAP");
    XSH_ASSURE_NOT_NULL( wavemap_frame);
    check( wavemap_name = cpl_frame_get_filename( wavemap_frame));
    check( wavemap_img = cpl_image_load( wavemap_name, CPL_TYPE_FLOAT, 0, 0));
    check( wavemap_data = cpl_image_get_data_float( wavemap_img));
    nx = cpl_image_get_size_x( wavemap_img);
    ny = cpl_image_get_size_y( wavemap_img);
  }

  /* Create vector to house line's specres */
  tilt_list_size = tilt_list->size;
  check( a_specres = cpl_array_new( tilt_list_size, CPL_TYPE_DOUBLE));

  /* Loop over Lines */
  for( i=0 ; i< tilt_list_size; i++) {
    double xc=0, yc=0, yc0=0, yc1=0;
    double pix_width=0 ;
    float lambda0=0, lambda1=0, lambdac=0;
    double tilt =0.0;

    /* get X, Y position of the center */
    xc = tilt_list->list[i]->cenposx;
    yc = tilt_list->list[i]->tilt_y;

    pix_width = tilt_list->list[i]->deltay;
    lambdac = tilt_list->list[i]->wavelength;
    tilt = tilt_list->list[i]->tilt;

    /* line is init to invalid data */
    tilt_list->list[i]->specres = 0;
    tilt_list->list[i]->flag = 1;

    /* calculate lower/upper positions of mid width in pixels */
    yc0 = yc - (pix_width/2.);
    yc1 = yc + (pix_width/2.);

    if ( displist != NULL){
      int absorder = tilt_list->list[i]->order;
      int iorder=0;
      
      while( absorder != displist->list[iorder].absorder){
        iorder++;
      } 
      cpl_vector_set( positions, 0, xc);
      cpl_vector_set( positions, 1, yc0);

      check( lambda0 = xsh_dispersol_list_eval( displist, 
        displist->list[iorder].lambda_poly, positions));

      cpl_vector_set( positions, 1, yc1);
      check( lambda1 = xsh_dispersol_list_eval( displist,
        displist->list[iorder].lambda_poly, positions));
    }
    else{
        if( (yc0 >= 1) && (yc1 <= ny-1) ) {
      // convert to lambda ==> get lambda from wavemap
          lambda0 = get_lambda( wavemap_data, xc, yc0, nx,ny);
          lambda1 = get_lambda( wavemap_data, xc, yc1, nx,ny);
        }
    }
    xsh_msg_dbg_high("Lambda1: %f, Lambda0: %f, Lambdac: %f, Pix_width: %lf",
      lambda0, lambda1, lambdac, pix_width);

    if ( lambda0 != 0. && lambda1 != 0.) {
      tilt_list->list[i]->flag = 0;
        /* tilt = tan(alpha) cos(atan(alpha)) = 1/sqrt(1+tilt²) */
	tilt_list->list[i]->specres = lambdac/fabs(lambda1 - lambda0)*
	  sqrt( 1+tilt*tilt);
    }
    check( cpl_array_set( a_specres, i, tilt_list->list[i]->specres));
  }

  check( *specres_med = cpl_array_get_median( a_specres));
  check( *specres_stdev = cpl_array_get_stdev( a_specres));

  xsh_msg("---Clipping on spectral resolution");
  check( xsh_array_clip_median( a_specres, kappa, niter,
    frac, specres_med, specres_stdev));

  for( j=0; j < tilt_list_size; j++){
    //double val=0.0;
  

    //val = cpl_array_get_double( a_specres, j, &status);
    if (status == 1){
      tilt_list->list[j]->flag = 2;
    }
  }
 cleanup:
  xsh_free_array( &a_specres);
  xsh_free_image( &wavemap_img);
  xsh_free_vector( &positions);
  xsh_dispersol_list_free( &displist);
  return ;
}
/*****************************************************************************/

/*****************************************************************************/
/*****************************************************************************/
static void 
set_qc_parameters( cpl_propertylist *tilt_header,
                   cpl_propertylist *shift_header, 
		   double * ypos, 
                   double * width,
		   double * intens, 
		   xsh_linetilt_list *tilt_list,
		   xsh_instrument * instrument,
		   int nlinecat, 
                   int nb_lines,
		   double specres_med, 
                   double specres_stdev)
{
  cpl_array * yarr = NULL, * warr = NULL, * iarr = NULL ;
  double avg, med, std, wavg, wstd, iavg ;
  int n=tilt_list->size;
  int i=0;
  int k=0;
  check( yarr = cpl_array_wrap_double( ypos, n ) ) ;
  check( warr = cpl_array_wrap_double( width, n ) ) ;
  check( iarr = cpl_array_wrap_double( intens, n ) ) ;
  /* remove from vector flagged values */
  for(i=0;i<n;i++) {
    if(tilt_list->list[i]->flag >0 ) {
      cpl_array_set_invalid(yarr,i);
      cpl_array_set_invalid(warr,i);
      cpl_array_set_invalid(iarr,i);
      k++;
    }
  }
  check( avg = cpl_array_get_mean( yarr ) ) ;
  check( med = cpl_array_get_median( yarr ) ) ;
  check( std = cpl_array_get_stdev( yarr ) ) ;

  check( wstd = cpl_array_get_stdev( warr ) ) ;
  check( wavg = cpl_array_get_mean( warr ) ) ;

  check( iavg = cpl_array_get_mean( iarr ) ) ;

  xsh_msg("diffy avg=%16.14g med=%16.14g std=%16.14g",avg,med,std);
  xsh_msg("FWHM avg=%16.14g std=%16.14g",wavg,wstd);
  xsh_msg("nlineint iavg=%16.14g",iavg);

  xsh_msg_dbg_low( "FWHM Avg: %.2lf, RMS: %.2lf", wavg, wstd ) ;

  check( xsh_pfits_set_qc( tilt_header, &avg, QC_WAVECAL_DIFFYAVG,
			   instrument ) ) ;
  check( xsh_pfits_set_qc( tilt_header, &med, QC_WAVECAL_DIFFYMED,
			   instrument ) ) ;
  check( xsh_pfits_set_qc( tilt_header, &std, QC_WAVECAL_DIFFYSTD,
			   instrument ) ) ;
  check( xsh_pfits_set_qc( tilt_header, &iavg, QC_WAVECAL_NLININT,
                           instrument));
  check( xsh_pfits_set_qc( tilt_header, &wavg, QC_WAVECAL_FWHMAVG,
                           instrument ) ) ;
  check( xsh_pfits_set_qc( tilt_header, &wstd, QC_WAVECAL_FWHMRMS,
                           instrument ) ) ;
  check( xsh_pfits_set_qc( tilt_header, &nlinecat, QC_WAVECAL_CATLINE,
                           instrument ) ) ;
  check( xsh_pfits_set_qc( tilt_header, &nb_lines, QC_WAVECAL_MATCHLINE,
                           instrument ) ) ;
  check( xsh_pfits_set_qc( tilt_header, &n, QC_WAVECAL_FOUNDLINE,
                           instrument));
  check( xsh_pfits_set_qc( tilt_header, &specres_med, QC_RESOLMED, instrument));
  if(isnan(specres_stdev)) {
      /* dummy value to allow saving of product */
      specres_stdev = -999;
  }
  check( xsh_pfits_set_qc( tilt_header, &specres_stdev, QC_RESOLRMS, instrument));



  check( xsh_pfits_set_qc( shift_header, &avg, QC_WAVECAL_DIFFYAVG,
                           instrument ) ) ;
  check( xsh_pfits_set_qc( shift_header, &med, QC_WAVECAL_DIFFYMED,
                           instrument ) ) ;
  check( xsh_pfits_set_qc( shift_header, &std, QC_WAVECAL_DIFFYSTD,
                           instrument ) ) ;
  check( xsh_pfits_set_qc( shift_header, &iavg, QC_WAVECAL_NLININT,
                           instrument));

 cleanup:
  xsh_unwrap_array( &yarr ) ;
  xsh_unwrap_array( &warr ) ;
  xsh_unwrap_array( &iarr ) ;
  return ;
}
/*****************************************************************************/

/*****************************************************************************/
/*****************************************************************************/
static void 
clean_arclist_data( cpl_frame *wavesol_frame, 
		    cpl_frame *arclines_frame,
		    xsh_order_list *orders, 
		    cpl_frame *config_model_frame,
		    cpl_frame *pre_frame,
		    cpl_frame *spectralformat_frame,
		    double **lambda, 
		    double **n, 
		    double **x,
		    double **y,
		    double **xmin, 
		    double **xmax,
		    int *size, 
		    double slit, 
		    double slit_min, 
		    double slit_max,
		    xsh_instrument *instrument)
{
  xsh_arclist *arclist = NULL;
  int arclist_size =0, global_size=0;
  cpl_vector** spectral_tab = NULL;
  xsh_wavesol * wsol = NULL;
  xsh_xs_3 config_model;
  int found_temp=true;
  xsh_spectralformat_list *spectralformat_list = NULL;
  int bad_positions=0;
  int i, j, index_loc = 0;

  check( arclist = xsh_arclist_load( arclines_frame));
  check( spectralformat_list =
    xsh_spectralformat_list_load( spectralformat_frame, instrument));
  check( arclist_size = xsh_arclist_get_size( arclist));

  if ( wavesol_frame != NULL) {
     check( wsol = xsh_wavesol_load( wavesol_frame, instrument));
  }
  else if ( config_model_frame != NULL ) {
   /* update cfg table for temperature */
   check(xsh_model_temperature_update_frame( &config_model_frame, pre_frame,
     instrument, &found_temp));
    check( xsh_model_config_load_best( config_model_frame, &config_model));
    check( xsh_model_binxy(&config_model,instrument->binx,instrument->biny));
  }
  else{
    XSH_ASSURE_NOT_ILLEGAL_MSG(1==0,
                               "Undefined solution type (POLY or MODEL).");
  }

   XSH_CALLOC( spectral_tab, cpl_vector*, arclist_size);

  for( i=0; i< arclist_size; i++){
    cpl_vector* res = NULL;
    float lambdaARC;
    int res_size;

    check( lambdaARC = xsh_arclist_get_wavelength( arclist, i));
    check( res = xsh_spectralformat_list_get_orders( spectralformat_list,
      lambdaARC));
 
    if ( res != NULL){
      check( res_size = cpl_vector_get_size( res));
    }
    else{
      res_size = 0;
      check( xsh_arclist_reject(arclist,i));
    }
    global_size += res_size;
    spectral_tab[i] = res;
  }
  xsh_msg (" Arc List x Order size = %d", global_size);

  XSH_MALLOC( *lambda, double, global_size);
  XSH_MALLOC( *n, double, global_size);
  XSH_MALLOC( *x, double, global_size);
  XSH_MALLOC( *xmin, double, global_size);
  XSH_MALLOC( *xmax, double, global_size);
  XSH_MALLOC( *y, double, global_size);

  for( i=0; i< arclist_size; i++){
    double comp_x=0.0, comp_y=0.0, comp_xmin=0.0, comp_ymin=0.0;
    double comp_xmax=0.0, comp_ymax=0.0;
    cpl_vector *spectral_res = NULL;
    int spectral_res_size=0;
    float lambdaARC;

    check( lambdaARC = xsh_arclist_get_wavelength(arclist, i));
    check( spectral_res = spectral_tab[i]);

    if (spectral_res != NULL){
      check( spectral_res_size = cpl_vector_get_size( spectral_res));
      for( j=0; j< spectral_res_size; j++){
        int absorder = 0;
        int oidx;

        check( absorder = (int) cpl_vector_get( spectral_res, j));
        check( oidx = xsh_order_list_get_index_by_absorder( orders, absorder));
        if (wsol != NULL){
          check( comp_x = xsh_wavesol_eval_polx( wsol, lambdaARC, absorder, 
            slit));
          check( comp_y = xsh_wavesol_eval_poly( wsol, lambdaARC, absorder, 
            slit));
          check( comp_xmin = xsh_wavesol_eval_polx( wsol, lambdaARC, absorder,
            slit_min));
          check( comp_xmax = xsh_wavesol_eval_polx( wsol, lambdaARC, absorder,
            slit_max));
        }
        else{
          check( xsh_model_get_xy( &config_model, instrument,
            lambdaARC, absorder, slit, &comp_x, &comp_y));
          check( xsh_model_get_xy( &config_model, instrument,
            lambdaARC, absorder, slit_min, &comp_xmin, &comp_ymin));
          check( xsh_model_get_xy( &config_model, instrument,
            lambdaARC, absorder, slit_max, &comp_xmax, &comp_ymax));
        }

        if ( (comp_y >= xsh_order_list_get_starty( orders, oidx )) &&
             (comp_y <= xsh_order_list_get_endy( orders, oidx))) {
          (*lambda)[index_loc] = lambdaARC;
          (*n)[index_loc] = absorder;
          (*x)[index_loc] = comp_x;
          (*y)[index_loc] = comp_y;
          if ( comp_xmin < comp_xmax){
            (*xmin)[index_loc] = comp_xmin;
            (*xmax)[index_loc] = comp_xmax;
            xsh_msg_dbg_medium("The relation f(s)->X is increasing, X and s axes have same direction.");
          }
          else{
            (*xmin)[index_loc] = comp_xmax;
            (*xmax)[index_loc] = comp_xmin;
            xsh_msg_dbg_medium("The relation f(s)->X is decreasing, X and s axes have opposite  directions");
          }
          xsh_msg_dbg_medium(
            "lambda %f order %d slit %f smin %f smax %f x %f y %f xmin %f xmax %f",
            lambdaARC, absorder, slit, slit_min, slit_max, comp_x, comp_y, comp_xmin, comp_xmax);
          index_loc++;
        }
        else{
          xsh_msg_dbg_low( "Ypix out of Order" ) ;
          bad_positions++ ;
        }
      }
    }
  }
  xsh_msg("Clean size %d (%d bad positions)", index_loc, bad_positions);
  *size = index_loc;

  cleanup:
    xsh_arclist_free( &arclist);
    xsh_wavesol_free( &wsol);
    xsh_spectralformat_list_free( &spectralformat_list);

    if ( spectral_tab != NULL){
      for(i=0; i< arclist_size; i++){
        xsh_free_vector( &spectral_tab[i]);
      }
      XSH_FREE( spectral_tab);
    }
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      XSH_FREE( *lambda);
      XSH_FREE( *n);
      XSH_FREE( *x);
      XSH_FREE( *xmin);
      XSH_FREE( *xmax);
      XSH_FREE( *y);
    }
    return;
}
/*****************************************************************************/


/*****************************************************************************/
/** 

* @brief
* Detect and follow arc lines. Computes center, width and tilt parameters.
* The position of the center of each line is computed using the wavesol
* table, with arguments: lambda, order and slit. lambda is taken
* from the arclines table, order and slit is taken from the theoretical 
* table.
 
* @param pre_frame input frame in PRE format
* @param arclines_frame Input image with arcs
* @param wavesol_frame original wavesolution (from 2dmap)
* @param order_frame Order table frame
* @param[in] spectralformat_frame The spectral format
* @param[in] config_model_frame The model configuration frame
* @param[in] wavemap_frame wavemap frame
* @param[in] disptab_frame dispersion solution frame
* @param[in] follow_param  parameters to control line tilt determination 
* @param[in] slit slit extension value
* @param[in] slit_min min slit value in arcsec 
* @param[in] slit_max max slit value in arcsec
* @param[in] tag_id product category
* @param[in] ifu_flag flag to know if arc frame is observed in IFU mode
* @param[in] instrument instrument arm setting
* @param[out] tilt_frame line tilt table frame
* @param[out] shift_frame  line shift frame
*/
/*****************************************************************************/
static void 
xsh_follow_arclines( cpl_frame *pre_frame,
			  cpl_frame *arclines_frame,
			  cpl_frame *wavesol_frame, 
			  cpl_frame *order_frame,
                          cpl_frame *spectralformat_frame,
			  cpl_frame * config_model_frame,
                          cpl_frame *wavemap_frame,
                          cpl_frame *disptab_frame,
			  xsh_follow_arclines_param *follow_param,
                          double slit,
                          double slit_min,
                          double slit_max,
                          const char* tag_id,
                          int ifu_flag,
			  xsh_instrument * instrument,
			  cpl_frame **tilt_frame,
			  cpl_frame **shift_frame, const int clean_tmp)
{
  xsh_pre * pre = NULL;
  xsh_order_list *orders = NULL;
  double * vlambdadata = NULL, *vorderdata = NULL;
  double * vxthedata = NULL,  * vythedata = NULL;
  double *vxmindata = NULL,  * vxmaxdata = NULL;
  int sol_size = 0;
  const char* arclines_name = NULL;
  int s_n_low = 0, no_centroid = 0, bad_position = 0 , no_tilt=0;

  XSH_GAUSSIAN_FIT fit_res ;
  xsh_shift_tab *shift_tab = NULL ;
  xsh_linetilt_list *tilt_list = NULL ;
  cpl_propertylist *tilt_header = NULL ;
  double *ydelta = NULL, *width = NULL, *intensity = NULL;  
  int goodlines = 0 ;

  int i;

  const char * shift_tag = NULL ;
  char  tag[256];
  char fname[256];
  int specres_niter=0;
  double specres_kappa = 0, specres_frac = 1;
  double specres_med = 0., specres_stdev = 0 ;
  cpl_vector *svect = NULL;
  int cat_lines, match_lines, found_lines;

  XSH_ASSURE_NOT_NULL( pre_frame);
  XSH_ASSURE_NOT_NULL( arclines_frame);
  XSH_ASSURE_NOT_NULL( order_frame);

  XSH_ASSURE_NOT_NULL( follow_param);
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( spectralformat_frame);
  XSH_ASSURE_NOT_NULL( tag_id);

  /* parameters */
  XSH_ASSURE_NOT_NULL( follow_param->specres_clipping);
  specres_niter = follow_param->specres_clipping->niter;  
  specres_kappa = follow_param->specres_clipping->sigma;
  specres_frac = follow_param->specres_clipping->frac;

  check( pre = xsh_pre_load( pre_frame, instrument));
  check( orders = xsh_order_list_load( order_frame, instrument));
  /* Load data */

  xsh_set_image_cpl_bpmap ( pre->data, pre->qual, instrument->decode_bp);
 
  /* clean arc list */
  check( clean_arclist_data( wavesol_frame, arclines_frame, orders, 
    config_model_frame, pre_frame, spectralformat_frame, 
    &vlambdadata, &vorderdata, 
    &vxthedata, &vythedata, &vxmindata, &vxmaxdata, 
    &sol_size, slit, slit_min, slit_max, instrument));

  /* Create shifttab */
  check( shift_tab = xsh_shift_tab_create( instrument));

  XSH_CALLOC( ydelta, double, sol_size);
  XSH_CALLOC( width, double, sol_size);
  XSH_CALLOC( intensity, double, sol_size);

  check( arclines_name = cpl_frame_get_filename( arclines_frame));
  check( tilt_header = cpl_propertylist_load( arclines_name, 0));
  check( tilt_list = xsh_linetilt_list_new( sol_size, tilt_header));

  /* Loop over all clean arc lines:
     Loop over arclines
     get a lambda and slit
     while ( get the order from Theoretical map )
     compute X,Y position from wavesol
     compute center
     fit gaussian
     if fit OK then
     calculate tilt
     save
     fi
     endwhile
  */

  for ( i = 0 ; i < sol_size ; i++ ) {
    double lambda, order;
    double xpix, ypix;
    int ok, ret, rej;
    int nt = 0, ng = 0 ;
    double s_n =0;

    lambda = vlambdadata[i];
    order = vorderdata[i];
    xpix = vxthedata[i];
    ypix = vythedata[i];

    xsh_msg_dbg_low( "LAMBDA %f, ORDER %f, SLIT %f, X %lf, Y %lf", lambda, 
		     order, slit, xpix, ypix );
    /* Detect Centroid */
    check( ok = detect_centroid( pre, lambda, order, xpix, ypix, 
      follow_param, 1, &fit_res));
 
    if ( ok != 0 ) {
      xsh_msg_dbg_low( "    ******* Could NOT Fit Centroid" ) ;
      xsh_msg_dbg_low( "Lambda: %f, Order: %f - Line NOT Fitted",
        lambda, order);
      no_centroid++ ;
    }
    else{
      /* IF S/N is < limit, dont keep this line */
      if( (xpix<=pre->nx) && (xpix>0) && (ypix<=pre->ny) && (ypix>0)) {
        double flux_val, err_val;
        
        check( flux_val = cpl_image_get( pre->data, xpix, ypix, &rej ));
        check( err_val = cpl_image_get( pre->data, xpix, ypix, &rej ));
        check(s_n = cpl_image_get( pre->data, xpix, ypix, &rej )/
            cpl_image_get( pre->errs, xpix, ypix, &rej));

        if ( s_n < follow_param->s_n_min ) {
          xsh_msg_dbg_low( "   %f/%f < %f => S/N too low, line skipped",
            flux_val, err_val, follow_param->s_n_min);
          s_n_low++;
        }
        else{
          double slope = 0.0, chisq = 0.0, xmin = 0.0, xmax = 0.0;
          double fwhm_center = 0.0;
          double good_center = 0;

          /* Then fit tilt:
             Along the X axis, [xc-length,xc+length], detect
             all centroids.
             Then fit a line (ax+b) between all centroids.
             The 'a' should be 0 if the line is // x-axis
          */
          xmin = vxmindata[i];
          xmax = vxmaxdata[i];
          if ( (ret = find_tilt( fit_res.peakpos, xpix, lambda, order,
                             follow_param, pre, orders,
                             instrument, &slope, &chisq,
                             &xmin, &xmax, &nt, &ng,
                             &fwhm_center,  &good_center,
                             ifu_flag )) == 0 ) {
            int rej, ix, iy;
            xsh_linetilt * tilt_line = NULL ;

            ix = xsh_round_double( xpix);
            iy = xsh_round_double( ypix);

            /* Prepare for QC param */
            ydelta[goodlines] =  good_center-ypix;
            width[goodlines] = CPL_MATH_FWHM_SIG*fit_res.sigma;
            intensity[goodlines] = cpl_image_get( pre->data, ix, iy, &rej);
            check( tilt_line = xsh_linetilt_new());
            tilt_line->wavelength = lambda;
            tilt_line->slit = slit;
            tilt_line->tilt = slope ;
            tilt_line->chisq = chisq;
            tilt_line->xmin = xmin ;
            tilt_line->xmax = xmax ;
            tilt_line->ntot = nt ;
            tilt_line->ngood = ng ;
            tilt_line->deltay = fwhm_center;
            tilt_line->name = NULL ;
            tilt_line->order = order ;
            tilt_line->cenposx = xpix;
            tilt_line->pre_pos_y = ypix;
            tilt_line->cenposy = fit_res.peakpos ;
            tilt_line->tilt_y = good_center;
            tilt_line->shift_y = good_center-ypix;
            tilt_line->specres = fwhm_center; /* in pixels ! */
            tilt_line->area = fit_res.area;
            tilt_line->intensity = cpl_image_get( pre->data, ix, iy, &rej);
            check( xsh_linetilt_list_add( tilt_list, tilt_line, goodlines));
            xsh_msg_dbg_low( "Lambda: %f, Order: %f - Line Fitted: %lf,%lf",
              lambda, order, xpix, tilt_line->cenposy);
            goodlines++;
          }
          else{
            xsh_msg_dbg_low( "   Can't compute tilt, line skipped" );
            no_tilt++;
          }
        }
      }
      else{
        xsh_msg_dbg_low( "   Bad position, line skipped" ) ;
        bad_position++;
      }
    }
  }

  xsh_msg( "No centroid %d Bad Position: %d, S/N too low: %d No tilt %d",
           no_centroid, bad_position, s_n_low, no_tilt);
  if ( goodlines == 0 ){
    xsh_msg( "***** NO FITTED LINE !!!!" );
  }
  XSH_ASSURE_NOT_ILLEGAL( goodlines > 0 ) ;
  xsh_msg( "  Good Fitted lines : %d/%d", goodlines, sol_size);

  /* If we have produced a wavemap, we can compute the spectral resolution */
  if ( wavemap_frame != NULL || disptab_frame != NULL) {
    compute_specres( wavemap_frame, disptab_frame, instrument,
      tilt_list, specres_niter, 
      specres_kappa, specres_frac,
		     &specres_med, &specres_stdev);
    if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
      xsh_msg( "ERROR while computing Spectral Resolution" ) ;
      cpl_error_reset() ;
      specres_med = 0. ;
      specres_stdev = 0. ;
    }
  }
  else { 
    xsh_msg_error( "NO Wavemap (WAVE_MAP) or dispersion table (DISP_TAB), can't compute Spectral Resolution");
  }

  cat_lines = sol_size;
  found_lines = goodlines;
  match_lines = tilt_list->size;

  set_qc_parameters( tilt_header, shift_tab->header, ydelta, width, intensity, 
		     tilt_list,instrument, sol_size, goodlines, 
		     specres_med, specres_stdev);

  sprintf( tag, "FOLLOW_LINETILT_%s_%s", tag_id, 
    xsh_instrument_arm_tostring( instrument));
  sprintf(fname,"%s.fits",tag);
 
  check( *tilt_frame = xsh_linetilt_list_save( tilt_list, instrument, fname, tag,
    specres_kappa, specres_niter));

  sprintf( tag, "TILT_TAB_%s_%s", tag_id,
    xsh_instrument_arm_tostring( instrument));
  check( cpl_frame_set_tag( *tilt_frame, tag));
 
  xsh_add_temporary_file(fname) ;
  xsh_msg( "Tilt Frame saved" ) ;

  /* Save the shift table */
  xsh_msg( "Saving shift table SLIT, %d lines", tilt_list->size);

  check( svect = cpl_vector_wrap( tilt_list->size, ydelta));

  if ( ifu_flag == CENTER_SLIT){
    check( shift_tab->shift_y = cpl_vector_get_median( svect));
    shift_tag = XSH_GET_TAG_FROM_ARM( XSH_SHIFT_TAB_SLIT, instrument);
  }
  else{
    check( shift_tab->shift_y_cen = cpl_vector_get_median( svect));
    sprintf( tag, "SHIFT_TAB_%s_%s", tag_id,
      xsh_instrument_arm_tostring( instrument));
    shift_tag = tag;
  } 

  check( xsh_pfits_set_qc( shift_tab->header, &cat_lines, QC_WAVECAL_CATLINE,
                           instrument ) ) ;
  check( xsh_pfits_set_qc( shift_tab->header, &match_lines, QC_WAVECAL_MATCHLINE,
                           instrument ) ) ;
  check( xsh_pfits_set_qc( shift_tab->header, &found_lines, QC_WAVECAL_FOUNDLINE,
                           instrument ) ) ;
  check( *shift_frame = xsh_shift_tab_save( shift_tab, shift_tag,clean_tmp));

  cleanup:
    xsh_pre_free( &pre);
    xsh_order_list_free( &orders);
    xsh_linetilt_list_free( &tilt_list);
    xsh_shift_tab_free( &shift_tab);
    xsh_unwrap_vector( &svect);    

    XSH_FREE( vlambdadata);
    XSH_FREE( vorderdata);
    XSH_FREE( vxthedata);
    XSH_FREE( vxmindata);
    XSH_FREE( vxmaxdata);
    XSH_FREE( vythedata);
    XSH_FREE( ydelta);
    XSH_FREE( width);
    XSH_FREE( intensity);
    return ;
}

void 
xsh_follow_arclines_slit( cpl_frame *pre_frame,
                          cpl_frame *arclines_frame,
                          cpl_frame *wavesol_frame,
                          cpl_frame *order_frame,
                          cpl_frame *spectralformat_frame,
                          cpl_frame * config_model_frame,
                          cpl_frame *wavemap_frame,
                          cpl_frame *slitmap_frame,
                          cpl_frame *disptab_frame,
                          xsh_follow_arclines_param *follow_param,
                          xsh_instrument * instrument,
                          cpl_frame **tilt_frame,
                          cpl_frame **shift_frame)
{
  double smin=-6.0, smax=6.0;

/*
  check( xsh_get_slit_edges( slitmap_frame, &smin, &smax,
    NULL, NULL, instrument));
*/

  check( xsh_follow_arclines( pre_frame, arclines_frame, wavesol_frame, 
                              order_frame, spectralformat_frame, 
                              config_model_frame, wavemap_frame, disptab_frame,
                              follow_param, 0.0, smin, smax, "SLIT", 
                              CENTER_SLIT, instrument,
                              tilt_frame, shift_frame,0));

  cleanup:
    return;
}

/*****************************************************************************/

/*****************************************************************************/
/** 
    @brief
    Detect and follow arc lines. Computes center, width and tilt parameters.
    The position of the center of each line is computed using the wavesol
    table, with arguments: lambda, order and slit. lambda is taken
    from the arclines table, order and slit is taken from the theoretical 
    table.

* @param pre_frame input frame in PRE format
* @param arclines_frame Input image with arcs
* @param wavesol_frame original wavesolution (from 2dmap)
* @param order_frame Order table frame
* @param[in] spectralformat_frame The spectral format
* @param[in] config_model_frame The model configuration frame
* @param[in] wavemap_frame wavemap frame
* @param[in] slitmap_frame wavemap frame
* @param[in] disptab_frame dispersion solution frame
* @param[in] follow_param  parameters to control line tilt determination 
* @param[in] instrument instrument arm setting
* @param[out] tilt_set line tilt table frame
* @param[out] shift_frame  line shift frame
    */
/*****************************************************************************/
void
xsh_follow_arclines_ifu( cpl_frame *pre_frame,
                         cpl_frame *arclines_frame,
                         cpl_frame *wavesol_frame, 
                         cpl_frame *order_frame,
                         cpl_frame *spectralformat_frame,
                         cpl_frame *config_model_frame,
                         cpl_frame *wavemap_frame,
                         cpl_frame *slitmap_frame,
                         cpl_frame *disptab_frame,
                         xsh_follow_arclines_param *follow_param,
                         xsh_instrument * instrument,
                         cpl_frameset *tilt_set,
                         cpl_frame **shift_frame)
{


  int ifu, spos=0;
  double slit_min=-6, slitcen_lo=-2.0, slitcen_up=2.0, slit_max=6;
  char tag_id[256];
  cpl_frameset *shift_ifu_set = NULL;
  cpl_frame *shift_tab_frame = NULL;
  xsh_shift_tab *shift_tab_low = NULL;
  xsh_shift_tab *shift_tab_cen = NULL;
  xsh_shift_tab *shift_tab_up = NULL;
  const char* shift_tag = NULL;
  double slit_pos[4];
  double shift_center=0, shift_lower=0, shift_upper=0;

  XSH_ASSURE_NOT_NULL( tilt_set);
  XSH_ASSURE_NOT_NULL( spectralformat_frame);

  shift_ifu_set = cpl_frameset_new();

  check( xsh_get_slit_edges( slitmap_frame, &slit_min, &slit_max,
    &slitcen_lo, &slitcen_up, instrument));
  slit_pos[0] = slit_min;
  slit_pos[1] = slitcen_lo;
  slit_pos[2] = slitcen_up;
  slit_pos[3] = slit_max;

  /* Loop over 3 slitlets */

  for( ifu = LOWER_IFU_SLITLET ; ifu <= UPPER_IFU_SLITLET; ifu++) {
    cpl_frame *tmp_tilt_frame = NULL;
    cpl_frame *tmp_shift_frame = NULL;
    double slitlet_min, slitlet_max, slitlet_center;

    slitlet_min = slit_pos[spos];
    slitlet_max = slit_pos[spos+1];
    slitlet_center = (slitlet_min+slitlet_max)/2.0;

    xsh_msg( "IFU Slitlet %s: center = %f arcsec; edges = [%f, %f] arcsec", 
      SlitletName[ifu], slitlet_center, slitlet_min, slitlet_max);

    sprintf( tag_id, "%s_IFU", SlitletName[ifu]) ;
 
    check( xsh_follow_arclines( pre_frame, arclines_frame, wavesol_frame,
      order_frame, spectralformat_frame, config_model_frame, wavemap_frame,
      disptab_frame,
      follow_param, slitlet_center, slitlet_min, slitlet_max, tag_id, ifu, instrument, 
                                &tmp_tilt_frame, &tmp_shift_frame,1));

    spos++;
    check( cpl_frameset_insert( tilt_set, tmp_tilt_frame));
    check( cpl_frameset_insert( shift_ifu_set, tmp_shift_frame));
  }

  check( shift_tab_frame = cpl_frameset_get_frame( shift_ifu_set, 0));
  xsh_msg_dbg_medium( "Lower Shift tab frame %s", 
    cpl_frame_get_filename( shift_tab_frame));
  check( shift_tab_low = xsh_shift_tab_load( shift_tab_frame, instrument));

  check( shift_tab_frame = cpl_frameset_get_frame( shift_ifu_set, 1));
  xsh_msg_dbg_medium( "Center Shift tab frame %s", 
    cpl_frame_get_filename( shift_tab_frame));
  check( shift_tab_cen = xsh_shift_tab_load( shift_tab_frame, instrument));

  check( shift_tab_frame = cpl_frameset_get_frame( shift_ifu_set, 2));
  xsh_msg_dbg_medium( "Upper Shift tab frame %s", 
    cpl_frame_get_filename( shift_tab_frame));
  check( shift_tab_up = xsh_shift_tab_load( shift_tab_frame, instrument));

  /* Merge all the shift tab in the center shift tab */
  shift_lower = shift_tab_low->shift_y_cen;
  shift_center = shift_tab_cen->shift_y_cen;
  shift_upper = shift_tab_up->shift_y_cen;

  xsh_msg_dbg_medium( "Measured shift for slitlet: LOWER %f, CENTER %f, UPPER %f",
    shift_lower, shift_center, shift_upper);

  shift_tab_cen->shift_y_down = shift_lower;
  shift_tab_cen->shift_y_up = shift_upper;

  shift_tag = XSH_GET_TAG_FROM_ARM( XSH_SHIFT_TAB_IFU, instrument); 
  check( *shift_frame = xsh_shift_tab_save( shift_tab_cen, shift_tag,0));


  cleanup:
    xsh_shift_tab_free( &shift_tab_low);
    xsh_shift_tab_free( &shift_tab_cen);
    xsh_shift_tab_free( &shift_tab_up);
    xsh_free_frameset( &shift_ifu_set);
    return;
}

/**@}*/
