/*                                                                           *
 *   This file is part of the ESO X-shooter Pipeline                         *
 *   Copyright (C) 2006 European Southern Observatory                        *
 *                                                                           *
 *   This library is free software; you can redistribute it and/or modify    *
 *   it under the terms of the GNU General Public License as published by    *
 *   the Free Software Foundation; either version 2 of the License, or       *
 *   (at your option) any later version.                                     *
 *                                                                           *
 *   This program is distributed in the hope that it will be useful,         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
 *   GNU General Public License for more details.                            *
 *                                                                           *
 *   You should have received a copy of the GNU General Public License       *
 *   along with this program; if not, write to the Free Software             *
 *   Foundation, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA    *
 *                                                                           */

/*
 * $Author: amodigli $
 * $Date: 2012-12-18 14:15:44 $
 * $Revision: 1.56 $
*/

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_create_waemap Wave Map Creation
 * @ingroup drl_functions
 *
 * Functions used to compute and create a wave map from the
 * residual map (recipe xsh_2dmap)
 */

#include <xsh_drl.h>
#include <xsh_pfits.h>
#include <xsh_pfits_qc.h>
#include <xsh_utils.h>
#include <xsh_data_order.h>
#include <xsh_error.h>
#include <xsh_utils.h>
#include <xsh_msg.h>
#include <xsh_data_pre.h>
#include <xsh_data_instrument.h>
#include <xsh_data_order.h>
#include <xsh_data_wavesol.h>
#include <xsh_data_resid_tab.h>
#include <xsh_data_wavemap.h>
#include <xsh_data_spectralformat.h>
#include <xsh_model_io.h>
#include <xsh_model_kernel.h>
#include <cpl.h>



/******************************************************************************/
/*
  @brief
    Create maps from a dispersion solution
  @param[in] dispsol_frame
    The dispersion solution
  @param(in] ordertab_frame
    The order table
  @param(in] pre_frame
    The science image frame
  @param[in] instrument
    The data instrument structure
  @param(out] wavemap_frame
    The wave map frame
  @param[out] slitmap_frame
    The slit map frame
*/
/******************************************************************************/
void xsh_create_map( cpl_frame *dispsol_frame, cpl_frame *ordertab_frame,
  cpl_frame *pre_frame, xsh_instrument *instrument, cpl_frame **wavemap_frame,
		     cpl_frame **slitmap_frame,const char* rec_prefix)
{
  xsh_dispersol_list *dispersol_tab = NULL;
  xsh_pre *pre = NULL;
  char wavemap_tag[256];
  char slitmap_tag[256];
  /* Checking input parameters */
  XSH_ASSURE_NOT_NULL( dispsol_frame);
  XSH_ASSURE_NOT_NULL( ordertab_frame);
  XSH_ASSURE_NOT_NULL( pre_frame);
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( wavemap_frame);
  XSH_ASSURE_NOT_NULL( slitmap_frame);

  /* Loading data */
  check( pre = xsh_pre_load( pre_frame, instrument));
  check( dispersol_tab = xsh_dispersol_list_load( dispsol_frame,
    instrument));
  sprintf(wavemap_tag,"%s_%s",
	  rec_prefix,XSH_GET_TAG_FROM_ARM( XSH_WAVE_MAP_POLY, instrument));
  sprintf(slitmap_tag,"%s_%s",
	  rec_prefix,XSH_GET_TAG_FROM_ARM( XSH_SLIT_MAP_POLY, instrument));

  check( *wavemap_frame = xsh_dispersol_list_to_wavemap( dispersol_tab,
    ordertab_frame, pre, instrument,wavemap_tag));
  check( *slitmap_frame = xsh_dispersol_list_to_slitmap( dispersol_tab,
							 ordertab_frame, 
							 pre, instrument,
							 slitmap_tag));

  cleanup:
    xsh_dispersol_list_free( &dispersol_tab);
    xsh_pre_free( &pre); 
    return;
}
/******************************************************************************/

