/*                                                                           *
 *   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-09 15:13:34 $
 * $Revision: 1.96 $
 * $Name: not supported by cvs2svn $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_data_order Orders
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/

/**@{*/


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

#include <math.h>
#include <xsh_data_order.h>
#include <xsh_utils.h>
#include <xsh_error.h>
#include <xsh_utils_wrappers.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <xsh_dfs.h>
#include <cpl.h>
#include <xsh_utils_table.h>
#include <xsh_data_instrument.h>
#include <xsh_data_spectralformat.h>

/*----------------------------------------------------------------------------
                                 Function implementation
 ----------------------------------------------------------------------------*/
/** 
 * Dump main info about an order table (for each order of the list)
 * 
 * @param[in] list pointer to table list
 * @param[in] fname File name
 */
void xsh_order_list_dump( xsh_order_list* list, const char * fname )
{
  int i ;
  FILE * fout ;

  if ( fname == NULL ) fout = stdout ;
  else fout = fopen( fname, "w" ) ;
  fprintf( fout, "Found %d orders\n", list->size ) ;

  for( i = 0 ; i<list->size ; i++ ) {
    int degree;
    cpl_size j;

    fprintf( fout,  "order: %d, ", list->list[i].absorder ) ;
    fprintf( fout,  "Starty: %d, Endy: %d\n", list->list[i].starty,
	     list->list[i].endy ) ;
    degree = cpl_polynomial_get_degree( list->list[i].cenpoly ) ;
    for( j = 0 ; j<= degree ; j++ )
      fprintf( fout,  "  %lf ",
	       cpl_polynomial_get_coeff( list->list[i].cenpoly, &j ) ) ;
    for( j = 0 ; j<= degree ; j++ )
      fprintf( fout,  "%lf ",
	       cpl_polynomial_get_coeff( list->list[i].edglopoly, &j ) ) ;
    for( j = 0 ; j<= degree ; j++ )
      fprintf( fout,  " %lf ",
	       cpl_polynomial_get_coeff( list->list[i].edguppoly, &j ) ) ;
    fprintf( fout, "\n" ) ;
  }
  if ( fname != NULL ) fclose( fout ) ;
}

/** 
 * Check that end and starty are coherent. If not, set starty at 0 and endy
 * at the Y size of the image.
 * 
 * @param list Order list to check
 * @param ny Image size (Y)
 */
void xsh_order_list_verify( xsh_order_list * list, int ny )
{
  int i ;
  /*
    Verify that data are correct
  */

  for( i = 0 ; i<list->size ; i++ )
    if ( list->list[i].endy <= list->list[i].starty ) {
      list->list[i].endy = ny;
      list->list[i].starty = 1;
    }

  return ;
}

/*--------------------------------------------------------------------------*/
/**
  @brief Create a new order list from size (no check)
  @param[in] size
    The size of list
  @return
    A new ALLOCATED order list
 */
/*---------------------------------------------------------------------------*/
xsh_order_list* xsh_order_list_new( int size)
{
  xsh_order_list* result = NULL;

  XSH_ASSURE_NOT_ILLEGAL( size > 0);
  XSH_CALLOC(result,xsh_order_list,1);
  result->size = size;
  XSH_CALLOC(result->list, xsh_order, result->size);
  XSH_NEW_PROPERTYLIST(result->header);
  
  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_order_list_free(&result);
    }
    return result;   
}

/*--------------------------------------------------------------------------*/
/**
  @brief create an empty order list
  @param[in] instr instrument in use
  @return the order list structure

 */
