/*                                                                           *
 *   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: 2013-03-01 17:03:01 $
 * $Revision: 1.67 $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_data_rec Rec
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/

/**@{*/


/*-----------------------------------------------------------------------------
  Includes
  ----------------------------------------------------------------------------*/
#include <string.h>
#include <math.h>
#include <xsh_utils_table.h>
#include <xsh_data_rec.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <xsh_dfs.h>
#include <cpl.h>

/*----------------------------------------------------------------------------
  Function implementation
  ----------------------------------------------------------------------------*/
/** 
 * Dump main info about a rec table (for each order of the list)
 * 
 * @param list pointer to table list
 * @param fname File name
 */
void xsh_rec_list_dump( xsh_rec_list * list,
			const char * fname )
{
  int i ;
  FILE * fout = NULL;

  XSH_ASSURE_NOT_NULL( list) ;
  if ( fname == NULL ) {
    fout = stdout;
  }
  else {
    fout = fopen( fname, "w" ) ;
  }
  XSH_ASSURE_NOT_NULL( fout ) ;

  fprintf( fout, "Rec List. Nb of orders: %d\n", list->size ) ;
  for( i = 0 ; i<list->size ; i++ ) {
    fprintf( fout,  " Entry %2d: Order %d, Nlambda: %d, Nslit: %d\n",
	     i, list->list[i].order, list->list[i].nlambda,
	     list->list[i].nslit ) ;
  }

 cleanup:
  if ( fname != NULL && fout != NULL ) fclose( fout ) ;
  return ;
}


/*---------------------------------------------------------------------------*/
/**
  @brief
    Create an empty order list
  @param[in] instr
    The instrument in use
  @return
    The order list structure
*/
/*---------------------------------------------------------------------------*/
xsh_rec_list* xsh_rec_list_create(xsh_instrument* instr)
{
  xsh_rec_list *result = NULL;
  XSH_ARM xsh_arm;
  int size;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL( instr);

  /* Get arm */
  xsh_arm = xsh_instrument_get_arm( instr) ;

  /* Special necessary for tests */
  if ( instr->config != NULL )
    size = instr->config->orders;
  else if( xsh_arm == XSH_ARM_UVB){
    size = XSH_REC_TABLE_NB_UVB_ORDERS;
  }
  else if ( xsh_arm == XSH_ARM_VIS){
    size = XSH_REC_TABLE_NB_VIS_ORDERS;
  }
  else if ( xsh_arm == XSH_ARM_NIR){
    size = XSH_REC_TABLE_NB_NIR_ORDERS;
  }
  else {
    size = 0;
  }
  check(result =  xsh_rec_list_create_with_size( size, instr));

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_rec_list_free( &result);
    }  
    return result;
}

/*---------------------------------------------------------------------------*/
/**
  @brief
    Create an empty order list
  @param[in] size size of list structure
  @param[in] instr
    The instrument in use
  @return
    The order list structure
*/
/*---------------------------------------------------------------------------*/
xsh_rec_list* xsh_rec_list_create_with_size(int size, xsh_instrument* instr)
{
  xsh_rec_list* result = NULL;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL( instr);
  XSH_ASSURE_NOT_ILLEGAL( size > 0);

  /* Allocate memory */
  XSH_CALLOC(result, xsh_rec_list, 1);

  /* Special necessary for tests */
  result->size = size;

  XSH_ASSURE_NOT_ILLEGAL(result->size > 0);
  result->instrument = instr;
  XSH_CALLOC( result->list, xsh_rec, result->size);
  XSH_NEW_PROPERTYLIST( result->header);
  result->slit_min = 0.0;
  result->slit_max = 0.0;
  result->nslit = 0;

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_rec_list_free( &result);
    }
    return result;
}
/*---------------------------------------------------------------------------*/
/**
  @brief
    Allocate memory for the order idx of the rectify list
  @param[in] list
    The rectify list
  @param[in] idx
    The index of the order in the list
  @param[in] absorder
    The absorder of the order
  @param[in] nlambda
    The number of lambda values
  @param[in] ns
    The number of slit values
*/
/*---------------------------------------------------------------------------*/
void xsh_rec_list_set_data_size( xsh_rec_list * list, int idx, int absorder,
				 int nlambda, int ns )
{
  xsh_rec * prec = NULL ;
  int depth ;

  XSH_ASSURE_NOT_NULL( list) ;
  XSH_ASSURE_NOT_ILLEGAL( idx < list->size);
  XSH_CMP_INT( idx, >=, 0, "Index not in range",);
  XSH_CMP_INT( idx, <, list->size, "Index not in range",); 
  XSH_CMP_INT( ns, >, 0, "Check size in slit",);
  XSH_CMP_INT( nlambda, >, 0, "Check size in lambda",);

  prec = &list->list[idx] ;
  XSH_ASSURE_NOT_NULL( prec);

  //if ( list->max_nlambda < nlambda ) list->max_nlambda = nlambda ;
  //if ( list->max_nslit < ns ) list->max_nslit = ns;
  depth = nlambda*ns;

  prec->order = absorder;
  prec->nlambda = nlambda;
  prec->nslit = ns;
  xsh_msg_dbg_high( "Rec Data Size: nlambda: %d, ns: %d, depth: %d",
		    nlambda, ns, depth);
  XSH_CALLOC( prec->slit, float, ns) ;
  XSH_CALLOC( prec->lambda, double, nlambda);
  XSH_CALLOC( prec->data1, float, depth);
  XSH_CALLOC( prec->errs1, float, depth);
  XSH_CALLOC( prec->qual1, int, depth);

 cleanup:
  return ;
}

xsh_rec_list * xsh_rec_list_duplicate( xsh_rec_list * old,
				       xsh_instrument * instrument )
{
  xsh_rec_list * new = NULL ;
  int nb_orders, order ;

  check( new = xsh_rec_list_create( instrument ) ) ;

  /* Loop over orders */
  nb_orders = old->size ;
  for( order = 0 ; order < nb_orders ; order++ ) {
    int absorder, nslit, nlambda ;
    float * fold, * fnew ;
    int * iold, * inew ;
    double * dold, * dnew ;

    absorder = xsh_rec_list_get_order( old, order ) ;
    nslit = xsh_rec_list_get_nslit( old, order ) ;
    nlambda = xsh_rec_list_get_nlambda( old, order ) ;

    check( xsh_rec_list_set_data_size( new, order, absorder, nlambda, nslit ));

    /* Now copy data, errs and qual */
    fold = xsh_rec_list_get_data1( old, order ) ;
    fnew = xsh_rec_list_get_data1( new, order ) ;
    memcpy( fnew, fold, nlambda*nslit*sizeof( float ) ) ;

    fold = xsh_rec_list_get_errs1( old, order ) ;
    fnew = xsh_rec_list_get_errs1( new, order ) ;
    memcpy( fnew, fold, nlambda*nslit*sizeof( float ) ) ;

    iold = xsh_rec_list_get_qual1( old, order ) ;
    inew = xsh_rec_list_get_qual1( new, order ) ;
    memcpy( inew, iold, nlambda*nslit*sizeof( int ) ) ;

    fold = xsh_rec_list_get_slit( old, order ) ;
    fnew = xsh_rec_list_get_slit( new, order ) ;
    memcpy( fnew, fold, nslit*sizeof( float ) ) ;

    dold = xsh_rec_list_get_lambda( old, order ) ;
    dnew = xsh_rec_list_get_lambda( new, order ) ;
    memcpy( dnew, dold, nlambda*sizeof( double ) ) ;
  }

  xsh_free_propertylist(&(new->header));
  new->header = cpl_propertylist_duplicate( old->header ) ;
  new->instrument = xsh_instrument_duplicate( old->instrument ) ;

 cleanup:
  return new ;
}

/*---------------------------------------------------------------------------*/
/**
   @brief load an rec list from a frame
   @param frame the table wich contains polynomials coefficients of the 
   orders
   @param instrument instrument in use
   @return the rec list structure

*/
/*---------------------------------------------------------------------------*/