/******************************************************************************/
/*
  @brief
    Create a wavemap using the physical model 
  @param[in]  model_frame
    The model configuration frame
  @param[in]  instrument
    The instrument description
*/
/******************************************************************************/
void xsh_create_model_map( cpl_frame* model_frame, xsh_instrument* instrument,
                           const char* wtag, const char* stag,
                           cpl_frame **wavemap_frame, 
                           cpl_frame **slitmap_frame,const int save_tmp)
{
  xsh_xs_3 model_config;
  //xsh_xs_3* p_xs_3;
  //xsh_pre * pre_sci = NULL ;
  XSH_ASSURE_NOT_NULL_MSG( model_frame,"If model-scenario is 0 make sure that the input model cfg has at least one parameter with Compute_Flag set to 1 and High_Limit>Low_limit");
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( wavemap_frame);
  XSH_ASSURE_NOT_NULL( slitmap_frame);
  XSH_ASSURE_NOT_NULL( wtag);
  XSH_ASSURE_NOT_NULL( stag);

  check( xsh_model_config_load_best( model_frame, &model_config));
/*
  check(xsh_model_temperature_update_frame(&config_model_frame,
                                            ref_frame,
                                            instr,&found_temp));
*/

  check( xsh_model_binxy( &model_config, instrument->binx, 
    instrument->biny));

  check(xsh_model_maps_create(&model_config,instrument,wtag,stag,
                                 wavemap_frame,slitmap_frame,save_tmp));

  /*
  xsh_msg("Generating dispersol frame...");
  check( *dispsol_frame = xsh_model_dispsol_create(&model_config,      
    instrument,dtag));
  */

  cleanup:
  //    xsh_pre_free( &pre_sci);

    return;
}
/******************************************************************************/