/*---------------------------------------------------------------------------*/
xsh_order_list* xsh_order_list_create(xsh_instrument* instr)
{
  xsh_order_list* result = NULL;
  int size;
  XSH_INSTRCONFIG * config = NULL;

  /* check input parameters */
  XSH_ASSURE_NOT_NULL(instr);
  check( config = xsh_instrument_get_config( instr ) ) ;
  XSH_ASSURE_NOT_NULL( config ) ;
  size = config->orders;
  check( result = xsh_order_list_new( size));
  result->instrument = instr;
  result->absorder_min = config->order_min;
  result->absorder_max = config->order_max;
  result->bin_x = xsh_instrument_get_binx( instr ) ;
  result->bin_y = xsh_instrument_get_biny( instr ) ;

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

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

 */
/*---------------------------------------------------------------------------*/
xsh_order_list* xsh_order_list_load(cpl_frame* frame, xsh_instrument* instr)
{
  cpl_table* table = NULL; 
  cpl_propertylist* header = NULL;
  const char* tablename = NULL;
  xsh_order_list* result = NULL;
  int i = 0, pol_degree = 0;
  cpl_size k=0;
  /* check input parameters */
  XSH_ASSURE_NOT_NULL( frame);
  XSH_ASSURE_NOT_NULL( instr);

  /* get table filename */
  check( tablename = cpl_frame_get_filename(frame));

  XSH_TABLE_LOAD( table, tablename);
  /* to be able to run with less orders in spectral format tab */

  check(result = xsh_order_list_create(instr));
  check(header = cpl_propertylist_load(tablename,0));
  check(cpl_propertylist_append(result->header, header));
  /*
  xsh_msg("size=%d check=%d",result->size, cpl_table_get_nrow(table));
  XSH_ASSURE_NOT_ILLEGAL(result->size == cpl_table_get_nrow(table));
  */

  for(i=0;i<result->size;i++){
    /* load the order in structure */
    check(xsh_get_table_value(table, XSH_ORDER_TABLE_COLNAME_ORDER,
      CPL_TYPE_INT, i, &(result->list[i].order)));
    check(xsh_get_table_value(table, XSH_ORDER_TABLE_COLNAME_ABSORDER,
      CPL_TYPE_INT, i, &(result->list[i].absorder)));
    /* Get the polynomial degree */
    xsh_get_table_value(table, XSH_ORDER_TABLE_DEGY,
			CPL_TYPE_INT, i, &pol_degree);
    if ( cpl_error_get_code() != CPL_ERROR_NONE ) {
      pol_degree = 2 ;
      cpl_error_reset() ;
    }

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

    check(result->list[i].edguppoly = cpl_polynomial_new(1));
    check(result->list[i].cenpoly = cpl_polynomial_new(1));
    check(result->list[i].edglopoly = cpl_polynomial_new(1));

    check(result->list[i].slicuppoly = cpl_polynomial_new(1));
    check(result->list[i].sliclopoly = cpl_polynomial_new(1));

    for( k = 0 ; k <= pol_degree ; k++ ) {
      char colname[32] ;
      float coef ;

      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_CENTER, k);
      check(xsh_get_table_value(table, colname, CPL_TYPE_FLOAT, i, &coef));
      check(cpl_polynomial_set_coeff(result->list[i].cenpoly,
				     &k,coef));

      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_EDGLO, k);
      check(xsh_get_table_value(table, colname, CPL_TYPE_FLOAT, i, &coef));
      check(cpl_polynomial_set_coeff(result->list[i].edglopoly,
				     &k,coef));

      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_EDGUP, k);
      check(xsh_get_table_value(table, colname, CPL_TYPE_FLOAT, i, &coef));
      check(cpl_polynomial_set_coeff(result->list[i].edguppoly,
				     &k,coef));

      /* Compatibility with old order table (had only SLIT) */
      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_SLICUP, k);
      if ( cpl_table_has_column( table, colname ) == 1 ) {
	check(xsh_get_table_value(table, colname, CPL_TYPE_FLOAT, i, &coef));
	check(cpl_polynomial_set_coeff(result->list[i].slicuppoly,
				       &k,coef));
      }
      else check(cpl_polynomial_set_coeff(result->list[i].slicuppoly,
					  &k, 0.));

      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_SLICLO, k);
      if ( cpl_table_has_column( table, colname ) == 1 ) {
	check(xsh_get_table_value(table, colname, CPL_TYPE_FLOAT, i, &coef));
	check(cpl_polynomial_set_coeff(result->list[i].sliclopoly,
				     &k,coef));
      }
      else check(cpl_polynomial_set_coeff(result->list[i].sliclopoly,
					  &k, 0.));
	
    }

    /* Starty/Endy */
    check(xsh_get_table_value(table, XSH_ORDER_TABLE_COLNAME_STARTY,
			      CPL_TYPE_INT, i, &result->list[i].starty));
    check(xsh_get_table_value(table, XSH_ORDER_TABLE_COLNAME_ENDY,
			      CPL_TYPE_INT, i, &result->list[i].endy));

    /*check_msg(
      result->list[i].cencoefsig0 = cpl_table_get_float(table,
      "CENCOEFSIG0",i,flag),
     "try to load column CENCOEFSIG0 , line %d",i);
    check_msg(
      result->list[i].cencoefsig1 = cpl_table_get_float(table,
      "CENCOEFSIG1",i,flag),
     "try to load column CENCOEFSIG1 , line %d",i); 
    check_msg(
      result->list[i].cencoefsig2 = cpl_table_get_float(table,
      "CENCOEFSIG2",i,flag),
     "try to load column CENCOEFSIG2 , line %d",i);
    check_msg(
      result->list[i].edgupcoefsig0 = cpl_table_get_float(table,
      "EDGUPCOEFSIG0",i,flag),
     "Try to load column EDGUPCOEFSIG0, line %d",i);
     check_msg(
      result->list[i].edgupcoefsig1 = cpl_table_get_float(table,
      "EDGUPCOEFSIG1",i,flag),
     "try to load column EDGUPCOEFSIG1, line %d",i);
     check_msg(
      result->list[i].edgupcoefsig2 = cpl_table_get_float(table,
      "EDGUPCOEFSIG2",i,flag),
     "try to load column EDGUPCOEFSIG2, line %d",i);
     check_msg(
      result->list[i].edglocoefsig0 = cpl_table_get_float(table,
      "EDGLOCOEFSIG0",i,flag),
     "try to load column EDGLOCOEFSIG0, line %d",i);
    check_msg(
      result->list[i].edglocoefsig1 = cpl_table_get_float(table,
      "EDGLOCOEFSIG1",i,flag),
     "try to load column EDGLOCOEFSIG1, line %d",i);
   check_msg(
      result->list[i].edglocoefsig2 = cpl_table_get_float(table,
      "EDGLOCOEFSIG2",i,flag),
     "try to load column EDGLOCOEFSIG2, line %d",i);
   */
 }

  result->bin_x = xsh_instrument_get_binx( instr ) ;
  result->bin_y = xsh_instrument_get_biny( instr ) ;
  xsh_msg_dbg_medium( "Order Table Load, Binning: %d x %d", result->bin_x,
	   result->bin_y);

  cleanup:
    if (cpl_error_get_code () != CPL_ERROR_NONE) {
      xsh_error_msg("can't load frame %s",cpl_frame_get_filename(frame));
      xsh_order_list_free(&result);
    }
    xsh_free_propertylist(&header);
    XSH_TABLE_FREE( table);
    return result;  
}