xsh_rec_list* xsh_rec_list_load(cpl_frame* frame,
				xsh_instrument* instrument)
{
  xsh_rec_list* result = NULL;
  const char * tablename = NULL ;
  cpl_table * table = NULL; 
  cpl_propertylist* header = NULL;
  int nbext, i ;
  int size;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL(frame);
  XSH_ASSURE_NOT_NULL(instrument);

  /* get table filename */
  check(tablename = cpl_frame_get_filename(frame));
  xsh_msg_dbg_low( "Loading Rectified Frame: %s", tablename ) ;
  
  check( size = cpl_frame_get_nextensions( frame));

  /* Create internal structure */
  /* Allocate memory */
  XSH_CALLOC(result, xsh_rec_list, 1);
  result->size = size;  
  XSH_ASSURE_NOT_ILLEGAL(result->size > 0);
  result->instrument = instrument;

  XSH_CALLOC( result->list, xsh_rec, result->size);

  XSH_NEW_PROPERTYLIST( result->header);

  check(header = cpl_propertylist_load(tablename,0));
  check(cpl_propertylist_erase_regexp(header,
                        "^(ARCFILE|ORIGFILE|CHECKSUM|DATASUM)$", CPL_FALSE));
  check(cpl_propertylist_append(result->header, header));
  //xsh_msg("tablename=%s",tablename);
  //check(result->slit_min=xsh_pfits_get_extract_slit_min(header));
  //check(result->slit_max=xsh_pfits_get_extract_slit_max(header));

  xsh_free_propertylist(&header);
  
  nbext = size;

  xsh_msg_dbg_medium( "   Nb of extensions: %d", nbext ) ;
  /* Loop over FITS extensions */
  for( i = 0 ; i<nbext ; i++ ) {
    int nb, k, order, depth, nlambda, nslit ;
    const cpl_array * data_array ;
    const float * farray = NULL ;
    const int * iarray = NULL ;

    check( table = cpl_table_load( tablename, i+1, 0 ) ) ;
    check(xsh_get_table_value(table, XSH_REC_TABLE_COLNAME_ORDER,
			      CPL_TYPE_INT, 0, &order));
    /* Now populate the structure */
    result->list[i].order = order ;
    check(xsh_get_table_value(table, XSH_REC_TABLE_COLNAME_NLAMBDA,
			      CPL_TYPE_INT, 0, &nlambda ));
    result->list[i].nlambda = nlambda ;
    check(xsh_get_table_value(table, XSH_REC_TABLE_COLNAME_NSLIT,
			      CPL_TYPE_INT, 0, &nslit ));
    result->list[i].nslit = nslit ;
    xsh_rec_list_set_data_size( result, i, order, nlambda, nslit ) ;
    /* Get arrays (slit, lambda) */
    if (nslit > 1){
    check( xsh_table_get_array_float( table, XSH_REC_TABLE_COLNAME_SLIT,
      result->list[i].slit, nslit));
    }
    else{
      check(xsh_get_table_value(table, XSH_REC_TABLE_COLNAME_SLIT,
                              CPL_TYPE_FLOAT, 0, result->list[i].slit ));
    }
    check( xsh_table_get_array_double( table, XSH_REC_TABLE_COLNAME_LAMBDA,
      result->list[i].lambda, nlambda));
    
    /* Get arrays (flux, errs, qual ) */
    depth = nlambda*nslit ;

    check( data_array = cpl_table_get_array( table,
					     XSH_REC_TABLE_COLNAME_FLUX1,
					     0 )) ;
    nb = cpl_array_get_size( data_array ) ;
    XSH_ASSURE_NOT_ILLEGAL( nb == depth ) ;

    /* Copy FLUX1 data to rec_list */
    check( farray = cpl_array_get_data_float_const( data_array ) ) ;
    for( k = 0 ; k<depth ; k++ ) *(result->list[i].data1+k) = *farray++ ;

    farray = NULL ;
    check( data_array = cpl_table_get_array( table,
					     XSH_REC_TABLE_COLNAME_ERRS1,
					     0 )) ;
    check( nb = cpl_array_get_size( data_array ) ) ;
    /* Copy ERRS1 data to rec_list */
    check( farray = cpl_array_get_data_float_const( data_array ) ) ;
    for( k = 0 ; k<depth ; k++ ) *(result->list[i].errs1+k) = *farray++ ;

    check( data_array = cpl_table_get_array( table,
					     XSH_REC_TABLE_COLNAME_QUAL1,
					     0 )) ;
    nb = cpl_array_get_size( data_array ) ;
    /* Copy QUAL1 data to rec_list */
    iarray = cpl_array_get_data_int_const( data_array ) ;
    for( k = 0 ; k<depth ; k++ ) *(result->list[i].qual1+k) = *iarray++ ;

    xsh_msg_dbg_low( "   Loaded, order %d, nlambda %d, nslit %d",
		     order, nlambda, nslit ) ;

    cpl_table_delete( table ) ;
  }

 cleanup:
  xsh_free_propertylist(&header);
  return result ;
}



xsh_rec_list* xsh_rec_list_load_eso(cpl_frame* frame,
				    xsh_instrument* instrument)
{
  xsh_rec_list* result = NULL;
  const char * imagelist_name = NULL ;
  cpl_image * ima_data = NULL; 
  cpl_image * ima_errs = NULL; 
  cpl_image * ima_qual = NULL; 
  cpl_propertylist* header = NULL;
  cpl_propertylist* hdata=NULL;
  cpl_propertylist* herrs=NULL;
  cpl_propertylist* hqual=NULL;
  const char* ext_name=NULL;

  int nbext, i,j ;
  int size;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL(frame);
  XSH_ASSURE_NOT_NULL(instrument);


  /* get table filename */
  check(imagelist_name = cpl_frame_get_filename(frame));
  xsh_msg_dbg_low( "Loading Rectified Frame: %s", imagelist_name ) ;
  
  check( nbext = cpl_frame_get_nextensions( frame));
 

  size=(nbext+1)/3;
  
  /* Create internal structure */
  /* Allocate memory */
  XSH_CALLOC(result, xsh_rec_list, 1);
  result->size = size;  
  XSH_ASSURE_NOT_ILLEGAL(result->size > 0);
  result->instrument = instrument;

  XSH_CALLOC( result->list, xsh_rec, result->size);

  XSH_NEW_PROPERTYLIST( result->header);


  check(header = cpl_propertylist_load(imagelist_name,0));
  check(cpl_propertylist_erase_regexp(header,
                        "^(ARCFILE|ORIGFILE|CHECKSUM|DATASUM)$", CPL_FALSE));
  check(cpl_propertylist_append(result->header, header));

  xsh_free_propertylist(&header);
  
  //nbext = size;

  xsh_msg_dbg_medium( "   Nb of extensions: %d", nbext ) ;
  /* Loop over FITS extensions */

  for( i = 0,j=0 ; j<nbext ; i++, j+=3 ) {
    int k, order, depth, nlambda, nslit ;
    const float * farray = NULL ;
    const int * iarray = NULL ;
    double s_step=0;
    double w_step=0;
    double s_start=0;
    double w_start=0;
    int sx=0;
    int sy=0;
  
    check(ima_data=cpl_image_load(imagelist_name,XSH_PRE_DATA_TYPE,0,j+0));
    check(ima_errs=cpl_image_load(imagelist_name,XSH_PRE_ERRS_TYPE,0,j+1));
    check(ima_qual=cpl_image_load(imagelist_name,XSH_PRE_QUAL_TYPE,0,j+2));

    check( hdata = cpl_propertylist_load( imagelist_name, j+0 ) ) ;
    check( herrs = cpl_propertylist_load( imagelist_name, j+1 ) ) ;
    check( hqual = cpl_propertylist_load( imagelist_name, j+2 ) ) ;
    check(ext_name=xsh_pfits_get_extname(herrs));
    order=10*(ext_name[3]-48)+(ext_name[4]-48);
    //xsh_msg("iter=%d order=%d",i,order);
 
    /* Now populate the structure */
    result->list[i].order = order ;

    nlambda=xsh_pfits_get_naxis1(herrs);
    nslit=xsh_pfits_get_naxis2(herrs);

    w_step=xsh_pfits_get_cdelt1(herrs);
    s_step=xsh_pfits_get_cdelt2(herrs);

    w_start=xsh_pfits_get_crval1(herrs);
    s_start=xsh_pfits_get_crval2(herrs);

    result->list[i].nlambda = nlambda ;
    result->list[i].nslit = nslit ;
    
    xsh_rec_list_set_data_size( result, i, order, nlambda, nslit ) ;
  
    /* Get arrays (slit, lambda) */
    if (nslit > 1){
      for(k=0;k<nslit;k++) {
	*(result->list[i].slit+k)=(s_start+k*s_step);
      }
    }
    else{
      for(k=0;k<nslit;k++) {
	*(result->list[i].slit+k)=0;
      }
    }
  

    for(k=0;k<nlambda;k++) {
      *(result->list[i].lambda+k)=(w_start+k*w_step);
    }
    /* Get arrays (flux, errs, qual ) */
    depth = nlambda*nslit ;
 
    /* Copy FLUX1 data to rec_list */
    check( farray = cpl_image_get_data_float( ima_data)) ;
    sx = cpl_image_get_size_x( ima_data ) ;
    sy = cpl_image_get_size_y( ima_data ) ;
    XSH_ASSURE_NOT_ILLEGAL( sx*sy == depth ) ;
 
    for( k = 0 ; k<depth ; k++ ) *(result->list[i].data1+k) = *farray++ ;
    farray = NULL ;

    /* Copy ERRS1 data to rec_list */
    check( farray = cpl_image_get_data_float( ima_errs)) ;
    for( k = 0 ; k<depth ; k++ ) *(result->list[i].errs1+k) = *farray++ ;

    /* Copy QUAL1 data to rec_list */
    check( iarray = cpl_image_get_data_int( ima_qual)) ;
    for( k = 0 ; k<depth ; k++ ) *(result->list[i].qual1+k) = *iarray++ ;

    xsh_msg_dbg_low( "   Loaded, order %d, nlambda %d, nslit %d",
		     order, nlambda, nslit ) ;
 
    xsh_free_image( &ima_data ) ;
    xsh_free_image( &ima_errs ) ;
    xsh_free_image( &ima_qual ) ;
    xsh_free_propertylist( &hdata ) ;
    xsh_free_propertylist( &herrs ) ;
    xsh_free_propertylist( &hqual ) ;

  }

 cleanup:
  xsh_free_image( &ima_data ) ;
  xsh_free_image( &ima_errs ) ;
  xsh_free_image( &ima_qual ) ;
  xsh_free_propertylist( &hdata ) ;
  xsh_free_propertylist( &herrs ) ;
  xsh_free_propertylist( &hqual ) ;

  xsh_free_propertylist(&header);

  return result ;
}