/******************************************************************************/
/*
  @brief
    Create a wavemap using the 2D solution 
  @param[in]  pre_frame
    The image frame
  @param[in] wave_tab_2d_frame
    The 2D solution
  @param[in] order_tab_frame
    The order tab frame
  @param[in] spectral_format_frame
    The spectral format frame
  @param[in] dispsol_par
    The dispersion solution parameters
  @param[in]  instrument
    The instrument description
  @param[in]  vm_tag
    The wavemap tag 
  @param[out]  dispersol_frame
    The dispersion solution frame
  @param[out]  slitmap_frame
    The slitmap frame

  @return
    The wavemap
*/
/******************************************************************************/
cpl_frame * 
xsh_create_poly_wavemap( cpl_frame *pre_frame,
                         cpl_frame *wave_tab_2d_frame, 
                         cpl_frame *order_tab_frame,
                         cpl_frame *spectral_format_frame, 
                         xsh_dispersol_param *dispsol_par,
                         xsh_instrument * instrument, 
                         const char * wm_tag,
                         cpl_frame **dispersol_frame, 
                         cpl_frame** slitmap_frame)
{
  xsh_pre *pre = NULL;
  xsh_spectralformat_list *spec_list = NULL;
  xsh_wavesol *wave_tab_2d = NULL;
  cpl_frame *result = NULL ;
  float slit_step = 1.5;
  float lambda_step = 0.1;
  //float sol_min_lambda=0;
  //float sol_max_lambda=0;
  float sol_min_slit, sol_max_slit;
  //int sol_min_order, sol_max_order;
  int i, idx, size, slit_size;
  double j, k;
  double *vlambda = NULL, *vslit = NULL, *vorder = NULL;
  double *pos_x = NULL, *pos_y = NULL;
  /* take from create_wavemap*/
  xsh_wavemap_list *wm_list = NULL ;
  int binx;
  //int biny ;
  char wm_name[256];

  /* Check input parameters */
  XSH_ASSURE_NOT_NULL( wave_tab_2d_frame);
  XSH_ASSURE_NOT_NULL( order_tab_frame);
  XSH_ASSURE_NOT_NULL( spectral_format_frame);
  XSH_ASSURE_NOT_NULL( dispsol_par);
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( dispersol_frame);

  /* Load data */
  if (pre_frame != NULL){
    check( pre = xsh_pre_load( pre_frame, instrument));
  }
  /* Get binning (used by wavesol) */
  check( binx = xsh_instrument_get_binx( instrument )) ;
  //check( biny = xsh_instrument_get_biny( instrument )) ;

  check( spec_list = xsh_spectralformat_list_load( spectral_format_frame,
    instrument));
  check( wave_tab_2d = xsh_wavesol_load( wave_tab_2d_frame,
					 instrument ));
  /* Set binning, used in wavesol_eval_polx/y */
  check( xsh_wavesol_set_bin_x( wave_tab_2d, binx ) ) ;
  check( xsh_wavesol_set_bin_y( wave_tab_2d, binx ) ) ;

  //sol_min_lambda = wave_tab_2d->min_lambda;
  //sol_max_lambda = wave_tab_2d->max_lambda;
  //sol_min_order = wave_tab_2d->min_order;
  //sol_max_order = wave_tab_2d->max_order;
  sol_min_slit = wave_tab_2d->min_slit;
  sol_max_slit = wave_tab_2d->max_slit;
  /*
  xsh_msg("sol lambda %f,%f order %d,%d slit %f,%f",
    sol_min_lambda,sol_max_lambda, sol_min_order, sol_max_order,
    sol_min_slit, sol_max_slit);
  */
  /* evaluate size of grid points */
  slit_size = (int)((sol_max_slit-sol_min_slit)/slit_step)+1;

  size=0;
  for( i=0; i< spec_list->size; i++){
    float lambda_min, lambda_max;
    int lambda_size;

    lambda_min = spec_list->list[i].lambda_min_full;
    lambda_max = spec_list->list[i].lambda_max_full;
    XSH_ASSURE_NOT_ILLEGAL_MSG(lambda_max>= lambda_min, 
                              "lambda_max< lambda_min!! Check input spectralformat table, columns WLMAXFUL and WLMINFUL");
    lambda_size = (int)((lambda_max-lambda_min)/lambda_step)+1;
    size += lambda_size*slit_size;
  }
  xsh_msg_dbg_medium( "size %d", size ) ;

  idx = 0;
  XSH_MALLOC( vorder, double, size);
  XSH_MALLOC( vlambda, double, size);
  XSH_MALLOC( vslit, double, size);
  XSH_MALLOC( pos_x, double, size);
  XSH_MALLOC( pos_y, double, size);

  for( i=0; i< spec_list->size; i++){
    double absorder;
    float lambda_min, lambda_max;

    absorder= (double)spec_list->list[i].absorder;
    lambda_min = spec_list->list[i].lambda_min_full;
    lambda_max = spec_list->list[i].lambda_max_full;
    xsh_msg("order %f lambda %f-%f",absorder, lambda_min, lambda_max);

    for(j=lambda_min; j <= lambda_max; j+=lambda_step){
      for( k=sol_min_slit; k <= sol_max_slit; k+=slit_step){
        double x, y;
        
        check( x = xsh_wavesol_eval_polx( wave_tab_2d, j, absorder, k));
        check( y = xsh_wavesol_eval_poly( wave_tab_2d, j, absorder, k));
        vorder[idx] = absorder;
        vlambda[idx] = j;
        vslit[idx] = k;
        pos_x[idx] = x;
        pos_y[idx] = y;
        idx++;
      }
    }
    xsh_msg_dbg_medium( "i %d idx %d / %d", i, idx, size);
  }

  if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM){
    FILE *test = NULL;
    int itest;

    test = fopen( "wavemap_grid.log", "w");
    for( itest=0; itest < size; itest++){
      fprintf(test, "%f %f\n", pos_x[itest], pos_y[itest]);
    }
    fclose(test);
  }

  /* take from create_wavemap*/
  check( wm_list = xsh_wavemap_list_create( instrument));
#if 0
  check( xsh_wavemap_list_compute( vlambda, pos_x, pos_y, size,
    vorder, wm_par, wm_list));
#else
  /* Had to replace 'size' by 'idx' because on MacOSX Intel, under certain 
     circumstances, the 'size' is too large by 8, though 'idx' is OK !!! 
     Actually on the Mac the following code gives a size of 766, instead of 765
     on Linux !
     Don't understand why !!!! Laurent
  */
  check( xsh_wavemap_list_compute_poly( vlambda, vslit, pos_x, pos_y, idx,
                                   vorder, dispsol_par, wm_list));
#endif
  sprintf(wm_name,"%s.fits",wm_tag);
  check( result = xsh_wavemap_list_save_poly( wm_list, order_tab_frame,
    pre, instrument, wm_tag, dispersol_frame, slitmap_frame));
  if ( pre != NULL){
     //check( xsh_add_temporary_file( wm_name));
    check( cpl_frame_set_tag( result,wm_tag)); 
    check( cpl_frame_set_tag( *slitmap_frame,
      XSH_GET_TAG_FROM_ARM( XSH_SLIT_MAP_POLY, instrument)));
  }
  cleanup:

    XSH_FREE( vorder);
    XSH_FREE( vlambda);
    XSH_FREE( vslit);
    XSH_FREE( pos_x);
    XSH_FREE( pos_y);
    xsh_pre_free( &pre);
    xsh_spectralformat_list_free( &spec_list);
    xsh_wavesol_free( &wave_tab_2d);
    xsh_wavemap_list_free( &wm_list);
    return result;
}



