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

/*
 * $Author: amodigli $
 * $Date: 2012-12-18 14:15:44 $
 * $Revision: 1.38 $
 * $Name: not supported by cvs2svn $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_data_dispersol Dispersion solution 
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/


/**@{*/


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

#include <math.h>

#include <xsh_data_dispersol.h>
#include <xsh_utils.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <xsh_dfs.h>
#include <cpl.h>
#include <xsh_utils_table.h>
#include <xsh_data_spectralformat.h>
#include <xsh_data_order.h>


/*----------------------------------------------------------------------------
                                 Function implementation
 ----------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
/** 
 * @brief
 *   Create a new dispersion solution list
 * @param[in] size
 *   The size of the diseprsion list
 * @param[in] degx poly degree on X direction
 * @param[in] degy poly degree on Y direction
 * @param[in] instrument instrument (arm) setting
 * @return
 *   The new ALLOCATED dispersion list
 */
/*--------------------------------------------------------------------------*/
xsh_dispersol_list* xsh_dispersol_list_new( int size, int degx, int degy,
  xsh_instrument *instrument)
{
  xsh_dispersol_list *result = NULL;

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

  XSH_CALLOC( result, xsh_dispersol_list, 1);
  result->size = size;
  result->degx = degx;
  result->degy = degy;
  check( result->binx = xsh_instrument_get_binx( instrument)); 
  check( result->biny = xsh_instrument_get_biny( instrument));
  XSH_CALLOC( result->list, xsh_dispersol, result->size);
  XSH_NEW_PROPERTYLIST( result->header);

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_dispersol_list_free( &result);
    }
    return result;
}
/*--------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------*/
/**
 * @brief
 *   Load a dispersion list from a frame
 * @param[in] frame
 *   The frame contatining the dispersion list
 * @param[in] instrument instrument (arm) setting
 * @return A new allocated dispersion list
 */
/*--------------------------------------------------------------------------*/
xsh_dispersol_list* xsh_dispersol_list_load( cpl_frame *frame, 
  xsh_instrument *instrument)
{
  xsh_dispersol_list *result = NULL;
  const char* tablename = NULL;
  cpl_table* table = NULL;
  int rows;
  //int cols ;
  int degx, degy;
  char coefname[20];
  int i;

  /* check input parameters */
  XSH_ASSURE_NOT_ILLEGAL( frame);

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

  XSH_TABLE_LOAD( table, tablename);
  check( rows = cpl_table_get_nrow( table));
  //check( cols = cpl_table_get_ncol( table));


  check( xsh_get_table_value( table, XSH_DISPERSOL_TABLE_COLNAME_DEGX,
    CPL_TYPE_INT, 0, &degx));

  check( xsh_get_table_value( table, XSH_DISPERSOL_TABLE_COLNAME_DEGY,
    CPL_TYPE_INT, 0, &degy));

  check( result = xsh_dispersol_list_new( rows/2, degx, degy, instrument));

  for(i=0; i< rows/2; i++){
    cpl_polynomial *lambda_poly = NULL;
    cpl_polynomial *slit_poly = NULL;
    cpl_size pows[2];
    int k,l, absorder = 0;
    int j = 2*i;

    check( xsh_get_table_value( table, XSH_DISPERSOL_TABLE_COLNAME_ORDER,
      CPL_TYPE_INT, j, &absorder));
    check( lambda_poly = cpl_polynomial_new( 2));
    check( slit_poly = cpl_polynomial_new( 2));
    for( k=0; k<= degx; k++){
      for( l=0;  l<= degy; l++){
        double coef_lambda = 0.0, coef_slit = 0.0;
        sprintf(coefname,"C%d%d",k,l);
        check( xsh_get_table_value( table, coefname,CPL_TYPE_DOUBLE, j, 
          &coef_lambda));
        check( xsh_get_table_value( table, coefname,CPL_TYPE_DOUBLE, j+1,
          &coef_slit));
        pows[0] =k;
        pows[1] = l;
        check( cpl_polynomial_set_coeff( lambda_poly, pows, coef_lambda));
        check( cpl_polynomial_set_coeff( slit_poly, pows, coef_slit));
      }
    } 
    check( xsh_dispersol_list_add( result, i, absorder, lambda_poly, slit_poly));
  }

  cleanup:
    xsh_free_table( &table);
    if ( cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_dispersol_list_free( &result);
    }
    return result;
}
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
/**
 * @brief
 *   Add a dispersion solution in the list
 * @param[in] list
 *   The dispersion list
 * @param[in] idx
 *   The index in the list
 * @param[in] absorder
 *   The absolute order
 * @param[in] lambda_poly
 *   The lambda 2D polynomial solution
 * @param[in] slit_poly
 *   The slit 2D polynomial solution
 */