xsh_rec_list* xsh_rec_list_load_eso_1d(cpl_frame* frame,
                    xsh_instrument* instrument)
{
  xsh_rec_list* result = NULL;
  const char * imagelist_name = NULL ;
  cpl_vector * vec_data = NULL;
  cpl_vector * vec_errs = NULL;
  cpl_vector * vec_qual = NULL;
  cpl_propertylist* header = NULL;
  cpl_propertylist* hdata=NULL;
  cpl_propertylist* herrs=NULL;
  cpl_propertylist* hqual=NULL;
  const char* ext_name=NULL;

  int nbext, i,j ;
  int size;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL(frame);
  XSH_ASSURE_NOT_NULL(instrument);


  /* get table filename */
  check(imagelist_name = cpl_frame_get_filename(frame));
  xsh_msg_dbg_low( "Loading Rectified Frame: %s", imagelist_name ) ;

  check( nbext = cpl_frame_get_nextensions( frame));


  size=(nbext+1)/3;

  /* Create internal structure */
  /* Allocate memory */
  XSH_CALLOC(result, xsh_rec_list, 1);
  result->size = size;
  XSH_ASSURE_NOT_ILLEGAL(result->size > 0);
  result->instrument = instrument;

  XSH_CALLOC( result->list, xsh_rec, result->size);

  XSH_NEW_PROPERTYLIST( result->header);


  check(header = cpl_propertylist_load(imagelist_name,0));
  check(cpl_propertylist_erase_regexp(header,
                        "^(ARCFILE|ORIGFILE|CHECKSUM|DATASUM)$", CPL_FALSE));
  check(cpl_propertylist_append(result->header, header));

  xsh_free_propertylist(&header);

  //nbext = size;

  xsh_msg_dbg_medium( "   Nb of extensions: %d", nbext ) ;
  /* Loop over FITS extensions */

  for( i = 0,j=0 ; j<nbext ; i++, j+=3 ) {
    int k, order, depth, nlambda;
    const double * farray = NULL ;
    const double * iarray = NULL ;
    double w_step=0;
    double w_start=0;
    int sx=0;

    check(vec_data=cpl_vector_load(imagelist_name,j+0));
    check(vec_errs=cpl_vector_load(imagelist_name,j+1));
    check(vec_qual=cpl_vector_load(imagelist_name,j+2));

    check( hdata = cpl_propertylist_load( imagelist_name, j+0 ) ) ;
    check( herrs = cpl_propertylist_load( imagelist_name, j+1 ) ) ;
    check( hqual = cpl_propertylist_load( imagelist_name, j+2 ) ) ;
    check(ext_name=xsh_pfits_get_extname(herrs));
    order=10*(ext_name[3]-48)+(ext_name[4]-48);
    //xsh_msg("iter=%d order=%d",i,order);

    /* Now populate the structure */
    result->list[i].order = order ;

    nlambda=xsh_pfits_get_naxis1(herrs);

    w_step=xsh_pfits_get_cdelt1(herrs);

    w_start=xsh_pfits_get_crval1(herrs);

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

    xsh_rec_list_set_data_size( result, i, order, nlambda, 1 ) ;

    /* Get arrays (lambda) */
    for(k=0;k<nlambda;k++) {
      *(result->list[i].lambda+k)=(w_start+k*w_step);
    }
    /* Get arrays (flux, errs, qual ) */
    depth = nlambda;

    /* Copy FLUX1 data to rec_list */
    check( farray = cpl_vector_get_data( vec_data)) ;
    sx = cpl_vector_get_size( vec_data ) ;
    XSH_ASSURE_NOT_ILLEGAL( sx == depth ) ;

    for( k = 0 ; k<depth ; k++ ) *(result->list[i].data1+k) = (float)*farray++ ;
    farray = NULL ;

    /* Copy ERRS1 data to rec_list */
    check( farray = cpl_vector_get_data( vec_errs)) ;
    for( k = 0 ; k<depth ; k++ ) *(result->list[i].errs1+k) = (float)*farray++ ;

    /* Copy QUAL1 data to rec_list */
    check( iarray = cpl_vector_get_data( vec_qual)) ;
    for( k = 0 ; k<depth ; k++ ) *(result->list[i].qual1+k) = (int)*iarray++ ;

    xsh_msg_dbg_low( "   Loaded, order %d, nlambda %d",order, nlambda) ;

    xsh_free_vector( &vec_data ) ;
    xsh_free_vector( &vec_errs ) ;
    xsh_free_vector( &vec_qual ) ;
    xsh_free_propertylist( &hdata ) ;
    xsh_free_propertylist( &herrs ) ;
    xsh_free_propertylist( &hqual ) ;

  }

 cleanup:
  xsh_free_vector( &vec_data ) ;
  xsh_free_vector( &vec_errs ) ;
  xsh_free_vector( &vec_qual ) ;
  xsh_free_propertylist( &hdata ) ;
  xsh_free_propertylist( &herrs ) ;
  xsh_free_propertylist( &hqual ) ;

  xsh_free_propertylist(&header);

  return result ;
}