cpl_frame* 
xsh_create_dispersol_physmod(cpl_frame *pre_frame,
			     cpl_frame *order_tab_frame,
                             cpl_frame* mod_cfg_frame,
			     cpl_frame* wave_map_frame,
			     cpl_frame* slit_map_frame,
                             xsh_dispersol_param *dispsol_param,
                             cpl_frame* spectral_format_frame,
                             xsh_instrument* instrument,
                             const int clean_tmp)
{


  xsh_pre *pre = NULL;
  cpl_frame* disp_frame=NULL;
  xsh_spectralformat_list *spec_list = NULL;
  //int binx=0;
  //int biny=0 ;
  xsh_xs_3 model_config;
  int slit_size=0;
  int slit_step=1.5;


  //float sol_min_lambda=0, sol_max_lambda=0;
  float sol_min_slit=0, sol_max_slit=0;
  int sol_min_order=0, sol_max_order=0;
  cpl_image* wmap_ima=NULL;
  cpl_image* smap_ima=NULL;
  cpl_frame* loc_slit_map_frame=NULL;
  const char* name=NULL;
  int size=0;
  int i=0;
  int idx=0;
  float lambda_step=0.1;

  double *vlambda = NULL, *vslit = NULL, *vorder = NULL;
  double *pos_x = NULL, *pos_y = NULL;
  double j=0;
  double k=0;

  xsh_wavemap_list *wm_list = NULL ;
  cpl_frame* result=NULL;
  char wm_tag[256];
  char wm_name[256];

  /* Check input parameters */
  XSH_ASSURE_NOT_NULL_MSG(mod_cfg_frame,"Null model cfg frame!");
  XSH_ASSURE_NOT_NULL_MSG(spectral_format_frame,"Null spectral format frame!");
  XSH_ASSURE_NOT_NULL_MSG( instrument,"Null instrument setting!");

  if (pre_frame != NULL){
    check( pre = xsh_pre_load( pre_frame, instrument));
  }

  /* Get binning (used by wavesol) */
  //check( binx = xsh_instrument_get_binx( instrument )) ;
  //check( biny = xsh_instrument_get_biny( instrument )) ;
  check(spec_list=xsh_spectralformat_list_load(spectral_format_frame,
					       instrument));
  check( xsh_model_config_load_best( mod_cfg_frame, &model_config));
  check( xsh_model_binxy( &model_config, instrument->binx, 
    instrument->biny));

  name=cpl_frame_get_filename(wave_map_frame);
  wmap_ima=cpl_image_load(name,CPL_TYPE_FLOAT,0,0);

  name=cpl_frame_get_filename(slit_map_frame);
  smap_ima=cpl_image_load(name,CPL_TYPE_FLOAT,0,0);


  //sol_min_lambda = cpl_image_get_min(wmap_ima);
  //sol_max_lambda = cpl_image_get_max(wmap_ima);
  xsh_free_image(&wmap_ima);

  sol_min_slit = cpl_image_get_min(smap_ima);
  sol_max_slit = cpl_image_get_max(smap_ima);
  xsh_free_image(&smap_ima);

  /* evaluate size of grid points */
  slit_size = (int)((sol_max_slit-sol_min_slit)/slit_step)+1;

  size=0;
  for( i=0; i< spec_list->size; i++){
    float lambda_min, lambda_max;
    int lambda_size;

    sol_min_order = (sol_min_order > spec_list->list[i].absorder) ? spec_list->list[i].absorder : sol_min_order;
    sol_max_order = (sol_max_order < spec_list->list[i].absorder) ? spec_list->list[i].absorder : sol_max_order;

    lambda_min = spec_list->list[i].lambda_min_full;
    lambda_max = spec_list->list[i].lambda_max_full;
    XSH_ASSURE_NOT_ILLEGAL_MSG(lambda_max>= lambda_min, 
                              "lambda_max< lambda_min!! Check input spectralformat table, columns WLMAXFUL and WLMINFUL");
    lambda_size = (int)((lambda_max-lambda_min)/lambda_step)+1;
    size += lambda_size*slit_size;
  }
  xsh_msg_dbg_medium( "size %d", size ) ;


  /* As in poly mode fill vector with positions */
  /* Here we could directly use wave and slit maps, as we know results */
  idx = 0;
  XSH_MALLOC( vorder, double, size);
  XSH_MALLOC( vlambda, double, size);
  XSH_MALLOC( vslit, double, size);
  XSH_MALLOC( pos_x, double, size);
  XSH_MALLOC( pos_y, double, size);

  for( i=0; i< spec_list->size; i++){
    double absorder;
    float lambda_min, lambda_max;
    int morder=0;

    absorder= (double)spec_list->list[i].absorder;
    morder= spec_list->list[i].absorder;
    lambda_min = spec_list->list[i].lambda_min_full;
    lambda_max = spec_list->list[i].lambda_max_full;
    xsh_msg("order %f lambda %f-%f",absorder, lambda_min, lambda_max);

    for(j=lambda_min; j <= lambda_max; j+=lambda_step){
      for( k=sol_min_slit; k <= sol_max_slit; k+=slit_step){
        double x, y;
        xsh_model_get_xy(&model_config,instrument,j,morder,k,&x,&y);
        vorder[idx] = absorder;
        vlambda[idx] = j;
        vslit[idx] = k;
        pos_x[idx] = x;
        pos_y[idx] = y;
        idx++;
      }
    }
    xsh_msg_dbg_medium( "i %d idx %d / %d", i, idx, size);
  }

  if ( xsh_debug_level_get() >= XSH_DEBUG_LEVEL_MEDIUM){
    FILE *test = NULL;
    int itest;

    test = fopen( "wavemap_grid.log", "w");
    for( itest=0; itest < size; itest++){
      fprintf(test, "%f %f\n", pos_x[itest], pos_y[itest]);
    }
    fclose(test);
  }

  /* generates wave list in order to have later the dispersion relation */
  check( wm_list = xsh_wavemap_list_create( instrument));

  check( xsh_wavemap_list_compute_poly( vlambda, vslit, pos_x, pos_y, idx,
                                   vorder, dispsol_param, wm_list));

  sprintf(wm_tag,"WAVE_MAP_POLY_%s",xsh_instrument_arm_tostring(instrument));
  sprintf(wm_name,"%s.fits",wm_tag);
  check( result = xsh_wavemap_list_save_poly( wm_list, order_tab_frame,
					      pre, instrument, wm_tag, 
					      &disp_frame,&loc_slit_map_frame));

  if(clean_tmp) {
     xsh_add_temporary_file(wm_name);
  }
  /* This generate recipe crash
  if ( pre != NULL){
    check( xsh_add_temporary_file( wm_name));
    check( cpl_frame_set_tag( result,wm_tag)); 
    check( cpl_frame_set_tag( &slit_map_frame,
      XSH_GET_TAG_FROM_ARM( XSH_SLIT_MAP_POLY, instrument)));
  }
  */


 cleanup:
  xsh_free_image(&wmap_ima);  
  xsh_free_image(&smap_ima);
  xsh_free_frame(&result);
  xsh_free_frame(&loc_slit_map_frame);

  XSH_FREE( vorder);
  XSH_FREE( vlambda);
  XSH_FREE( vslit);
  XSH_FREE( pos_x);
  XSH_FREE( pos_y);
  xsh_pre_free( &pre);
  xsh_spectralformat_list_free( &spec_list);
  xsh_wavemap_list_free( &wm_list);
  return disp_frame;
}