/*--------------------------------------------------------------------------*/
void xsh_dispersol_list_add( xsh_dispersol_list *list, int idx, 
  int absorder, cpl_polynomial *lambda_poly, 
  cpl_polynomial *slit_poly)
{
  /* check input parameters */
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( lambda_poly);
  XSH_ASSURE_NOT_NULL( slit_poly);
  XSH_ASSURE_NOT_ILLEGAL( idx >=0 && idx < list->size);

  list->list[idx].absorder = absorder;
  list->list[idx].lambda_poly = lambda_poly;
  list->list[idx].slit_poly = slit_poly;

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

/*--------------------------------------------------------------------------*/
/**
 * @brief
 *   Free the dispersion list
 * @param[in] list
 *   The list to free
 */
/*--------------------------------------------------------------------------*/
void xsh_dispersol_list_free( xsh_dispersol_list **list)
{ 
  int i = 0;

  if ( list && *list){
    /* free the list */
    for( i = 0; i < (*list)->size; i++){
      xsh_free_polynomial( &(*list)->list[i].lambda_poly);
      xsh_free_polynomial( &(*list)->list[i].slit_poly);
    }
    if ((*list)->list){
      cpl_free( (*list)->list);
    }
    xsh_free_propertylist( &((*list)->header));
    cpl_free( *list);
    *list = NULL;
  }
}
/*--------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------*/
/**
 * @brief
 *   Save a WAVE MAP image
 * @param[in] list
 *   The dispersion list
 * @param[in] order_frame
 *   The order list frame
 * @param[in] pre
 *   The pre image
 * @param[in] instr instrument (arm) setting
 * @param[in] tag product category
 * @return
 *   The wave map frame
 */
/*--------------------------------------------------------------------------*/
cpl_frame* xsh_dispersol_list_to_wavemap( xsh_dispersol_list *list,
                                          cpl_frame *order_frame, 
                                          xsh_pre *pre, 
                                          xsh_instrument *instr,
                                          const char* tag)

{
  cpl_frame *result = NULL;
  cpl_image *image = NULL;
  xsh_order_list *order_list = NULL;
  int nx, ny;
  cpl_vector* pos = NULL;
  double *data = NULL;
  int i, y;
  char filename[256];
  cpl_propertylist *wavemap_header = NULL;

  /* Check input */
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( order_frame);
  XSH_ASSURE_NOT_NULL( pre);
  XSH_ASSURE_NOT_NULL( instr);  
 
  /* Load data */
  check( order_list = xsh_order_list_load( order_frame, instr));   
  nx = pre->nx;
  ny = pre->ny;
  pos = cpl_vector_new(2);

  
  check( image = cpl_image_new( nx, ny, CPL_TYPE_DOUBLE));
  check( data = cpl_image_get_data_double( image));
  check( wavemap_header = cpl_propertylist_new());


  for( i=0; i < list->size; i++) {
    int starty, endy;
    double lambda_min, lambda_max;
    double lambda_starty_lo, lambda_starty_up, lambda_endy_lo, lambda_endy_up;
    double dxmin, dxmax ;
    int xmin, xmax;
    int absorder;


    absorder = order_list->list[i].absorder;
    check( starty = xsh_order_list_get_starty( order_list, i));

    check( endy = xsh_order_list_get_endy( order_list, i));


 for ( y=starty ; y<=endy && y<ny; y++){
      int x;

      /*    get xmin, xmax from order table*/
      check( dxmin = xsh_order_list_eval( order_list,
					  order_list->list[i].edglopoly,
					  (double)y ) ) ;
      check( dxmax = xsh_order_list_eval( order_list,
					  order_list->list[i].edguppoly,
					  (double)y ) ) ;
      xmin = ceil( dxmin);
      xmax = floor( dxmax);
      for ( x=xmin ; x<=xmax && x<nx; x++){
        double lambda;

        cpl_vector_set(pos, 0, (double)x);
        cpl_vector_set(pos, 1, (double)y);
	/*calculate lambda(x,y)*/

	check( lambda = xsh_dispersol_list_eval( 
          list, list->list[i].lambda_poly, pos)); 	
	/*add lambda to image */
	data[x-1+(y-1)*nx] = (float)lambda;

      }

    }

    /* compute maxium and minimum of possible wavelength for this order */
    check( dxmin = xsh_order_list_eval( order_list,
                                          order_list->list[i].edglopoly,
                                          (double)starty));
    check( dxmax = xsh_order_list_eval( order_list,
                                          order_list->list[i].edguppoly,
                                          (double)starty));
    xmin = ceil( dxmin);
    xmax = floor( dxmax);

    cpl_vector_set(pos, 0, (double)xmin);
    cpl_vector_set(pos, 1, (double)starty);
    check( lambda_starty_lo = xsh_dispersol_list_eval(
      list, list->list[i].lambda_poly, pos));

    cpl_vector_set(pos, 0, (double)xmax);
    check( lambda_starty_up = xsh_dispersol_list_eval(
      list, list->list[i].lambda_poly, pos));

    check( dxmin = xsh_order_list_eval( order_list,
                                          order_list->list[i].edglopoly,
                                          (double)endy));
    check( dxmax = xsh_order_list_eval( order_list,
                                          order_list->list[i].edguppoly,
                                          (double)endy));
    xmin = ceil( dxmin);
    xmax = floor( dxmax);

    cpl_vector_set(pos, 0, (double)xmin);
    cpl_vector_set(pos, 1, (double)endy);
    check( lambda_endy_lo = xsh_dispersol_list_eval(
      list, list->list[i].lambda_poly, pos));

    cpl_vector_set(pos, 0, (double)xmax);
    check( lambda_endy_up = xsh_dispersol_list_eval(
      list, list->list[i].lambda_poly, pos));

    if ( lambda_starty_lo < lambda_endy_lo 
      && lambda_starty_up < lambda_endy_up){
      if ( lambda_starty_lo > lambda_starty_up){
        lambda_min = lambda_starty_lo;
      }
      else{
        lambda_min = lambda_starty_up;
      }
      if ( lambda_endy_lo > lambda_endy_up){
        lambda_max = lambda_endy_up;
      }
      else{
        lambda_max = lambda_endy_lo;
      }
    }
    else if ( lambda_starty_lo > lambda_endy_lo 
      && lambda_starty_up > lambda_endy_up){
      if ( lambda_starty_lo > lambda_starty_up){
        lambda_max = lambda_starty_up;
      }
      else{
        lambda_max = lambda_starty_lo;
      }
      if ( lambda_endy_lo > lambda_endy_up){
        lambda_min = lambda_endy_lo;
      }
      else{
        lambda_min = lambda_endy_up;
      }
    }
    else{
      xsh_msg_warning( 
        "wavelength variation differs between upper(%f %f)  and lower edges( %f %f)",
        lambda_starty_up, lambda_endy_up, lambda_endy_up, lambda_endy_lo);
      lambda_min = 0.0;
      lambda_max = 0.0;
    }

    /* write KW */
    check( xsh_pfits_set_wavemap_order_lambda_min( 
      wavemap_header, absorder, lambda_min));
    check( xsh_pfits_set_wavemap_order_lambda_max( 
      wavemap_header, absorder, lambda_max));
  }

  sprintf(filename,"%s.fits",tag);
  check( xsh_pfits_set_pcatg( wavemap_header, tag));

  check( cpl_image_save( image, filename, CPL_BPP_IEEE_FLOAT, 
    wavemap_header, CPL_IO_DEFAULT));

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

  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_frame( &result);
    }
    xsh_free_image ( &image);
    xsh_free_propertylist( &wavemap_header);
    xsh_order_list_free( &order_list);
    xsh_free_vector( &pos);


    return result;
}
/*--------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------*/
/**
 * @brief
 *   Save a SLIT MAP image
 * @param[in] list
 *   The dispersion list
 * @param[in] order_frame
 *   The order list frame
 * @param[in] pre
 *   The pre image
 * @param[in] instr instrument (arm) setting
 * @param[in] tag product tag
 * @return
 *   The slit map frame
 */