/*---------------------------------------------------------------------------*/
/** 
  @brief 
    Invert the rectified flux images of the input frame into a new
    frame. 
  @param rec_frame 
    In/out rectified frame
  @param tag 
    Output Tag
  @param instrument 
    Pointer to xsh_instrument structure
  @return 
    A new frame with all flux images inverted ( multiplied by -1)
 */
/*---------------------------------------------------------------------------*/
cpl_frame * xsh_rec_list_frame_invert( cpl_frame * rec_frame,
  const char *tag, xsh_instrument *instrument)
{
  cpl_frame *result = NULL;
  xsh_rec_list *rec_list = NULL;
  int norders, order;
  float *data = NULL;
  char fname[256];
  //const char* orig_name = NULL;

  XSH_ASSURE_NOT_NULL( rec_frame);
  XSH_ASSURE_NOT_NULL( tag);
  XSH_ASSURE_NOT_NULL( instrument);

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

  /* Loop over orders */
  norders = rec_list->size;
  for( order = 0; order < norders; order++) {
    int i;
    int img_size, nlambda, nslit;

    check( nlambda = xsh_rec_list_get_nlambda( rec_list, order));
    check( nslit = xsh_rec_list_get_nslit( rec_list, order));
    img_size = nlambda * nslit;

    check( data = xsh_rec_list_get_data1( rec_list, order ) ) ;
    for( i = 0 ; i< img_size ; i++, data++ ) {
      *data *= -1.;
    }
  }

  /* Now save with a new name */
  sprintf( fname,"%s.fits", tag);
  check( result = xsh_rec_list_save( rec_list, fname, tag, 0 ) ) ;

 cleanup:
  xsh_rec_list_free( &rec_list ) ;
  return result ;
}

/*---------------------------------------------------------------------------*/
/**
   @brief free memory associated to a rec_list
   @param list the rec_list to free
*/
/*---------------------------------------------------------------------------*/
void xsh_rec_list_free(xsh_rec_list** list)
{
  if (list != NULL && *list != NULL ) {
    /*
      BUG - Laurent - 09-01-2009
      Something strange occurs (under Linux, not MacOs):
       in the recipe xsh_scired_slit_nod, for UVB (not for VIS), there is
       a crash in this function during one of the cpl_free calls.
      In order to allow continuation of tests, freeing of the rec_list
       is - temporarily - disable until the problem is understood and
       fixed.
    */
    int i ;
    xsh_rec_list * plist = NULL;

    plist = *list;
    /* free the list */

    for (i = 0; i < plist->size; i++) {
      xsh_rec * pr = &(plist->list[i]) ;

      xsh_msg_dbg_high( "Freeing order index %d", i ) ;
      if ( pr == NULL ) continue ;
      xsh_msg_dbg_high( "     Abs Order: %d", pr->order ) ;
      cpl_free( pr->slit ) ;
      cpl_free( pr->lambda ) ;
      cpl_free( pr->data1 ) ;
      cpl_free( pr->errs1 ) ;
      cpl_free( pr->qual1 ) ;
    }
    if ((*list)->list){
      cpl_free((*list)->list);
    }

    xsh_free_propertylist(&((*list)->header));
    cpl_free(*list);
    *list = NULL;
  }
}



/*---------------------------------------------------------------------------*/
/**
   @brief get header of the table
   @param list the rec_list
   @return the header associated to the table
*/
/*---------------------------------------------------------------------------*/
cpl_propertylist * xsh_rec_list_get_header(xsh_rec_list* list)
{
  cpl_propertylist * res = NULL;

  XSH_ASSURE_NOT_NULL(list);
  res = list->header;
 cleanup:
  return res;
}

/** 
 * Return the number of slit from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param idx Index in the list
 * 
 * @return Nb of slit
 */
int xsh_rec_list_get_nslit( xsh_rec_list* list, int idx )
{
  int res = 0 ;

  XSH_ASSURE_NOT_NULL(list);
  res = list->list[idx].nslit ;

 cleanup:
  return res ;
}

/** 
 * Return the order absolute number from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param idx Index in the list
 * 
 * @return Nb of slit
 */
int xsh_rec_list_get_order( xsh_rec_list* list, int idx )
{
  int res = 0 ;

  XSH_ASSURE_NOT_NULL(list);
  res = list->list[idx].order ;

 cleanup:
  return res ;
}

/** 
 * Return the number of lambda from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param idx Index in the list
 * 
 * @return nlambda
 */
int xsh_rec_list_get_nlambda( xsh_rec_list* list, int idx )
{
  int res = 0 ;

  XSH_ASSURE_NOT_NULL( list);
  res = list->list[idx].nlambda ;

 cleanup:
  return res ;
}

/** 
 * Return the slit buffer from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param idx Index in the list
 * 
 * @return Pointer to slit buffer
 */
float * xsh_rec_list_get_slit( xsh_rec_list* list, int idx )
{
  float * res = NULL ;

  XSH_ASSURE_NOT_NULL(list);
  res = list->list[idx].slit ;

 cleanup:
  return res ;
}


/** 
 * Return the slit min from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 *
 * @return Pointer to slit buffer
 */
double xsh_rec_list_get_slit_min( xsh_rec_list* list)
{
  double res = 0 ;

  XSH_ASSURE_NOT_NULL(list);
  res = list->slit_min ;

 cleanup:
  return res ;
}



/** 
 * Return the slit max from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * 
 * @return Pointer to slit buffer
 */
double xsh_rec_list_get_slit_max( xsh_rec_list* list)
{
  double res = 0 ;

  XSH_ASSURE_NOT_NULL(list);
  res = list->slit_max ;

 cleanup:
  return res ;
}


double xsh_rec_list_get_lambda_min( xsh_rec_list* list)
{
  double lambda_min = 10000;
  int i;

  XSH_ASSURE_NOT_NULL( list);

  for( i=0; i< list->size; i++){
    if ( list->list[i].lambda != NULL){
      double lambda;
      
      lambda = list->list[i].lambda[0];
      if ( lambda < lambda_min){
        lambda_min = lambda;
      }
    }
  }

  cleanup:
    return lambda_min;
}

double xsh_rec_list_get_lambda_max( xsh_rec_list* list)
{
  double lambda_max =0.0;
  int i;

  XSH_ASSURE_NOT_NULL( list);

  for( i=0; i< list->size; i++){
    if ( list->list[i].lambda != NULL){
      double lambda;

      lambda = list->list[i].lambda[list->list[i].nlambda-1];
      if ( lambda  > lambda_max){
        lambda_max = lambda;
      }
    }
  }
  cleanup:
    return lambda_max;
}


/** 
 * Set the slit min val in the xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param val slit value to set
 * 
 * @return Pointer to slit buffer
 */
cpl_error_code xsh_rec_list_set_slit_min( xsh_rec_list* list, const double val)
{

  XSH_ASSURE_NOT_NULL(list);
  list->slit_min = val;

 cleanup:
  return cpl_error_get_code() ;
}



/** 
 * Set the slit max val in the xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param val slit value to set
 * 
 * @return Pointer to slit buffer
 */
cpl_error_code xsh_rec_list_set_slit_max( xsh_rec_list* list,const double val)
{

  XSH_ASSURE_NOT_NULL(list);
  list->slit_max = val;

 cleanup:
  return cpl_error_get_code() ;
}


/** 
 * Return the lambda buffer from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param idx Index in the list
 * 
 * @return Pointer to lambda buffer
 */
double* xsh_rec_list_get_lambda( xsh_rec_list* list, int idx )
{
  double* res = NULL ;

  XSH_ASSURE_NOT_NULL(list);
  res = list->list[idx].lambda ;

 cleanup:
  return res ;
}

/** 
 * Return the data1 buffer from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param idx Index in the list
 * 
 * @return Pointer to data1 buffer
 */
float * xsh_rec_list_get_data1( xsh_rec_list* list, int idx )
{
  float * res = NULL ;

  XSH_ASSURE_NOT_NULL(list);
  res = list->list[idx].data1 ;

 cleanup:
  return res ;
}