/*---------------------------------------------------------------------------*/
/**
  @brief fit the polynomial solution of given points
  @param[in] list The order list
  @param[in] size Nb of elements in the list
  @param[in] order Array of orders
  @param[in] posx Array of X positions
  @param[in] posy Array of Y positions
  @param[in] deg_poly Degree of the polynomial
 */
/*---------------------------------------------------------------------------*/

void xsh_order_list_fit(xsh_order_list *list, int size, double* order,
  double* posx, double* posy, int deg_poly)
{
  int ordersize, i, nborder, nb_keep_order;
  cpl_vector *vx = NULL, *vy = NULL;
  
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( order);
  XSH_ASSURE_NOT_NULL( posx);
  XSH_ASSURE_NOT_NULL( posy);
  XSH_ASSURE_NOT_ILLEGAL( deg_poly >= 0);

  ordersize = 0;
  nborder = 0;
  nb_keep_order = 0;
  xsh_msg("Fit order traces");
  xsh_msg_dbg_high("List size=%d",size);
  xsh_msg_dbg_high("Fit a polynomial of degree %d by order",deg_poly);
  xsh_msg_dbg_high("Search from order %d to %d", list->absorder_min, 
    list->absorder_max);
  for(i=1; i <= size; i++) {
    if ( i < size && fabs(order[i-1] - order[i] ) < 0.0001) {
      ordersize++;
    }
    else {
      int absorder = order[i-1];

      if(  (absorder >= list->absorder_min) && 
        (absorder <= list->absorder_max) ){

        ordersize++;
        check( vx = cpl_vector_wrap( ordersize, &(posx[i-ordersize])));
        check( vy = cpl_vector_wrap( ordersize, &(posy[i-ordersize])));
        xsh_msg_dbg_low("%d) absorder %lg nbpoints %d",
		      nborder+1, order[i-1],ordersize);
        XSH_ASSURE_NOT_ILLEGAL_MSG(ordersize > deg_poly,
        "You must have more points to fit correctly this order (may be detectarclines-ordertab-deg-y is too large or (for xsh_predict) detectarclines-min-sn is too large)");
        check( list->list[nb_keep_order].cenpoly = 
          xsh_polynomial_fit_1d_create(vy, vx, deg_poly,NULL));
        list->list[nb_keep_order].order = nborder;
        list->list[nb_keep_order].absorder = (int)(order[i-1]);

        check( xsh_unwrap_vector(&vx));
        check( xsh_unwrap_vector(&vy));
        nb_keep_order++;
      }
      else{
        xsh_msg("WARNING skipping absorder %d because is not in range", 
          absorder);
      }
      nborder++;
      ordersize = 0; 
    }
  }
  XSH_ASSURE_NOT_ILLEGAL_MSG( list->size == nb_keep_order,"to fix this, in xsh_predict, you may try to decrease detectarclines-min-sn");
  cleanup:
    xsh_unwrap_vector(&vx);
    xsh_unwrap_vector(&vy);
    return;
}

/*---------------------------------------------------------------------------*/
/**
  @brief free memory associated to an order_list
  @param[in] list the order_list to free
 */
/*---------------------------------------------------------------------------*/
void xsh_order_list_free(xsh_order_list** list)
{
  int i = 0;

  if (list && *list){
    /* free the list */
    for (i = 0; i < (*list)->size; i++){
      xsh_free_polynomial(&(*list)->list[i].cenpoly);
      xsh_free_polynomial(&(*list)->list[i].edguppoly);
      xsh_free_polynomial(&(*list)->list[i].edglopoly);
      xsh_free_polynomial(&(*list)->list[i].slicuppoly);
      xsh_free_polynomial(&(*list)->list[i].sliclopoly);
      xsh_free_polynomial(&(*list)->list[i].blazepoly);
    }
    if ((*list)->list){
      cpl_free( (*list)->list);
    }
    xsh_free_propertylist(&((*list)->header));
    cpl_free( *list);
    *list = NULL;
  }
}

/*---------------------------------------------------------------------------*/
/*
  @brief 
    Get index by absorder
  @param[in] list
    The order_list
  @param[in] absorder
    The abs order
  @return
    The index in order list corresponding to the absorder
*/
/*---------------------------------------------------------------------------*/
int xsh_order_list_get_index_by_absorder( xsh_order_list* list,
  double absorder)
{
  int idx = 0;
  int size = 0;

  XSH_ASSURE_NOT_NULL( list);
  size = list->size;
  while (idx < size && (list->list[idx].absorder != absorder)){
    idx++;
  }
  XSH_ASSURE_NOT_ILLEGAL( idx < size);

  cleanup:
    return idx;
    
}
/*---------------------------------------------------------------------------*/
/**
  @brief get header of the table
  @param[in] list the order_list
  @return the header associated to the table
 */