/*---------------------------------------------------------------------------*/
/**
  @brief
    Monitor Flux level along the orders traces given by an input table
  @param frm_map
    The input frame image whose flux is to be monitored
  @param frm_tab
    input frame table with order traces

  @return
    The input frame image FITS header is updated with flux level QC parameters 
*/
/*---------------------------------------------------------------------------*/

cpl_error_code
xsh_wavemap_qc(cpl_frame* frm_map,const cpl_frame* frm_tab)
{
  int sx=0;
  //int sy=0;
  double* cx=NULL;
  double* cy=NULL;
  int wx=0;
  int wy=0;
  cpl_image* ima=NULL;  
  const char* name_tab=NULL;
  const char* name_map=NULL;
  char qc_wlen[40];
  double* pima=NULL;
  cpl_propertylist* plist=NULL;
  cpl_table* tab=NULL;
  cpl_table* ext=NULL;

  double wlen=0;
  int ord_min=0;
  int ord_max=0;
  int i=0;
  int next=0;

  XSH_ASSURE_NOT_NULL(frm_map);
  XSH_ASSURE_NOT_NULL(frm_tab);
  check(name_tab=cpl_frame_get_filename(frm_tab));
  check(tab=cpl_table_load(name_tab,2,0));
  check(ord_min=cpl_table_get_column_min(tab,"ABSORDER"));
  check(ord_max=cpl_table_get_column_max(tab,"ABSORDER"));


  name_map=cpl_frame_get_filename(frm_map);
  ima=cpl_image_load(name_map,CPL_TYPE_DOUBLE,0,0);
  pima=cpl_image_get_data_double(ima);
  sx=cpl_image_get_size_x(ima);
  //sy=cpl_image_get_size_y(ima);
  plist=cpl_propertylist_load(name_map,0);
  for(i=ord_min;i<=ord_max;i++) {
    next=cpl_table_and_selected_int(tab,"ABSORDER",CPL_EQUAL_TO,i);;
    ext=cpl_table_extract_selected(tab);
    cx=cpl_table_get_data_double(ext,"CENTER_X");
    cy=cpl_table_get_data_double(ext,"CENTER_Y");
    wx=cx[next/2];
    wy=cy[next/2];
    wlen=pima[wx+sx*wy];
    sprintf(qc_wlen,"%s%d",XSH_QC_WMAP_WAVEC,i);
    cpl_propertylist_append_double(plist,qc_wlen,wlen);
    xsh_free_table(&ext);
    cpl_table_select_all(tab);

  }

  check(xsh_update_pheader_in_image_multi(frm_map,plist));

 cleanup:
  xsh_free_image(&ima);
  xsh_free_table(&tab);
  xsh_free_table(&ext);
  xsh_free_propertylist(&plist);
  return cpl_error_get_code();
}