/** 
 * Return the errs1 buffer from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param idx Index in the list
 * 
 * @return Pointer to errs1 buffer
 */
float * xsh_rec_list_get_errs1( xsh_rec_list* list, int idx )
{
  float * res = NULL ;

  XSH_ASSURE_NOT_NULL(list);
  res = list->list[idx].errs1 ;

 cleanup:
  return res ;
}

/** 
 * Return the qual1 buffer from a xsh_rec_list structure.
 * 
 * @param list Rectify structure pointer
 * @param idx Index in the list
 * 
 * @return Pointer to qual1 buffer
 */
int * xsh_rec_list_get_qual1( xsh_rec_list* list, int idx )
{
  int * res = NULL ;

  XSH_ASSURE_NOT_NULL(list);
  res = list->list[idx].qual1 ;

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

/*---------------------------------------------------------------------------*/
/**
  @brief
    Save a rec list in a frame
  @param list 
    The rec_list structure wich contains polynomials
    coefficients  of the orders
  @param filename 
    The name of the save file on disk
  @param tag
    The list pro catg
  @param is_temp
    CPl_TRUE if temporary file, CPL_FALSE otherwise
  @return 
    a newly allocated frame
*/
/*---------------------------------------------------------------------------*/
cpl_frame* xsh_rec_list_save( xsh_rec_list* list, const char* filename, 
  const char* tag, int is_temp)
{
  cpl_frame * result = NULL ;
  cpl_table * table = NULL;
  int nlambda = 0, nslit = 0, depth = 0;
  cpl_array  * dim = NULL, * temp_array = NULL ;
  int i, k, order;
  unsigned mode = CPL_IO_DEFAULT;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( filename);
  XSH_ASSURE_NOT_ILLEGAL( list->size > 0);

  /* Loop over Orders, as we create one table per order */
  for( i = 0 ; i < list->size ; i++ ) {
    
    nlambda = list->list[i].nlambda;
    nslit = list->list[i].nslit;
    depth = nlambda*nslit;
    order = list->list[i].order;

    /* skip the order */
    if (depth == 0){
      continue;
    }
    check( table = cpl_table_new( 1));

    /* create column names */
    check(cpl_table_new_column( table, XSH_REC_TABLE_COLNAME_ORDER,
      CPL_TYPE_INT));
    check(cpl_table_new_column( table, XSH_REC_TABLE_COLNAME_NLAMBDA,
      CPL_TYPE_INT));
    check(cpl_table_new_column( table, XSH_REC_TABLE_COLNAME_NSLIT,
      CPL_TYPE_INT));
    check(cpl_table_new_column_array( table, XSH_REC_TABLE_COLNAME_LAMBDA,
      CPL_TYPE_DOUBLE, nlambda));
    check(cpl_table_new_column_array( table, XSH_REC_TABLE_COLNAME_SLIT,
      CPL_TYPE_FLOAT, nslit));
    check(cpl_table_new_column_array( table, XSH_REC_TABLE_COLNAME_FLUX1,
      CPL_TYPE_FLOAT, depth));
    check( cpl_table_new_column_array( table, XSH_REC_TABLE_COLNAME_ERRS1,
      CPL_TYPE_FLOAT, depth));
    check( cpl_table_new_column_array( table, XSH_REC_TABLE_COLNAME_QUAL1,
      CPL_TYPE_INT, depth));

    /* Set column array dimensions */
    check( dim = cpl_array_new( 1, CPL_TYPE_INT)) ;
    cpl_array_set_int( dim, 0, nlambda);
    check( cpl_table_set_column_dimensions( 
      table, XSH_REC_TABLE_COLNAME_LAMBDA, dim));
    cpl_array_set_int( dim, 0, nslit);
    check( cpl_table_set_column_dimensions( table,
      XSH_REC_TABLE_COLNAME_SLIT, dim));
    xsh_free_array( &dim);

    check( dim = cpl_array_new( 2, CPL_TYPE_INT)) ;
    cpl_array_set_int( dim, 0, nlambda);
    cpl_array_set_int( dim, 1, nslit);

    check( cpl_table_set_column_dimensions( table,
      XSH_REC_TABLE_COLNAME_FLUX1, dim));
    check( cpl_table_set_column_dimensions( table,
      XSH_REC_TABLE_COLNAME_ERRS1, dim));
    check( cpl_table_set_column_dimensions( table,
      XSH_REC_TABLE_COLNAME_QUAL1, dim));
    xsh_free_array( &dim); 

    /* Populate the basic columns */
    check( cpl_table_set_int( table, XSH_REC_TABLE_COLNAME_ORDER, 0,
      order));
    check( cpl_table_set_int( table, XSH_REC_TABLE_COLNAME_NLAMBDA, 0,
      nlambda));
    check( cpl_table_set_int( table, XSH_REC_TABLE_COLNAME_NSLIT, 0,
      nslit));

    /* Create an array for slits and fill it*/
    check ( temp_array = cpl_array_new( nslit, CPL_TYPE_FLOAT));
    for( k = 0 ; k<nslit ; k++ ) {
      check( cpl_array_set_float( temp_array, k,
        *(list->list[i].slit+k)));
    }
    /* Insert into table */
    check( cpl_table_set_array( table, XSH_REC_TABLE_COLNAME_SLIT, 0,
      temp_array));
    xsh_free_array( &temp_array);

    /* Create an array for lambda and fill it */
    check( temp_array = cpl_array_new( nlambda, CPL_TYPE_DOUBLE));
    for( k = 0 ; k<nlambda ; k++ ) {
      check( cpl_array_set_double( temp_array, k,
        *(list->list[i].lambda+k)));
    }
    /* Insert into table */
    check( cpl_table_set_array( table, XSH_REC_TABLE_COLNAME_LAMBDA, 0,
      temp_array));
    xsh_free_array( &temp_array);

    /* images FLUX, ERRS, QUAL */
    check( temp_array = cpl_array_new( depth, CPL_TYPE_FLOAT));
    for( k = 0 ; k<depth ; k++ ) {
      check( cpl_array_set_float( temp_array, k,
        *(list->list[i].data1+k) ) ) ;
    }
    check( cpl_table_set_array( table, XSH_REC_TABLE_COLNAME_FLUX1, 0,
      temp_array));

    for( k = 0 ; k<depth ; k++ ) {
      check( cpl_array_set_float( temp_array, k,
        *(list->list[i].errs1+k) ) ) ;
    }
    check( cpl_table_set_array( table, XSH_REC_TABLE_COLNAME_ERRS1, 0,
      temp_array));
    xsh_free_array( &temp_array);

    check( temp_array = cpl_array_new( depth, CPL_TYPE_INT));
    for( k = 0 ; k<depth ; k++ ) {
      check( cpl_array_set_int( temp_array, k,
        *(list->list[i].qual1+k)));
    }
    check( cpl_table_set_array( table, XSH_REC_TABLE_COLNAME_QUAL1, 0,
      temp_array));
    xsh_free_array( &temp_array ) ;

 
    /* create fits file */
    xsh_pfits_set_bunit(list->header,XSH_BUNIT_FLUX_REL_C);
    check( cpl_table_save( table, list->header, NULL, filename, mode));
    mode = CPL_IO_EXTEND;
    XSH_TABLE_FREE( table);
  }

  /* Create the frame */
  check( result = xsh_frame_product( filename, tag, CPL_FRAME_TYPE_IMAGE, 
    CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_LEVEL_FINAL));

/*
  if ( is_temp == CPL_TRUE){
    check( cpl_frame_set_level( result, CPL_FRAME_LEVEL_TEMPORARY));
    xsh_add_temporary_file( filename);
  }
*/
  cleanup:
    XSH_TABLE_FREE( table);
    xsh_free_array( &temp_array);
    return result ;
}
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/**
  @brief
    Update header of rectified list writing mandatory KW
  @param rec_list 
    The rectified list to update
  @param pre
   the pre image used to build rectified list
  @param rec_par
    The rectify parameters
  @param pro_catg
    The given pro catg
*/
/*---------------------------------------------------------------------------*/
void xsh_rec_list_update_header( xsh_rec_list *rec_list, xsh_pre *pre,
  xsh_rectify_param *rec_par, const char* pro_catg)
{
  double lambda_min, lambda_max;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL( rec_list);
  XSH_ASSURE_NOT_NULL( pre);
  XSH_ASSURE_NOT_NULL( rec_par);

  /* Set header from science header */
  check( cpl_propertylist_append( rec_list->header, pre->data_header));

  /* Add bin_lambda and bin_spce to property list */
  check( xsh_pfits_set_rectify_bin_lambda( rec_list->header,
    rec_par->rectif_bin_lambda));
  check( xsh_pfits_set_rectify_bin_space( rec_list->header,
    rec_par->rectif_bin_space));

  check( lambda_min = xsh_rec_list_get_lambda_min( rec_list));
  check( lambda_max = xsh_rec_list_get_lambda_max( rec_list));

  check( xsh_pfits_set_rectify_lambda_min(rec_list->header,
    lambda_min)) ;
  check( xsh_pfits_set_rectify_lambda_max(rec_list->header,
    lambda_max)); 

  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));
  check( xsh_pfits_set_pcatg( rec_list->header, pro_catg));

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