/*---------------------------------------------------------------------------*/
cpl_propertylist* xsh_order_list_get_header(xsh_order_list* list)
{
  cpl_propertylist * res = NULL;

  XSH_ASSURE_NOT_NULL(list);
  res = list->header;
  cleanup:
    return res;
}
/*---------------------------------------------------------------------------*/
/**
  @brief get position on Y axis of first pixel detected on order
  @param[in] list the order_list
  @param[in] i the index order
  @return the starty of the order
 */
/*---------------------------------------------------------------------------*/
int xsh_order_list_get_starty(xsh_order_list* list, int i)
{
  int res = 0;
  /* check input parameters */
  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_ILLEGAL(i >= 0 && i< list->size);
  res = floor( convert_data_to_bin( list->list[i].starty, list->bin_y)+0.5);

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/**
  @brief get position on Y axis of last pixel detected on order
  @param[in] list the order_list
  @param[in] i the index order
  @return the starty of the order
 */
/*---------------------------------------------------------------------------*/
int xsh_order_list_get_endy(xsh_order_list* list, int i)
{
  int res = 100000000;
  /* check input parameters */
  XSH_ASSURE_NOT_NULL(list);
  XSH_ASSURE_NOT_ILLEGAL(i >= 0 && i< list->size);
  res = floor(convert_data_to_bin( (double)list->list[i].endy, 
    list->bin_y)+0.5);
  cleanup:
    return res;
}

int xsh_order_list_get_order( xsh_order_list * list, int absorder )
{
  int i = 0 ;

  XSH_ASSURE_NOT_NULL(list);

  for( i = 0 ; i<list->size ; i++ )
    if ( list->list[i].absorder == absorder ) return i ;

  return -1 ;
 cleanup:
  return -1 ;
}

/*---------------------------------------------------------------------------*/
/*
  @brief
    Merge to list in a new list
  @param[in] lista
    The first order list 
  @param[in] listb
    The second order list
  @return
    A new list corresponding to (lista,listb)
*/
/*---------------------------------------------------------------------------*/
xsh_order_list* xsh_order_list_merge( xsh_order_list* lista,
  xsh_order_list* listb)
{
  xsh_order_list* result = NULL;
  int size = 0;
  int i,j;

  XSH_ASSURE_NOT_NULL( lista);
  XSH_ASSURE_NOT_NULL( listb);

  size = lista->size+listb->size;
  check( result = xsh_order_list_new( size));

  for( i=0; i< lista->size; i++){
    result->list[i].order = i;
    result->list[i].absorder = lista->list[i].absorder;
    result->list[i].starty = lista->list[i].starty;
    result->list[i].endy = lista->list[i].endy;
    result->list[i].cenpoly = cpl_polynomial_duplicate( 
      lista->list[i].cenpoly);
    result->list[i].edguppoly = cpl_polynomial_duplicate( 
      lista->list[i].edguppoly);
    result->list[i].edglopoly = cpl_polynomial_duplicate( 
      lista->list[i].edglopoly);
    result->list[i].slicuppoly = cpl_polynomial_duplicate( 
      lista->list[i].slicuppoly);
    result->list[i].sliclopoly = cpl_polynomial_duplicate( 
      lista->list[i].sliclopoly);
  }

  for( i=0; i< listb->size; i++){
    j= lista->size+i;
    result->list[j].order = i;
    result->list[j].absorder = listb->list[i].absorder;
    result->list[j].starty = listb->list[i].starty;
    result->list[j].endy = listb->list[i].endy;
    result->list[j].cenpoly = cpl_polynomial_duplicate(
      listb->list[i].cenpoly);
    result->list[j].edguppoly = cpl_polynomial_duplicate(
      listb->list[i].edguppoly);
    result->list[j].edglopoly = cpl_polynomial_duplicate(
      listb->list[i].edglopoly);
    result->list[j].slicuppoly = cpl_polynomial_duplicate(
      listb->list[i].slicuppoly);
    result->list[j].sliclopoly = cpl_polynomial_duplicate( 
      listb->list[i].sliclopoly);
  }

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


/*---------------------------------------------------------------------------*/
/**
  @brief
    Set the bin of image in x
  @param[in] list
    The order list to update
  @param[in] bin
    The binning value in x
*/
/*---------------------------------------------------------------------------*/
void xsh_order_list_set_bin_x( xsh_order_list* list, int bin)
{
  XSH_ASSURE_NOT_NULL( list);
  list->bin_x = bin;

  cleanup:
    return;
}

/*---------------------------------------------------------------------------*/
/**
  @brief
    Set the bin of image in y
  @param[in] list
    The order list to update
  @param[in] bin
    The binning value in y
*/
/*---------------------------------------------------------------------------*/
void xsh_order_list_set_bin_y( xsh_order_list* list, int bin)
{
  XSH_ASSURE_NOT_NULL( list);
  list->bin_y = bin;

  cleanup:
    return;
}

/*---------------------------------------------------------------------------*/
/**
  @brief
    Evaluate an order list poly
  @param[in] list order list structure
  @param[in] poly
    An order list poly
  @param[in] y
    The position to evaluate
  @return
    The result
*/
/*---------------------------------------------------------------------------*/
double xsh_order_list_eval( xsh_order_list* list, cpl_polynomial *poly, 
  double y)
{
  double result=0;
  double y_no_bin;
  double x_no_bin;

  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( poly);

  y_no_bin = convert_bin_to_data( y, list->bin_y);
  check( x_no_bin = cpl_polynomial_eval_1d( poly, y_no_bin, NULL));
  result = convert_data_to_bin( x_no_bin, list->bin_x);

  cleanup:
    return result;
}

/*---------------------------------------------------------------------------*/
/**
  @brief
    Evaluate an order list poly but return the central pixel position 
    rounding the polynomial
  @param[in] list
    The order list to update
  @param[in] poly
    An order list poly
  @param[in] y
    The position to evaluate
  @return
    The result
*/
/*---------------------------------------------------------------------------*/
int xsh_order_list_eval_int( xsh_order_list* list, cpl_polynomial *poly,
  double y)
{
  double res = 0.0;
  int result=0;

  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( poly);
 
  check( res = xsh_order_list_eval( list, poly, y));
  result = floor( res+0.5);

  cleanup:
    return result;
}


/*---------------------------------------------------------------------------*/
/**
  @brief
    Shift a order list 
  @param[in] list
    The order_list structure which contains polynomials 
    coefficients of the orders
  @param[in] xshift 
    X shift
  @param[in] yshift
    Y shift
 */
/*---------------------------------------------------------------------------*/
void xsh_order_list_apply_shift( xsh_order_list *list, double xshift, 
  double yshift)
{
  cpl_polynomial *p = NULL;
  double coef;
  cpl_size pows = 0;
  int i;

  XSH_ASSURE_NOT_NULL( list);

  for( i=0; i< list->size; i++){
    p = list->list[i].cenpoly;
    check( coef = cpl_polynomial_get_coeff( p, &pows));
    coef += xshift;
    check( cpl_polynomial_set_coeff( p, &pows, coef));
    
    p = list->list[i].edguppoly;
    if ( p != NULL){
      check( coef = cpl_polynomial_get_coeff( p, &pows));
      coef += xshift;
      check( cpl_polynomial_set_coeff( p, &pows, coef));
    }
    p = list->list[i].edglopoly;
    if ( p != NULL){
      check( coef = cpl_polynomial_get_coeff( p, &pows));
      coef += xshift;
      check( cpl_polynomial_set_coeff( p, &pows, coef));
    }
    p = list->list[i].sliclopoly;
    if ( p != NULL){
      check( coef = cpl_polynomial_get_coeff( p, &pows));
      coef += xshift;
      check( cpl_polynomial_set_coeff( p, &pows, coef));
    }
    p = list->list[i].slicuppoly;
    if ( p != NULL){
      check( coef = cpl_polynomial_get_coeff( p, &pows));
      coef += xshift;
      check( cpl_polynomial_set_coeff( p, &pows, coef));
    }
  }

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

/*---------------------------------------------------------------------------*/
/**
  @brief
    Save an order list to a frame
  @param[in] order_list
    The order_list structure wich contains polynomials 
    coefficients of the orders
  @param[in] instrument 
    The instrument setting
  @param[in] filename
    The name of the save file on disk
  @param[in] tag
    The frame tag
  @param[in] ny y size of frame

  @return 
    A newly allocated frame
 */
/*---------------------------------------------------------------------------*/
cpl_frame* 
xsh_order_list_save( xsh_order_list* order_list, xsh_instrument* instrument, 
		     const char* filename, const char* tag,const int  ny)
{
  cpl_table* table = NULL;
  cpl_table* tabqc = NULL;
  cpl_frame * result = NULL ;
  int i=0;
  cpl_size k ;
  int pol_degree ;
  char colname[32] ;
  int PointStep = 8 ;
  //XSH_INSTRCONFIG *config = NULL;
  int nraw;
  int* porder=NULL;
  int* pabsorder=NULL;
  double* pcenterx=NULL;
  double* pcentery=NULL;
  double* pedgeupx=NULL;
  double* pedgelox=NULL;
  double* pslicupx=NULL;
  double* psliclox=NULL;
  int iorder=0;
  int iy=0;
  cpl_table *tab_ext = NULL;
  int norder=0;
  /* check input parameters */
  XSH_ASSURE_NOT_NULL( order_list);
  XSH_ASSURE_NOT_NULL( filename);
  XSH_ASSURE_NOT_NULL( tag);
  XSH_ASSURE_NOT_NULL( instrument);

  //check( config = xsh_instrument_get_config( instrument));
  check( pol_degree = cpl_polynomial_get_degree( 
    order_list->list[0].cenpoly));
  XSH_ASSURE_NOT_ILLEGAL_MSG( pol_degree > 0,"Possibly detectarclines-ordertab-deg-y is too small" );

  /* create a table */
  check(table = cpl_table_new( order_list->size));

  /* create column names */
  check(
    cpl_table_new_column( table, XSH_ORDER_TABLE_COLNAME_ORDER, 
    CPL_TYPE_INT));
  check(
    cpl_table_new_column( table, XSH_ORDER_TABLE_COLNAME_ABSORDER,
     CPL_TYPE_INT));

  /* Create as many column as needed, according to the polynomial degree */
  for( k = 0 ; k<= pol_degree ; k++ ) {

    sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_CENTER, k);
    check(
	  cpl_table_new_column(table, colname, CPL_TYPE_FLOAT));
    sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_EDGUP, k);
    check(
	  cpl_table_new_column(table, colname, CPL_TYPE_FLOAT));
    sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_EDGLO, k);
    check(
	  cpl_table_new_column(table, colname, CPL_TYPE_FLOAT));

    sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_SLICUP, k);
    check(
	  cpl_table_new_column(table, colname, CPL_TYPE_FLOAT));

    sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_SLICLO, k);
    check(
	  cpl_table_new_column(table, colname, CPL_TYPE_FLOAT));
  }
  check( cpl_table_new_column( table, XSH_ORDER_TABLE_DEGY, CPL_TYPE_INT ) ) ;

  check(
    cpl_table_new_column(table,XSH_ORDER_TABLE_COLNAME_STARTY,
     CPL_TYPE_INT));
  check(
    cpl_table_new_column(table,XSH_ORDER_TABLE_COLNAME_ENDY,
     CPL_TYPE_INT));
  check(cpl_table_set_size(table,order_list->size));


  /* insert data */
  for(i=0;i<order_list->size;i++){
    check(cpl_table_set_int(table,XSH_ORDER_TABLE_COLNAME_ORDER,
      i,order_list->list[i].order));
    check(cpl_table_set_int(table,XSH_ORDER_TABLE_COLNAME_ABSORDER,
      i,order_list->list[i].absorder));
        check(cpl_table_set_int(table,XSH_ORDER_TABLE_COLNAME_STARTY,
      i,order_list->list[i].starty));
    check(cpl_table_set_int(table,XSH_ORDER_TABLE_COLNAME_ENDY,
      i,order_list->list[i].endy));

    for( k = 0 ; k <= pol_degree ; k++ ) {
      double coef ;

      /* Get and Write coef from center polynomial */
      check(coef = cpl_polynomial_get_coeff(order_list->list[i].cenpoly,&k));
      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_CENTER, k);
      check(cpl_table_set(table, colname,i,coef));

      /* Get and Write coef from upper polynomial */
      if ( order_list->list[i].edguppoly != NULL ) {
        check(coef = cpl_polynomial_get_coeff(order_list->list[i].edguppoly,&k));
      }
      else coef = 0.0 ;
      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_EDGUP, k);
      check(cpl_table_set(table, colname,i,coef));

      /* Get and Write coef from lower polynomial */
      if ( order_list->list[i].edglopoly != NULL ) {
        check(coef = cpl_polynomial_get_coeff(order_list->list[i].edglopoly,&k));
      }
      else coef = 0.0;
      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_EDGLO, k);
      check(cpl_table_set(table, colname,i,coef));

    /* Get and Write coef from slic lower polynomial */
      if ( order_list->list[i].sliclopoly != NULL ) {
	check(coef = cpl_polynomial_get_coeff(order_list->list[i].sliclopoly,&k));
      }
      else coef = 0.0 ;
      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_SLICLO, k);
      check(cpl_table_set(table, colname, i, coef));

    /* Get and Write coef from slic upper polynomial */
      if ( order_list->list[i].slicuppoly != NULL ) {
	check(coef = cpl_polynomial_get_coeff(order_list->list[i].slicuppoly,&k));
      }
      else coef = 0.0 ;
      sprintf(colname, "%s%"CPL_SIZE_FORMAT, XSH_ORDER_TABLE_COLNAME_SLICUP, k);
      check(cpl_table_set(table, colname, i, coef));

    }
    check( cpl_table_set(table, XSH_ORDER_TABLE_DEGY, i, pol_degree ) ) ;
  }


  //Now we dump detailed centre/edge/slicer traces info in a table extension
  nraw = order_list->size*(ny/PointStep+0.5);

  check( tabqc = cpl_table_new(nraw));
  cpl_table_new_column(tabqc,XSH_ORDER_TABLE_COLNAME_ORDER,CPL_TYPE_INT);
  cpl_table_new_column(tabqc,XSH_ORDER_TABLE_COLNAME_ABSORDER,CPL_TYPE_INT);
  cpl_table_new_column(tabqc,XSH_ORDER_TABLE_COLNAME_CENTERX,CPL_TYPE_DOUBLE);
  cpl_table_new_column(tabqc,XSH_ORDER_TABLE_COLNAME_CENTERY,CPL_TYPE_DOUBLE);

  cpl_table_fill_column_window(tabqc,XSH_ORDER_TABLE_COLNAME_ORDER,0,nraw,-1);
  cpl_table_fill_column_window(tabqc,XSH_ORDER_TABLE_COLNAME_ABSORDER,0,nraw,-1);
  cpl_table_fill_column_window(tabqc,XSH_ORDER_TABLE_COLNAME_CENTERX,0,nraw,-1);
  cpl_table_fill_column_window(tabqc,XSH_ORDER_TABLE_COLNAME_CENTERY,0,nraw,-1);

  porder=cpl_table_get_data_int(tabqc,XSH_ORDER_TABLE_COLNAME_ORDER);
  pabsorder=cpl_table_get_data_int(tabqc,XSH_ORDER_TABLE_COLNAME_ABSORDER);
  pcenterx=cpl_table_get_data_double(tabqc,XSH_ORDER_TABLE_COLNAME_CENTERX);
  pcentery=cpl_table_get_data_double(tabqc,XSH_ORDER_TABLE_COLNAME_CENTERY);

  if ( XSH_CMP_TAG_LAMP( tag, XSH_ORDER_TAB_EDGES)){
    cpl_table_new_column(tabqc,XSH_ORDER_TABLE_COLNAME_EDGLOX,CPL_TYPE_DOUBLE);
    cpl_table_new_column(tabqc,XSH_ORDER_TABLE_COLNAME_EDGUPX,CPL_TYPE_DOUBLE);
    cpl_table_fill_column_window(tabqc,XSH_ORDER_TABLE_COLNAME_EDGLOX,0,nraw,-1);
    cpl_table_fill_column_window(tabqc,XSH_ORDER_TABLE_COLNAME_EDGUPX,0,nraw,-1);
    pedgelox=cpl_table_get_data_double(tabqc,XSH_ORDER_TABLE_COLNAME_EDGLOX);
    pedgeupx=cpl_table_get_data_double(tabqc,XSH_ORDER_TABLE_COLNAME_EDGUPX);

    if ( order_list->list[0].slicuppoly != NULL ) {
      cpl_table_new_column(tabqc,XSH_ORDER_TABLE_COLNAME_SLICLOX,CPL_TYPE_DOUBLE);
      cpl_table_new_column(tabqc,XSH_ORDER_TABLE_COLNAME_SLICUPX,CPL_TYPE_DOUBLE);

      cpl_table_fill_column_window(tabqc,XSH_ORDER_TABLE_COLNAME_SLICLOX,0,nraw,-1);
      cpl_table_fill_column_window(tabqc,XSH_ORDER_TABLE_COLNAME_SLICUPX,0,nraw,-1);

      psliclox=cpl_table_get_data_double(tabqc,XSH_ORDER_TABLE_COLNAME_SLICLOX);
      pslicupx=cpl_table_get_data_double(tabqc,XSH_ORDER_TABLE_COLNAME_SLICUPX);
    }
  }
  /* insert qc data */
  //Note that to work with ds9 coordinates we add 0.5 both to x and y pos 
  k=0;
  for( iorder=0; iorder< order_list->size; iorder++){
    int starty, endy;

    xsh_msg_dbg_high(" Produce solution for order %d",
            order_list->list[iorder].absorder);
    /* AMO this fixes the problem with EDGE order table found for UVB at com1*/
    check( starty = order_list->list[iorder].starty);
    check( endy = order_list->list[iorder].endy);
#if 0
    if ( starty == -1 && endy == -1 ) {
      xsh_msg( " **** Order %d Discarded", order_list->list[iorder].absorder);
      continue ;
    }
#endif
    if (starty == 0){
      xsh_msg("Warning starty equal zero, put starty ==> 1");
      starty = 1;
    }
    if ( endy == 0){
      xsh_msg("Warning starty equal zero, put endy ==> %d",ny);
      endy = ny;
    }
    /* xsh_msg("Y start %d %d",starty,endy); */
    for( iy=starty; iy<=endy; iy= iy+PointStep){
      float dx;

      check( dx = cpl_polynomial_eval_1d( order_list->list[iorder].cenpoly,
        iy, NULL));
      porder[k]=iorder;
      pabsorder[k]=order_list->list[iorder].absorder;
      pcenterx[k]=dx;
      pcentery[k]=iy;
      /* xsh_msg("cx=%g cy=%d pcx=%g pcy=%g",dx,iy,pcenterx[k],pcentery[k]); */
      if ( XSH_CMP_TAG_LAMP( tag, XSH_ORDER_TAB_EDGES)){
         check(dx=cpl_polynomial_eval_1d(order_list->list[iorder].edglopoly,
                                         iy, NULL ) ) ;
         pedgelox[k]=dx;
         check(dx=cpl_polynomial_eval_1d(order_list->list[iorder].edguppoly,
                                         iy, NULL ) ) ;
         pedgeupx[k]=dx;
         /* Check if IFU */
         if ( order_list->list[iorder].slicuppoly != NULL ) {
            check(dx=cpl_polynomial_eval_1d(order_list->list[iorder].slicuppoly,
                                            iy, NULL ) ) ;
            pslicupx[k]=dx;
         }
         if ( order_list->list[iorder].sliclopoly != NULL ) {
            check(dx=cpl_polynomial_eval_1d(order_list->list[iorder].sliclopoly,
                                            iy, NULL ) ) ;
            psliclox[k]=dx;
         }
      } //end check on edges
      k++;
    } //end loop on y
    if(starty!=-999) {
       /* -999 can occur only in QC mode and is used as flag to skip 
          edge detection on a given order. This was explitly asked by QC.
          Normal user mode should never have this behaviour.
        */
       norder++;
    }
  } //end loop on orders
 
  check( cpl_table_and_selected_int( tabqc, XSH_ORDER_TABLE_COLNAME_ORDER,
    CPL_GREATER_THAN, -1));
  
  tab_ext = cpl_table_extract_selected( tabqc);

  /* create fits file */
  check( xsh_pfits_set_pcatg( order_list->header, tag));
  if (strstr(tag, "EDGE") != NULL) {
        cpl_propertylist_append_int(order_list->header,XSH_QC_ORD_EDGE_NDET, norder);
        cpl_propertylist_set_comment(order_list->header,XSH_QC_ORD_EDGE_NDET,
                                     "number of detected order edges");
     }
  check( cpl_table_save(table, order_list->header, NULL, filename, 
    CPL_IO_DEFAULT));
  check( cpl_table_save(tab_ext, NULL, NULL, filename, 
    CPL_IO_EXTEND));

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

  cleanup:
    xsh_free_table( &tab_ext);
    xsh_free_table( &tabqc);
    xsh_free_table( &table);
    return result;
}