/*--------------------------------------------------------------------------*/
cpl_frame* xsh_dispersol_list_to_slitmap( xsh_dispersol_list *list,
					  cpl_frame *order_frame, 
					  xsh_pre *pre,
					  xsh_instrument *instr,
					  const char* tag)
{
  cpl_frame *result = NULL;
  cpl_image *image = NULL;
  cpl_image *ifu_map = NULL;
  xsh_order_list *order_list = NULL;
  int nx, ny;
  cpl_vector* pos = NULL;
  double *data = NULL;
  int *imap = NULL;
  double crpix1=1.;
  double crpix2=1.;
  double crval1=1.;
  double crval2=1.;
  double cdelt1=1.;
  double cdelt2=1.;

  int i, y;
  char filename[256];
  cpl_vector *med_low_vect = NULL, *med_up_vect = NULL, *med_cen_vect = NULL;
  cpl_vector *med_sliclo_vect = NULL, *med_slicup_vect = NULL;
  int nb_orders;
  double slit_up, slit_low, slit_cen;
  cpl_propertylist *slitmap_header = NULL;

  /* Check input */
  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( order_frame);
  XSH_ASSURE_NOT_NULL( pre);
  XSH_ASSURE_NOT_NULL( instr);  

  /* Load data */
  check( order_list = xsh_order_list_load( order_frame, instr)); 
  nx = pre->nx;
  ny = pre->ny;

  /* Check that the binning are the same */
  XSH_ASSURE_NOT_ILLEGAL( order_list->bin_x == pre->binx);
  XSH_ASSURE_NOT_ILLEGAL( order_list->bin_x == list->binx);

  pos = cpl_vector_new(2);

  check( image = cpl_image_new( nx, ny, CPL_TYPE_DOUBLE));
  check( data = cpl_image_get_data_double( image));
  check( ifu_map = cpl_image_new( nx, ny, CPL_TYPE_INT));
  check( imap = cpl_image_get_data_int( ifu_map));
  check( slitmap_header = cpl_propertylist_new());

  nb_orders = list->size;

  med_low_vect = cpl_vector_new( nb_orders);
  med_cen_vect = cpl_vector_new( nb_orders);
  med_up_vect = cpl_vector_new( nb_orders);
  if ( xsh_instrument_get_mode( instr) == XSH_MODE_IFU){
    med_sliclo_vect = cpl_vector_new( nb_orders);
    med_slicup_vect = cpl_vector_new( nb_orders);
  }

 
  for( i=0; i < nb_orders; i++) {
    int starty, endy, vect_size;
    int absorder;
    cpl_vector *low_vect = NULL, *up_vect = NULL, *cen_vect = NULL; 
    cpl_vector *sliclo_vect = NULL, *slicup_vect = NULL;

    check( starty = xsh_order_list_get_starty( order_list, i));
    check( endy = xsh_order_list_get_endy( order_list, i));
    absorder = order_list->list[i].absorder;

    //vect_size = endy-starty+1;
    int ymax = ( endy < ny ) ? endy:(ny-1);

    vect_size = ymax-starty+1;
/*
    xsh_msg("starty=%d endy=%d ny=%d ymax=%d vect_size=%d",
            starty,endy,ny,ymax,vect_size);
*/
    if(vect_size<1) {
       /* exit in vcase of 0 or negative sixze of vector */
     continue;

    }
    low_vect = cpl_vector_new( vect_size);
    cen_vect = cpl_vector_new( vect_size);
    up_vect = cpl_vector_new( vect_size);

    if ( xsh_instrument_get_mode( instr) == XSH_MODE_IFU){
      sliclo_vect = cpl_vector_new( vect_size);
      slicup_vect = cpl_vector_new( vect_size);
    }

    for ( y=starty ; y<=ymax; y++){
       //for ( y=starty ; y<=endy && y<ny; y++){
      double dxmin, dxmax, xcen;
      int xmin, xmax, x;
      double slit;
      double ypos;

      /*    get xmin, xmax */
      check( dxmin = xsh_order_list_eval( order_list,
					  order_list->list[i].edglopoly,
					  (double)y ) ) ;
      check( dxmax = xsh_order_list_eval( order_list,
					  order_list->list[i].edguppoly,
					  (double)y ) ) ;
      check( xcen = xsh_order_list_eval( order_list,
                                          order_list->list[i].cenpoly,
                                          (double)y ) ) ;

      xmin = ceil( dxmin);
      xmax = floor( dxmax);

      ypos = (double)y;
      cpl_vector_set(pos, 1, ypos);

      for ( x=xmin ; x<=xmax && x<nx; x++){
        double xpos;

        xpos = (double)x;
        cpl_vector_set(pos, 0, xpos);
        cpl_vector_set(pos, 1, ypos);
	/*calculate lambda(x,y)*/
	check( slit = xsh_dispersol_list_eval( list, list->list[i].slit_poly, pos));

	/*add lambda to image */
	data[x-1+(y-1)*nx] = (float)slit;
	imap[x-1+(y-1)*nx] = 1;
      }

      cpl_vector_set(pos, 0, xmin);
      cpl_vector_set(pos, 1, ypos);
      check( slit = xsh_dispersol_list_eval( list, list->list[i].slit_poly, pos));
      cpl_vector_set( low_vect, y-starty, slit);

      cpl_vector_set(pos, 0, xmax);
      cpl_vector_set(pos, 1, ypos);
      check( slit = xsh_dispersol_list_eval( list, list->list[i].slit_poly, pos));
      cpl_vector_set( up_vect, y-starty, slit);

      cpl_vector_set(pos, 0, xcen);
      cpl_vector_set(pos, 1, ypos);
      check( slit = xsh_dispersol_list_eval( list, list->list[i].slit_poly, pos));
      cpl_vector_set( cen_vect, y-starty, slit);
 
      if ( xsh_instrument_get_mode( instr) == XSH_MODE_IFU){
        xmin = xsh_order_list_eval( order_list,
                                          order_list->list[i].sliclopoly,
                                          (double)y );
        xmax = xsh_order_list_eval( order_list,
                                          order_list->list[i].slicuppoly,
                                          (double)y );
        cpl_vector_set(pos, 0, xmin);
        cpl_vector_set(pos, 1, ypos);
        check( slit = xsh_dispersol_list_eval( list, list->list[i].slit_poly, pos));
        cpl_vector_set( sliclo_vect, y-starty, slit);

        cpl_vector_set(pos, 0, xmax);
        cpl_vector_set(pos, 1, ypos);
        check( slit = xsh_dispersol_list_eval( list, list->list[i].slit_poly, pos));
        cpl_vector_set( slicup_vect, y-starty, slit);
      }

    }

    check( slit_up = cpl_vector_get_median( up_vect));
    check( slit_low = cpl_vector_get_median( low_vect));
    check( slit_cen = cpl_vector_get_median( cen_vect));
    //xsh_msg("med up=%g low=%g cen=%g",slit_up,slit_low,slit_cen);

    /* write some KW in header */
    check( xsh_pfits_set_slitmap_order_cen( slitmap_header, absorder, slit_cen));
    check( cpl_vector_set( med_cen_vect, i, slit_cen));

    if ( slit_up > slit_low){
      check( xsh_pfits_set_slitmap_order_edgup( slitmap_header, absorder, slit_up));
      check( xsh_pfits_set_slitmap_order_edglo( slitmap_header, absorder, slit_low));
      check( cpl_vector_set( med_low_vect, i, slit_low));
      check( cpl_vector_set( med_up_vect, i, slit_up));
    }
    else{
      check( xsh_pfits_set_slitmap_order_edgup( slitmap_header, absorder, slit_low));
      check( xsh_pfits_set_slitmap_order_edglo( slitmap_header, absorder, slit_up));
      check( cpl_vector_set( med_low_vect, i, slit_up));
      check( cpl_vector_set( med_up_vect, i, slit_low));
    }

    if ( xsh_instrument_get_mode( instr) == XSH_MODE_IFU){
      check( slit_up = cpl_vector_get_median( slicup_vect));
      check( slit_low = cpl_vector_get_median( sliclo_vect));
      if ( slit_up > slit_low){
        check( xsh_pfits_set_slitmap_order_slicup( slitmap_header, absorder, slit_up));
        check( xsh_pfits_set_slitmap_order_sliclo( slitmap_header, absorder, slit_low));
        check( cpl_vector_set( med_sliclo_vect, i, slit_low));
        check( cpl_vector_set( med_slicup_vect, i, slit_up));
      }
      else{
        check( xsh_pfits_set_slitmap_order_slicup( slitmap_header, absorder, slit_low));
        check( xsh_pfits_set_slitmap_order_sliclo( slitmap_header, absorder, slit_up));
        check( cpl_vector_set( med_sliclo_vect, i, slit_up));
        check( cpl_vector_set( med_slicup_vect, i, slit_low));
      }
      xsh_free_vector( &slicup_vect);
      xsh_free_vector( &sliclo_vect);
    }
    xsh_free_vector( &up_vect);
    xsh_free_vector( &low_vect);
    xsh_free_vector( &cen_vect);
  }

  check( slit_cen = cpl_vector_get_median( med_cen_vect));
  check( slit_up = cpl_vector_get_median( med_up_vect));
  check( slit_low = cpl_vector_get_median( med_low_vect));

  check( xsh_pfits_set_slitmap_median_cen( slitmap_header, slit_cen));
  check( xsh_pfits_set_slitmap_median_edgup( slitmap_header, slit_up));
  check( xsh_pfits_set_slitmap_median_edglo( slitmap_header, slit_low));

  if ( xsh_instrument_get_mode( instr) == XSH_MODE_IFU){
    check( slit_up = cpl_vector_get_median( med_slicup_vect));
    check( slit_low = cpl_vector_get_median( med_sliclo_vect));
    check( xsh_pfits_set_slitmap_median_slicup( slitmap_header, slit_up));
    check( xsh_pfits_set_slitmap_median_sliclo( slitmap_header, slit_low));
  }

  sprintf(filename,"%s.fits",tag);
  xsh_pfits_set_pcatg( slitmap_header, tag);
  cdelt1=xsh_instrument_get_binx(instr);
  cdelt2=xsh_instrument_get_biny(instr);
  xsh_pfits_set_wcs(slitmap_header,crpix1,crval1,cdelt1,crpix2,crval2,cdelt2);
  check( cpl_image_save( image, filename, CPL_BPP_IEEE_FLOAT, 
    slitmap_header, CPL_IO_DEFAULT));

  if ( xsh_instrument_get_mode( instr) == XSH_MODE_IFU){
    check( cpl_image_save( ifu_map, filename, CPL_BPP_32_SIGNED,
			   NULL, CPL_IO_EXTEND));
  }
  check( result = xsh_frame_product(filename,tag,
    CPL_FRAME_TYPE_IMAGE, CPL_FRAME_GROUP_PRODUCT,
    CPL_FRAME_LEVEL_TEMPORARY));

  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_frame( &result);
    }
    
    xsh_free_image ( &image);
    xsh_free_image ( &ifu_map);
    xsh_free_propertylist( &slitmap_header);
    xsh_order_list_free( &order_list);
    xsh_free_vector( &pos);
    xsh_free_vector( &med_up_vect);
    xsh_free_vector( &med_low_vect);
    xsh_free_vector( &med_cen_vect);
    xsh_free_vector( &med_slicup_vect);
    xsh_free_vector( &med_sliclo_vect);

    return result;
}
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
/**
  @brief
    Evaluate the polynomial according the binning
  @param[in] list 
    The dispersion list
  @param[in] poly 
    The slit or wave poly
  @param[in] pos 
    The position x, y in a vector
  @return
    The evaluation of the polynomial at the given position
*/
/*--------------------------------------------------------------------------*/
double 
xsh_dispersol_list_eval( xsh_dispersol_list *list, 
			 cpl_polynomial *poly, 
			 cpl_vector *pos)
{
  double result=0;
  double y_bin, y_no_bin;
  double x_bin, x_no_bin;

  XSH_ASSURE_NOT_NULL( list);
  XSH_ASSURE_NOT_NULL( pos);
  XSH_ASSURE_NOT_NULL( poly);

  check( x_bin = cpl_vector_get(pos, 0));
  check( y_bin = cpl_vector_get(pos, 1));

  x_no_bin = convert_bin_to_data( x_bin, list->binx);
  y_no_bin = convert_bin_to_data( y_bin, list->biny);
  
  check( cpl_vector_set(pos, 0, x_no_bin));
  check( cpl_vector_set(pos, 1, y_no_bin));

  check( result = cpl_polynomial_eval( poly, pos));  

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

/*--------------------------------------------------------------------------*/
/**
 * @brief
 *   Save a dispersion list on the disk
 *
 * @param list dispesion solution
 * @param instrument instrument (arm) setting
 */
/*--------------------------------------------------------------------------*/
cpl_frame* 
xsh_dispersol_list_save( xsh_dispersol_list *list, const char* tag)
{
  cpl_frame *result = NULL;
  cpl_table *table = NULL;
  char coefname[20];
  int i,j,k;
  int nbcoefs =0;
  cpl_size pows[2];
  char filename[256];

  /* Check input parameters */
  XSH_ASSURE_NOT_NULL( list);

  nbcoefs = (list->degx+1)*(list->degy+1);
  /* Create a table */
  check( table = cpl_table_new( XSH_DISPERSOL_TABLE_NBCOL+nbcoefs));
  check(cpl_table_set_size(table, list->size*2));

  check(
    cpl_table_new_column( table, XSH_DISPERSOL_TABLE_COLNAME_AXIS,
      CPL_TYPE_STRING));
  check(
    cpl_table_new_column( table, XSH_DISPERSOL_TABLE_COLNAME_ORDER,
      CPL_TYPE_INT));
  check(
    cpl_table_new_column( table, XSH_DISPERSOL_TABLE_COLNAME_DEGX,
      CPL_TYPE_INT));
  check(
    cpl_table_new_column( table, XSH_DISPERSOL_TABLE_COLNAME_DEGY,
      CPL_TYPE_INT));

  for(i=0; i<= list->degx; i++){
    for(j = 0;  j<= list->degy; j++){
      sprintf(coefname,"C%d%d",i,j);
      check( cpl_table_new_column( table, coefname, CPL_TYPE_DOUBLE));
    }
  }

  for(i=0; i< list->size; i++){
    int i2 = i*2;
    check(cpl_table_set_string(table,XSH_DISPERSOL_TABLE_COLNAME_AXIS,
      i2, XSH_DISPERSOL_AXIS_LAMBDA));
    check(cpl_table_set_string(table,XSH_DISPERSOL_TABLE_COLNAME_AXIS,
      i2+1, XSH_DISPERSOL_AXIS_SLIT));
    check(cpl_table_set_int(table,XSH_DISPERSOL_TABLE_COLNAME_ORDER,
      i2, list->list[i].absorder));
    check(cpl_table_set_int(table,XSH_DISPERSOL_TABLE_COLNAME_ORDER,
      i2+1, list->list[i].absorder));
    check(cpl_table_set_int(table,XSH_DISPERSOL_TABLE_COLNAME_DEGX,
      i2, list->degx));
    check(cpl_table_set_int(table,XSH_DISPERSOL_TABLE_COLNAME_DEGX,
      i2+1, list->degx));
    check(cpl_table_set_int(table,XSH_DISPERSOL_TABLE_COLNAME_DEGY,
      i2, list->degy));
    check(cpl_table_set_int(table,XSH_DISPERSOL_TABLE_COLNAME_DEGY,
      i2+1, list->degy));
    for( j=0; j<= list->degx; j++){
      for( k=0;  k<= list->degy; k++){
        double coef_lambda = 0.0, coef_slit = 0.0;
        sprintf(coefname,"C%d%d",j,k);
        pows[0] = j;
        pows[1] = k;
        check(coef_lambda = 
          cpl_polynomial_get_coeff( list->list[i].lambda_poly, pows));
        check(cpl_table_set_double( table, coefname, i2, coef_lambda));
        check(coef_slit = 
          cpl_polynomial_get_coeff( list->list[i].slit_poly, pows));
        check(cpl_table_set_double( table, coefname, i2+1, coef_slit));
      }
    }
  }

  sprintf(filename, "%s.fits",tag);
  check( xsh_pfits_set_pcatg(list->header,tag));
  check(cpl_table_save(table, list->header, NULL, 
    filename, CPL_IO_DEFAULT));
  /* 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( &table);
    if ( cpl_error_get_code() != CPL_ERROR_NONE) {
    }
    return result;
}
/*--------------------------------------------------------------------------*/

/**@}*/