/*---------------------------------------------------------------------------*/
/**
  @brief
    Save a rec list in a frame
  @param list 
    The rec_list structure wich contains polynomials
    coefficients  of the orders
  @param filename 
    The name of the save file on disk
  @param tag
    The pro catg of the save file on disk
  @param is_temp
    CPl_TRUE if temporary file, CPL_FALSE otherwise
  @return 
    a newly allocated frame
*/
/*---------------------------------------------------------------------------*/
cpl_frame* xsh_rec_list_save_table( xsh_rec_list* list, const char* filename, 
  const char* tag, int is_temp)
{
  cpl_frame * result = NULL ;
  cpl_table * table = NULL;
  int nlambda = 0, nslit = 0, depth = 0;
  cpl_array  * dim = NULL, * temp_array = NULL ;
  int i, k, order;
  unsigned mode = CPL_IO_DEFAULT;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( filename);
  XSH_ASSURE_NOT_ILLEGAL( list->size > 0);

  /* Loop over Orders, as we create one table per order */
  for( i = 0 ; i < list->size ; i++ ) {
    
    nlambda = list->list[i].nlambda;
    nslit = list->list[i].nslit;
    depth = nlambda*nslit;
    order = list->list[i].order;

    /* skip the order */
    if (depth == 0){
      continue;
    }
    check( table = cpl_table_new( 1));

    /* create column names */
    check(cpl_table_new_column( table, XSH_REC_TABLE_COLNAME_ORDER,
      CPL_TYPE_INT));
    check(cpl_table_new_column( table, XSH_REC_TABLE_COLNAME_NLAMBDA,
      CPL_TYPE_INT));
    check(cpl_table_new_column( table, XSH_REC_TABLE_COLNAME_NSLIT,
      CPL_TYPE_INT));
    check(cpl_table_new_column_array( table, XSH_REC_TABLE_COLNAME_LAMBDA,
      CPL_TYPE_DOUBLE, nlambda));
    check(cpl_table_new_column_array( table, XSH_REC_TABLE_COLNAME_SLIT,
      CPL_TYPE_FLOAT, nslit));

    /* Set column array dimensions */
    check( dim = cpl_array_new( 1, CPL_TYPE_INT)) ;
    cpl_array_set_int( dim, 0, nlambda);
    check( cpl_table_set_column_dimensions( 
      table, XSH_REC_TABLE_COLNAME_LAMBDA, dim));
    cpl_array_set_int( dim, 0, nslit);
    check( cpl_table_set_column_dimensions( table,
      XSH_REC_TABLE_COLNAME_SLIT, dim));
    xsh_free_array( &dim);

    check( dim = cpl_array_new( 2, CPL_TYPE_INT)) ;
    cpl_array_set_int( dim, 0, nlambda);
    cpl_array_set_int( dim, 1, nslit);

    xsh_free_array( &dim); 

    /* Populate the basic columns */
    check( cpl_table_set_int( table, XSH_REC_TABLE_COLNAME_ORDER, 0,
      order));
    check( cpl_table_set_int( table, XSH_REC_TABLE_COLNAME_NLAMBDA, 0,
      nlambda));
    check( cpl_table_set_int( table, XSH_REC_TABLE_COLNAME_NSLIT, 0,
      nslit));

    /* Create an array for slits and fill it*/
    check ( temp_array = cpl_array_new( nslit, CPL_TYPE_FLOAT));
    for( k = 0 ; k<nslit ; k++ ) {
      check( cpl_array_set_float( temp_array, k,
        *(list->list[i].slit+k)));
    }
    /* Insert into table */
    check( cpl_table_set_array( table, XSH_REC_TABLE_COLNAME_SLIT, 0,
      temp_array));
    xsh_free_array( &temp_array);

    /* Create an array for lambda and fill it */
    check( temp_array = cpl_array_new( nlambda, CPL_TYPE_DOUBLE));
    for( k = 0 ; k<nlambda ; k++ ) {
      check( cpl_array_set_double( temp_array, k,
        *(list->list[i].lambda+k)));
    }
    /* Insert into table */
    check( cpl_table_set_array( table, XSH_REC_TABLE_COLNAME_LAMBDA, 0,
      temp_array));
    xsh_free_array( &temp_array);

    /* images FLUX, ERRS, QUAL */
    /* create fits file */
    check( cpl_table_save( table, list->header, NULL, filename, mode));
    mode = CPL_IO_EXTEND;
    XSH_TABLE_FREE( table);
  }

  /* Create the frame */
  check( result = xsh_frame_product( filename, tag, CPL_FRAME_TYPE_TABLE, 
    CPL_FRAME_GROUP_PRODUCT, CPL_FRAME_LEVEL_FINAL));

  if ( is_temp == CPL_TRUE){
    check( cpl_frame_set_level( result, CPL_FRAME_LEVEL_TEMPORARY));
    xsh_add_temporary_file( filename);
  }

  cleanup:
    XSH_TABLE_FREE( table);
    xsh_free_array( &temp_array);
    return result ;
}
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/**
   @brief save an rec list to a frame
   @param list the rec_list structure wich contains polynomials
   coefficients  of the orders
   @param filename the name of the save file on disk
   @param tag rec list pro catg
   @return two newly allocated frames

*/
/*---------------------------------------------------------------------------*/
cpl_frame*
xsh_rec_list1D_save_as_tab(xsh_rec_list* list,const char* filename, const char* tag)
{

  cpl_table * table = NULL;
  int nlambda, nslit, depth ;
  cpl_array  * temp_array = NULL ;
  int i, k, order ;
  cpl_frame* result=NULL;

  double* plambda=NULL;
  float* pslit=NULL;

  float* pflux=NULL;
  float* perr=NULL;
  int* pqual=NULL;
  //cpl_propertylist* plist=NULL;
  char data_extname[20];
  char errs_extname[20];
  char qual_extname[20];
  unsigned mode = CPL_IO_DEFAULT;
  cpl_propertylist *header_data = NULL;
  cpl_propertylist *header_errs = NULL;
  cpl_propertylist *header_qual = NULL;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( filename);
  XSH_ASSURE_NOT_ILLEGAL( list->size > 0 ) ;

  /* Loop over Orders, as we create one table per order */
  for( i = 0; i < list->size ; i++ ) {

    nlambda = list->list[i].nlambda ;
    nslit = list->list[i].nslit ;
    depth = nlambda*nslit ;
    order = list->list[i].order ;

    xsh_msg_dbg_high("depth %d : %dx%d", depth, nslit, nlambda);
    xsh_msg("depth %d : %dx%d", depth, nslit, nlambda);
    /* skip the order */
    if (depth == 0){
      continue;
    }
    table = cpl_table_new(nlambda);
    cpl_table_new_column(table,"wave",CPL_TYPE_DOUBLE);
    cpl_table_new_column(table,"flux",CPL_TYPE_DOUBLE);
    cpl_table_new_column(table,"errs",CPL_TYPE_DOUBLE);
    cpl_table_new_column(table,"qual",CPL_TYPE_INT);
    cpl_table_fill_column_window_double(table,"wave",0,nlambda,0);
    cpl_table_fill_column_window_double(table,"flux",0,nlambda,0);
    cpl_table_fill_column_window_double(table,"errs",0,nlambda,0);
    cpl_table_fill_column_window_int(table,"qual",0,nlambda,0);
    check( header_data = cpl_propertylist_duplicate( list->header));
    header_errs=cpl_propertylist_new();
    header_qual=cpl_propertylist_new();


    check(plambda=cpl_table_get_data_double(table,"wave"));
    check(pflux=cpl_table_get_data_double(table,"flux"));
    check(perr=cpl_table_get_data_double(table,"errs"));
    check(pqual=cpl_table_get_data_int(table,"qual"));

    /*
    for( k = 0 ; k<nslit ; k++ ) {
      pslit[k]= (*(list->list[i].slit+k) )  ;
    }
    */
    for( k = 0 ; k<nlambda ; k++ ) {
      plambda[k]= (*(list->list[i].lambda+k) )  ;
    }

    for( k = 0 ; k<depth ; k++ ) {
      pflux[k]= (*(list->list[i].data1+k) )  ;
      perr[k]= (*(list->list[i].errs1+k) )  ;
      pqual[k]= (*(list->list[i].qual1+k) )  ;
    }

    /* product 2D */

    {
    	cpl_propertylist_erase_regexp(header_data, "^CRPIX2",0);
    	cpl_propertylist_erase_regexp(header_data, "^CRVAL2",0);
    	cpl_propertylist_erase_regexp(header_data, "^CDELT2",0);

    	cpl_propertylist_erase_regexp(header_errs, "^CRPIX2",0);
    	cpl_propertylist_erase_regexp(header_errs, "^CRVAL2",0);
    	cpl_propertylist_erase_regexp(header_errs, "^CDELT2",0);

    	cpl_propertylist_erase_regexp(header_qual, "^CRPIX2",0);
    	cpl_propertylist_erase_regexp(header_qual, "^CRVAL2",0);
    	cpl_propertylist_erase_regexp(header_qual, "^CDELT2",0);

    }


    sprintf(data_extname,"ORD%d_FLUX",order);
    sprintf(errs_extname,"ORD%d_ERRS",order);
    sprintf(qual_extname,"ORD%d_QUAL",order);
    xsh_msg_dbg_high("extname %s", data_extname);

    xsh_pfits_set_bunit(header_data,XSH_BUNIT_FLUX_REL_C);
    check( xsh_pfits_set_extname( header_data, data_extname));
    check(xsh_plist_set_extra_keys(header_data,"IMAGE","DATA","RMSE",
                                   data_extname,errs_extname,qual_extname,0));


    xsh_pfits_set_bunit(header_errs,XSH_BUNIT_FLUX_REL_C);
    xsh_pfits_set_extname( header_errs, errs_extname);
    check(xsh_plist_set_extra_keys(header_errs,"IMAGE","DATA","RMSE",
                                   data_extname,errs_extname,qual_extname,1));

    xsh_pfits_set_bunit(header_qual,XSH_BUNIT_NONE_C);
    xsh_pfits_set_extname( header_qual,qual_extname);
    check(xsh_plist_set_extra_keys(header_qual,"IMAGE","DATA","RMSE",
                                   data_extname,errs_extname,qual_extname,2));

    mode = CPL_IO_EXTEND;

    if(i==0) {
    	cpl_table_save( table, header_data, NULL, filename, CPL_IO_DEFAULT );
    } else {
    	cpl_table_save( table, header_data, NULL, filename, mode );
    }
    xsh_free_table(&table);
    xsh_free_propertylist( &header_data);
    xsh_free_propertylist( &header_errs);
    xsh_free_propertylist( &header_qual);

  }

  check(result=xsh_frame_product(filename,tag,CPL_FRAME_TYPE_IMAGE,
				    CPL_FRAME_GROUP_PRODUCT,
				    CPL_FRAME_LEVEL_FINAL));

  cleanup:
    xsh_free_propertylist( &header_data);
    XSH_TABLE_FREE( table);
    xsh_free_array( &temp_array ) ;
    return result ;
}