void xsh_order_split_qth_d2( cpl_frame* order_tab_frame,
  cpl_frame* spectralformat_frame, cpl_frame** qth_order_tab_frame,
  cpl_frame** d2_order_tab_frame, xsh_instrument *instr)
{
  cpl_table *order_tab = NULL;
  cpl_table *order_tab_ext = NULL;
  const char* order_tab_name = NULL;
  int order_tab_size;
  cpl_table *order_tab_qth = NULL;
  cpl_table *order_tab_d2 = NULL;
  cpl_table *order_tab_ext_qth = NULL;
  cpl_table *order_tab_ext_d2 = NULL;
  xsh_spectralformat_list *spectralformat = NULL;
  cpl_propertylist *header = NULL;
  int i=0;

  XSH_ASSURE_NOT_NULL( order_tab_frame);
  XSH_ASSURE_NOT_NULL( spectralformat_frame);
  XSH_ASSURE_NOT_NULL( qth_order_tab_frame);
  XSH_ASSURE_NOT_NULL( d2_order_tab_frame);
  XSH_ASSURE_NOT_NULL( instr);

  check( spectralformat = xsh_spectralformat_list_load( spectralformat_frame,
    instr));
  check( order_tab_name = cpl_frame_get_filename( order_tab_frame));
  XSH_TABLE_LOAD( order_tab, order_tab_name);
  check(order_tab_ext=cpl_table_load(order_tab_name,2,0));
  check( header = cpl_propertylist_load( order_tab_name, 0));
  check( order_tab_size = cpl_table_get_nrow( order_tab));
  for (i=0; i< order_tab_size; i++){
    int absorder;
    const char* lamp = NULL;

    check( xsh_get_table_value( order_tab, XSH_ORDER_TABLE_COLNAME_ABSORDER,
      CPL_TYPE_INT, i, &absorder));
    check( lamp = xsh_spectralformat_list_get_lamp( spectralformat,
      absorder));
    XSH_ASSURE_NOT_NULL( lamp);
    if ( strcmp(lamp,"QTH")==0){
      check( cpl_table_select_row( order_tab, i));
    }
    else{
      check( cpl_table_unselect_row( order_tab, i));
    }
  }

  check( order_tab_qth = cpl_table_extract_selected( order_tab));
  check( cpl_table_not_selected( order_tab));
  check( order_tab_d2 = cpl_table_extract_selected( order_tab));

  cpl_table_and_selected_int(order_tab_ext,"ABSORDER",CPL_GREATER_THAN,XSH_ORDER_MIN_UVB_D2-1);
  check( order_tab_ext_d2 = cpl_table_extract_selected( order_tab_ext));
  cpl_table_select_all(order_tab_ext);

  cpl_table_and_selected_int(order_tab_ext,"ABSORDER",CPL_LESS_THAN,XSH_ORDER_MAX_UVB_QTH+1);
  check( order_tab_ext_qth = cpl_table_extract_selected( order_tab_ext));


  check( cpl_table_save( order_tab_qth, header, NULL, "ORDER_TAB_CENTR_QTH_UVB.fits", CPL_IO_DEFAULT));
  check( cpl_table_save( order_tab_ext_qth, header, NULL, "ORDER_TAB_CENTR_QTH_UVB.fits", CPL_IO_EXTEND));
  check( cpl_table_save( order_tab_d2, header, NULL, "ORDER_TAB_CENTR_D2_UVB.fits", CPL_IO_DEFAULT));
  check( cpl_table_save( order_tab_ext_d2, header, NULL, "ORDER_TAB_CENTR_D2_UVB.fits", CPL_IO_EXTEND));
  xsh_add_temporary_file("ORDER_TAB_CENTR_QTH_UVB.fits");
  xsh_add_temporary_file("ORDER_TAB_CENTR_D2_UVB.fits");
  *qth_order_tab_frame = cpl_frame_new();
  cpl_frame_set_filename( *qth_order_tab_frame, "ORDER_TAB_CENTR_QTH_UVB.fits");
  *d2_order_tab_frame = cpl_frame_new();
  cpl_frame_set_filename( *d2_order_tab_frame, "ORDER_TAB_CENTR_D2_UVB.fits");  
  cleanup:
    xsh_spectralformat_list_free( &spectralformat);
    xsh_free_propertylist( &header);
    xsh_free_table( &order_tab);
    xsh_free_table( &order_tab_qth);
    xsh_free_table( &order_tab_d2);
    xsh_free_table( &order_tab_ext);
    xsh_free_table( &order_tab_ext_d2);
    xsh_free_table( &order_tab_ext_qth);

    return;
}
/**@}*/