/*---------------------------------------------------------------------------*/
/**
  @brief
    Monitor min/max/med/avg distance between detected lines on each ordee
  @param frm_tab
    input frame table with order traces
 @param is_poly
    data reduction method

  @return
    The input frame image FITS header is updated with flux level QC parameters 
*/
/*---------------------------------------------------------------------------*/

cpl_error_code
xsh_wavetab_qc(cpl_frame* frm_tab, const int is_poly)
{

  cpl_table* ext=NULL;
  const char* name_tab=NULL;
  cpl_table* tab=NULL;
  cpl_table* tbl=NULL;
  int nsel=0;
  int ord_min=0;
  int ord_max=0;
  int i=0;
  int j=0;
  cpl_vector* loc_vec=NULL;
  double* pvec=NULL;
  double* pvec_all_ord=NULL;
  double* py=NULL;
  double ymin=0;
  double ymax=0;
  double ymin_all=FLT_MAX;
  double ymax_all=FLT_MIN;

  double ymed=0;
  double yavg=0;
  char qc_line[40];
  cpl_propertylist* plist=NULL;
  cpl_vector* vec_all_ord=NULL;
  int ymin_all_ord=0;
  int ymax_all_ord=0;

  XSH_ASSURE_NOT_NULL(frm_tab);

  check(name_tab=cpl_frame_get_filename(frm_tab));
  check(tab=cpl_table_load(name_tab,1,0));
  check(ord_min=cpl_table_get_column_min(tab,"Order"));
  check(ord_max=cpl_table_get_column_max(tab,"Order"));
  check(plist=cpl_propertylist_load(name_tab,0));

  check(nsel=cpl_table_and_selected_int(tab,"Slit_index",CPL_EQUAL_TO,4));
  check(ext=cpl_table_extract_selected(tab));

  check(vec_all_ord=cpl_vector_new(ord_max-ord_min+1));
  check(pvec_all_ord=cpl_vector_get_data(vec_all_ord));

  for(i=ord_min;i<=ord_max;i++) {
     check(nsel=cpl_table_and_selected_int(ext,"Order",CPL_EQUAL_TO,i));
     check(tbl=cpl_table_extract_selected(ext));

     if(nsel>1) {
        check(loc_vec=cpl_vector_new(nsel-1));
        check(pvec=cpl_vector_get_data(loc_vec));

        if(is_poly) {
           py=cpl_table_get_data_double(tbl,"Ypoly");
        } else {
           py=cpl_table_get_data_double(tbl,"Ythanneal");
        }

        for(j=0;j<nsel-1;j++) {
           pvec[j]=fabs(py[j+1]-py[j]);

           if(pvec[j]>ymax_all) {
              ymax_all=pvec[j];
              ymax_all_ord=i;
           }
           if(pvec[j]<ymin_all) {
              ymin_all=pvec[j];
              ymin_all_ord=i;
           }


        }

        check(ymin=cpl_vector_get_min(loc_vec));
        check(ymax=cpl_vector_get_max(loc_vec));
        check(ymed=cpl_vector_get_median(loc_vec));
        check(yavg=cpl_vector_get_mean(loc_vec));
        check(pvec_all_ord[i-ord_min]=yavg);


        //xsh_msg("ymin=%g ymax=%g ymed=%g yavg=%g",ymin,ymax,ymed,yavg);
        sprintf(qc_line,"%s%d",XSH_QC_LINE_DIFMIN,i);
        check(cpl_propertylist_append_double(plist,qc_line,ymin));
        check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFMIN_C));
  
        sprintf(qc_line,"%s%d",XSH_QC_LINE_DIFMAX,i);
        check(cpl_propertylist_append_double(plist,qc_line,ymax));
        check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFMAX_C));
  
        sprintf(qc_line,"%s%d",XSH_QC_LINE_DIFMED,i);
        check(cpl_propertylist_append_double(plist,qc_line,ymed));
        check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFMED_C));
  
        sprintf(qc_line,"%s%d",XSH_QC_LINE_DIFAVG,i);
        check(cpl_propertylist_append_double(plist,qc_line,yavg));
        check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFAVG_C));

        xsh_free_vector(&loc_vec);
     } else {
        xsh_msg_warning("Too few values of 'Slit_index=4' for Order=%d",i);
        xsh_msg_warning("No %s (and similar) QC parameters can be generated for order %d",XSH_QC_LINE_DIFMIN,i);
     }
     check(cpl_table_select_all(ext));
     xsh_free_table(&tbl);
  }
  check(yavg=cpl_vector_get_mean(vec_all_ord));
  check(ymax=cpl_vector_get_max(vec_all_ord));
  check(ymed=cpl_vector_get_median(vec_all_ord));
  check(ymin=cpl_vector_get_min(vec_all_ord));



  sprintf(qc_line,"%s",XSH_QC_LINE_DIFMIN);
  check(cpl_propertylist_append_double(plist,qc_line,ymin_all));
  check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFMIN_C));

  sprintf(qc_line,"%s",XSH_QC_LINE_DIFMIN_ORD);
  check(cpl_propertylist_append_int(plist,qc_line,ymin_all_ord));
  check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFMIN_C));

  sprintf(qc_line,"%s",XSH_QC_LINE_DIFMAX);
  check(cpl_propertylist_append_double(plist,qc_line,ymax_all));
  check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFMAX_C));

  sprintf(qc_line,"%s",XSH_QC_LINE_DIFMAX_ORD);
  check(cpl_propertylist_append_int(plist,qc_line,ymax_all_ord));
  check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFMAX_C));

  sprintf(qc_line,"%s",XSH_QC_LINE_DIFMED);
  check(cpl_propertylist_append_double(plist,qc_line,ymed));
  check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFMED_C));

  sprintf(qc_line,"%s",XSH_QC_LINE_DIFAVG);
  check(cpl_propertylist_append_double(plist,qc_line,yavg));
  check(cpl_propertylist_set_comment(plist,qc_line,XSH_QC_LINE_DIFAVG_C));

  check(cpl_table_select_all(tab));
  check(cpl_table_save(tab, plist, NULL,name_tab, CPL_IO_DEFAULT));

 cleanup:
  xsh_free_table(&tab);
  xsh_free_table(&ext);
  xsh_free_table(&tbl);
  xsh_free_vector(&loc_vec);
  xsh_free_vector(&vec_all_ord);
  xsh_free_propertylist(&plist);


  return cpl_error_get_code();

}

/**@}*/