/*---------------------------------------------------------------------------*/
/**
   @brief save an rec list to a frame
   @param list the rec_list structure wich contains polynomials
   coefficients  of the orders
   @param filename the name of the save file on disk
   @param tag rec list pro catg
   @return two newly allocated frames

*/
/*---------------------------------------------------------------------------*/
cpl_frame*
xsh_rec_list_save2(xsh_rec_list* list,const char* filename, const char* tag)
{
 
  cpl_table * table = NULL;
  int nlambda, nslit, depth ;
  cpl_array  * temp_array = NULL ;
  int i, k, order ;

  cpl_image* img_lambda=NULL;
  cpl_image* img_slit=NULL;

  cpl_image* img_flux=NULL;
  cpl_image* img_err=NULL;
  cpl_image* img_qual=NULL;
  cpl_frame* result=NULL;



  double* plambda=NULL;
  float* pslit=NULL;

  float* pflux=NULL;
  float* perr=NULL;
  int* pqual=NULL;
  double cdelt1=0;
  double cdelt2=0;
  double crpix1=0;
  double crpix2=0;
  double crval1=0;
  double crval2=0;
  //cpl_propertylist* plist=NULL;
  char data_extname[20];
  char errs_extname[20];
  char qual_extname[20];
  unsigned mode = CPL_IO_DEFAULT;
  cpl_propertylist *header_data = NULL;
  cpl_propertylist *header_errs = NULL;
  cpl_propertylist *header_qual = NULL;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( filename);
  XSH_ASSURE_NOT_ILLEGAL( list->size > 0 ) ;

  /* Loop over Orders, as we create one table per order */
  for( i = 0; i < list->size ; i++ ) {

    nlambda = list->list[i].nlambda ;
    nslit = list->list[i].nslit ;
    depth = nlambda*nslit ;
    order = list->list[i].order ;

    xsh_msg_dbg_high("depth %d : %dx%d", depth, nslit, nlambda);

    /* skip the order */
    if (depth == 0){
      continue;
    }

    check( header_data = cpl_propertylist_duplicate( list->header));
    header_errs=cpl_propertylist_new();
    header_qual=cpl_propertylist_new();

    check(img_lambda=cpl_image_new(nlambda,1,CPL_TYPE_DOUBLE));
    check(img_slit=cpl_image_new(nslit,1,CPL_TYPE_FLOAT));
    check(img_flux=cpl_image_new(nlambda,nslit,CPL_TYPE_FLOAT));
    check(img_err=cpl_image_new(nlambda,nslit,CPL_TYPE_FLOAT));
    check(img_qual=cpl_image_new(nlambda,nslit,CPL_TYPE_INT));

    check(plambda=cpl_image_get_data_double(img_lambda));
    check(pslit=cpl_image_get_data_float(img_slit));
    check(pflux=cpl_image_get_data_float(img_flux));
    check(perr=cpl_image_get_data_float(img_err));
    check(pqual=cpl_image_get_data_int(img_qual));

    for( k = 0 ; k<nslit ; k++ ) {
      pslit[k]= (*(list->list[i].slit+k) )  ;
    }
    for( k = 0 ; k<nlambda ; k++ ) {
      plambda[k]= (*(list->list[i].lambda+k) )  ;
    }

    if (nlambda > 1){
      cdelt1=plambda[1]-plambda[0];
      crpix1=1.;
      crval1=plambda[0];

      check(xsh_pfits_set_wcs1(header_data, crpix1, crval1, cdelt1));
      check(xsh_pfits_set_wcs1(header_errs, crpix1, crval1, cdelt1));
      check(xsh_pfits_set_wcs1(header_qual, crpix1, crval1, cdelt1));

      xsh_msg_dbg_high("write axis 1 : %f %f %f", crpix1, crval1, cdelt1);


    }

    for( k = 0 ; k<depth ; k++ ) {
      pflux[k]= (*(list->list[i].data1+k) )  ;
      perr[k]= (*(list->list[i].errs1+k) )  ;
      pqual[k]= (*(list->list[i].qual1+k) )  ;
    }

    /* product 2D */
      crpix2=1.;
      crval2=pslit[0];
    if ( nslit > 1){
      xsh_msg_dbg_high("nslit %d", nslit);
      cdelt2=pslit[1]-pslit[0];
      xsh_pfits_set_wcs(header_data,crpix1,crval1,cdelt1,crpix2,crval2,cdelt2);
      xsh_pfits_set_wcs(header_errs,crpix1,crval1,cdelt1,crpix2,crval2,cdelt2);
      xsh_pfits_set_wcs(header_qual,crpix1,crval1,cdelt1,crpix2,crval2,cdelt2);

    } else {
      cpl_propertylist_erase_regexp(header_data, "^CRPIX2",0);
      cpl_propertylist_erase_regexp(header_data, "^CRVAL2",0);
      cpl_propertylist_erase_regexp(header_data, "^CDELT2",0);

      cpl_propertylist_erase_regexp(header_errs, "^CRPIX2",0);
      cpl_propertylist_erase_regexp(header_errs, "^CRVAL2",0);
      cpl_propertylist_erase_regexp(header_errs, "^CDELT2",0);

      cpl_propertylist_erase_regexp(header_qual, "^CRPIX2",0);
      cpl_propertylist_erase_regexp(header_qual, "^CRVAL2",0);
      cpl_propertylist_erase_regexp(header_qual, "^CDELT2",0);

      xsh_pfits_set_wcs1(header_data, crpix1, crval1, cdelt1);
      xsh_pfits_set_wcs1(header_errs, crpix1, crval1, cdelt1);
      xsh_pfits_set_wcs1(header_qual, crpix1, crval1, cdelt1);
  
    }
    
    xsh_pfits_set_extract_slit_min( header_data,list->slit_min);
    xsh_pfits_set_extract_slit_max( header_data,list->slit_max);

    xsh_pfits_set_extract_slit_min( header_errs,list->slit_min);
    xsh_pfits_set_extract_slit_max( header_errs,list->slit_max);

    xsh_pfits_set_extract_slit_min( header_qual,list->slit_min);
    xsh_pfits_set_extract_slit_max( header_qual,list->slit_max);
    
    sprintf(data_extname,"ORD%d_FLUX",order);
    sprintf(errs_extname,"ORD%d_ERRS",order);
    sprintf(qual_extname,"ORD%d_QUAL",order);
    xsh_msg_dbg_high("extname %s", data_extname);

    xsh_pfits_set_bunit(header_data,XSH_BUNIT_FLUX_REL_C);
    check( xsh_pfits_set_extname( header_data, data_extname));
    check(xsh_plist_set_extra_keys(header_data,"IMAGE","DATA","RMSE",
                                   data_extname,errs_extname,qual_extname,0));


    xsh_pfits_set_bunit(header_errs,XSH_BUNIT_FLUX_REL_C);
    xsh_pfits_set_extname( header_errs, errs_extname);
    check(xsh_plist_set_extra_keys(header_errs,"IMAGE","DATA","RMSE",
                                   data_extname,errs_extname,qual_extname,1));

    xsh_pfits_set_bunit(header_qual,XSH_BUNIT_NONE_C);
    xsh_pfits_set_extname( header_qual,qual_extname);
    check(xsh_plist_set_extra_keys(header_qual,"IMAGE","DATA","RMSE",
                                   data_extname,errs_extname,qual_extname,2));

    mode = CPL_IO_EXTEND;
    if ( nslit > 1){
       if(i==0) {
          cpl_image_save(img_flux,filename, CPL_BPP_IEEE_FLOAT, header_data, 
                         CPL_IO_DEFAULT);
       } else {
          cpl_image_save(img_flux,filename, CPL_BPP_IEEE_FLOAT, header_data, mode);

       }
       cpl_image_save(img_err,filename, CPL_BPP_IEEE_FLOAT, header_errs, mode) ;
       cpl_image_save(img_qual, filename, XSH_PRE_QUAL_BPP, header_qual, mode) ;

    } else {
      cpl_vector* flux1D = cpl_vector_new_from_image_row( img_flux, 1);
      cpl_vector* errs1D = cpl_vector_new_from_image_row( img_err, 1);
      cpl_vector* qual1D = cpl_vector_new_from_image_row( img_qual, 1);

       if(i==0) {
          cpl_vector_save( flux1D, filename, CPL_BPP_IEEE_FLOAT, header_data,CPL_IO_DEFAULT );
       } else {
          cpl_vector_save( flux1D, filename, CPL_BPP_IEEE_FLOAT, header_data, mode);
       }
      cpl_vector_save( errs1D, filename, CPL_BPP_IEEE_FLOAT, header_errs, mode);
      cpl_vector_save( qual1D, filename, CPL_BPP_32_SIGNED, header_qual, mode);
      xsh_free_vector( &flux1D);
      xsh_free_vector( &errs1D);
      xsh_free_vector( &qual1D);
    }

 

    xsh_free_image(&img_lambda);
    xsh_free_image(&img_slit);
    xsh_free_image(&img_flux);
    xsh_free_image(&img_err);
    xsh_free_image(&img_qual);
    xsh_free_propertylist( &header_data);
    xsh_free_propertylist( &header_errs);
    xsh_free_propertylist( &header_qual);

  }

  check(result=xsh_frame_product(filename,tag,CPL_FRAME_TYPE_IMAGE, 
				    CPL_FRAME_GROUP_PRODUCT,
				    CPL_FRAME_LEVEL_FINAL));

  cleanup:
    xsh_free_propertylist( &header_data);
    XSH_TABLE_FREE( table);
    xsh_free_array( &temp_array ) ;
    return result ;
}





void xsh_rec_get_nod_kw( cpl_frame * rec_frame, double * throw,
			 double * jitter,
			 double * reloffset, double * cumoffset )
{
  cpl_propertylist * header ;
  const char *fname = NULL ;
  double val ;

  XSH_ASSURE_NOT_NULL( rec_frame ) ;
  check( fname = cpl_frame_get_filename( rec_frame ) ) ;
  check( header = cpl_propertylist_load( fname, 0 ) ) ;

  val = xsh_pfits_get_nodthrow( header ) ;
  if ( cpl_error_get_code() == CPL_ERROR_NONE ) {
    *throw = val ;
  }
  else cpl_error_reset() ;

  val = xsh_pfits_get_nod_jitterwidth( header ) ;
  if ( cpl_error_get_code() == CPL_ERROR_NONE ) {
    *jitter = val ;
  }
  else
    cpl_error_reset() ;

  val = xsh_pfits_get_nod_reloffset( header ) ;
  if ( cpl_error_get_code() == CPL_ERROR_NONE ) {
    *reloffset = val ;
  }
  else cpl_error_reset() ;

  val = xsh_pfits_get_nod_cumoffset( header ) ;
  if ( cpl_error_get_code() == CPL_ERROR_NONE ) {
    *cumoffset = val ;
  }
  else cpl_error_reset() ;

 cleanup:
  xsh_free_propertylist( &header ) ;
  return ;
}

cpl_error_code
xsh_rec_get_nod_extract_slit_min_max(xsh_rec_list* rlist, const double slit_step,
		double* smin, double* smax)
{

 double nod_throw = xsh_pfits_get_nodthrow(rlist->header);
 int nslit = xsh_rec_list_get_nslit(rlist, 0);
 int hslit = round(0.5 * nod_throw / slit_step);
 *smax = nslit/2. + hslit;
 *smin = nslit/2. - hslit;

 return cpl_error_get_code();

}
/**@}*/
