/*                                                                            *
 *   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:45 $
 * $Revision: 1.23 $
*/

/**
 * @defgroup xsh_extract  Extract objects (xsh_extract)
 * @ingroup drl_functions
 *
 * Function ...
 */
/*----------------------------------------------------------------------------*/
/**@{*/

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

#include <math.h>
#include <xsh_drl.h>

#include <xsh_utils_table.h>
#include <xsh_badpixelmap.h>
#include <xsh_data_pre.h>
#include <xsh_dfs.h>
#include <xsh_pfits.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_fit.h>
#include <xsh_data_instrument.h>
#include <xsh_data_localization.h>
#include <xsh_data_rec.h>
#include <xsh_ifu_defs.h>
#include <xsh_data_image_3d.h>
#include <xsh_data_pre_3d.h>

#include <cpl.h>

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

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

static void fill_img( cpl_image * img, int ilambda, cpl_image * down,
		cpl_image * cen, cpl_image * up, int up_shift, int cen_shift)
{
  int nx, ny, j ;
  

  check(nx = cpl_image_get_size_x( down ));
  check(ny = cpl_image_get_size_y( down));
  xsh_msg_dbg_high( "fill image at %d (%d x %d )", ilambda, nx, ny ) ;

  for( j = 1 ; j <= ny ; j++ ) {
    int rej ;
    double flux ;

    check(flux = cpl_image_get( down, ilambda+1, j, &rej )) ;
    check(cpl_image_set( img, 1, j+cen_shift, flux )) ;
  }

  check(nx = cpl_image_get_size_x( cen));
  check(ny = cpl_image_get_size_y( cen));

  for( j = 1 ; j <= ny ; j++ ) {
    int rej ;
    double flux ;

    check(flux = cpl_image_get( cen, ilambda+1, j, &rej )) ;
    check(cpl_image_set( img, 2, j+cen_shift, flux ) );
  }

  check(nx = cpl_image_get_size_x( up ));
  check(ny = cpl_image_get_size_y( up));
  for( j = 1 ; j <= ny ; j++ ) {
    int rej ;
    double flux ;

    check(flux = cpl_image_get( up, ilambda+1, j, &rej )) ;
    check(cpl_image_set( img, 3, j+up_shift, flux ) );
  }

 cleanup:
  return ;
}

static void make_data_cube( xsh_pre_3d * pre_3d, xsh_pre * pre_down,
			    xsh_pre * pre_cen, xsh_pre * pre_up )
{
  int nx, ny, nz, i ;
  cpl_image * cur_img = NULL ;
  int up_shift=0;
  int cen_shift =0;

  nx = xsh_pre_3d_get_nx( pre_3d ) ;
  ny = xsh_pre_3d_get_ny( pre_3d ) ;
  nz = xsh_pre_3d_get_nz( pre_3d ) ;

  xsh_msg( "Build data cube: %d x %d x %d", nx, ny, nz);
  
  for( i = 0 ; i<nz ; i++ ) {
    /* Data */
    /* fill pseudo image and insert into data cube */
    check( cur_img = cpl_image_new( nx, ny, CPL_TYPE_FLOAT ) ) ;
    check(fill_img( cur_img, i, pre_down->data, pre_cen->data, pre_up->data,
      up_shift,cen_shift)) ;
    check( xsh_image_3d_insert( pre_3d->data, cur_img, i ) ) ;
    xsh_free_image( &cur_img ) ;
    /* Errs */
    check( cur_img = cpl_image_new( nx, ny, CPL_TYPE_FLOAT ) ) ;
    check(fill_img( cur_img, i, pre_down->errs, pre_cen->errs, pre_up->errs,
      up_shift,cen_shift)) ;
    check( xsh_image_3d_insert( pre_3d->errs, cur_img, i ) ) ;
    xsh_free_image( &cur_img ) ;
    /* Qual */
    check( cur_img = cpl_image_new( nx, ny, CPL_TYPE_INT ) ) ;
    check(fill_img( cur_img, i, pre_down->qual, pre_cen->qual, pre_up->qual,
      up_shift,cen_shift)) ;
    check( xsh_image_3d_insert( pre_3d->qual, cur_img, i ) ) ;
    xsh_free_image( &cur_img ) ;
  }

 cleanup:
  xsh_free_image( &cur_img ) ;
  return ;
}


cpl_frame* xsh_format( cpl_frameset *spectrum_frame_set, 
		       const char *result_name, xsh_instrument * instrument,
		       const char* rec_prefix)
{
  cpl_frame * result = NULL ;
  //xsh_image_3d * flux_3d = NULL;
  //xsh_image_3d * errs_3d = NULL;
  //xsh_image_3d * qual_3d = NULL ;
  xsh_pre * pre_down = NULL, * pre_cen = NULL, * pre_up = NULL ;
  cpl_frame * frame_down = NULL, * frame_cen = NULL, * frame_up = NULL ;
  int nslitlet = 3, nslit, nlambda ;
  int nslit_up, nslit_lo, nslit_cen;
  char tag[256];
  char pcatg[256];
  xsh_pre_3d * pre_3d = NULL ;
  double lambda_min, lambda_max, lambda_step ;
  double crval2 = 0.0, cdelt2=0.0;


  XSH_ASSURE_NOT_NULL( spectrum_frame_set ) ;
  XSH_ASSURE_NOT_NULL( result_name ) ;
  XSH_ASSURE_NOT_NULL( instrument ) ;

  check( frame_down = cpl_frameset_get_frame( spectrum_frame_set, 0));
  check( pre_down = xsh_pre_load( frame_down, instrument));
  check( frame_cen = cpl_frameset_get_frame( spectrum_frame_set, 1));
  check( pre_cen = xsh_pre_load( frame_cen, instrument));
  check( frame_up = cpl_frameset_get_frame( spectrum_frame_set, 2));
  check( pre_up = xsh_pre_load( frame_up, instrument));

  /* Retrieve lambda_min, max and step for calculation of
     KeyWords CDELT3, CRVAL3, etc*/
  check(lambda_min=xsh_pfits_get_rectify_lambda_min( pre_down->data_header));
  check(lambda_max=xsh_pfits_get_rectify_lambda_max( pre_down->data_header));
  check(lambda_step=xsh_pfits_get_rectify_bin_lambda( pre_down->data_header));
  xsh_msg( "Rect Lambda min, max, bin: %lf, %lf, %lf", lambda_min, lambda_max,
    lambda_step) ;

  nslit_lo = xsh_pre_get_ny( pre_down);
  nslit_up = xsh_pre_get_ny( pre_up);
  nslit_cen = xsh_pre_get_ny( pre_cen);

  nslit = nslit_cen;
  check( crval2 = xsh_pfits_get_crval2(pre_cen->data_header));
  check( cdelt2 = xsh_pfits_get_cdelt2(pre_cen->data_header));

  if (nslit_up > nslit_cen){
    nslit += nslit_up-nslit_cen;
    crval2 -= (nslit_up-nslit_cen)*cdelt2;
  }
  if (nslit_lo > nslit_cen){
    nslit += nslit_lo-nslit_cen;
  }

  nlambda = xsh_pre_get_nx( pre_down);

  xsh_msg("nslit : lo %d cen %d up %d",nslit_lo,nslit_cen,nslit_up);

  check( pre_3d = xsh_pre_3d_new( nslitlet, nslit, nlambda));
  xsh_msg( "Pre_3d created: %dx%dx%d", nslitlet, nslit, nlambda);

  //check( flux_3d = xsh_pre_3d_get_data( pre_3d));
  //check( errs_3d = xsh_pre_3d_get_errs( pre_3d));
  //check( qual_3d = xsh_pre_3d_get_qual( pre_3d));

  /* Rotate up and down by 180 degress */
  check( xsh_pre_flip( pre_down, 0));
  check( xsh_pre_flip( pre_up, 0));

  make_data_cube( pre_3d, pre_down, pre_cen, pre_up);

  /* Get headers and set PRO.CATG */
  check( cpl_propertylist_append( pre_3d->data_header,
    pre_cen->data_header));
  check( cpl_propertylist_append( pre_3d->errs_header,
    pre_cen->errs_header));
  check( cpl_propertylist_append( pre_3d->qual_header,
    pre_cen->qual_header));

  sprintf(pcatg,"%s_%s",rec_prefix,
	  XSH_GET_TAG_FROM_ARM( XSH_MERGE3D_IFU, instrument));

  xsh_msg( "   Setting PRO.CATG '%s'", pcatg ) ;
  check( xsh_pfits_set_pcatg( pre_3d->data_header, pcatg));
  check( xsh_pfits_set_pcatg( pre_3d->errs_header, pcatg));
  check( xsh_pfits_set_pcatg( pre_3d->qual_header, pcatg));

  check(xsh_pfits_set_wcs1(pre_3d->data_header, 1.0, -0.6, 0.6));
  check(xsh_pfits_set_wcs1(pre_3d->errs_header, 1.0, -0.6, 0.6));
  check(xsh_pfits_set_wcs1(pre_3d->qual_header, 1.0, -0.6, 0.6));

  check( xsh_pfits_set_crval2( pre_3d->data_header, crval2));
  check( xsh_pfits_set_crval2( pre_3d->errs_header, crval2));
  check( xsh_pfits_set_crval2( pre_3d->qual_header, crval2));

  check(xsh_pfits_set_wcs3(pre_3d->data_header, 0.5, lambda_min, lambda_step));
  check(xsh_pfits_set_wcs3(pre_3d->errs_header, 0.5, lambda_min, lambda_step));
  check(xsh_pfits_set_wcs3(pre_3d->qual_header, 0.5, lambda_min, lambda_step));


/* the fdollowing should be done using more compact functions as: */
  check(xsh_set_cd_matrix3d(pre_3d->data_header));
  check(xsh_set_cd_matrix3d(pre_3d->errs_header));
  check(xsh_set_cd_matrix3d(pre_3d->qual_header));

  xsh_msg( "Saving %s", result_name) ;

  check( result = xsh_pre_3d_save( pre_3d, result_name, 0));

  sprintf(tag,"%s_%s",rec_prefix, 
	  XSH_GET_TAG_FROM_ARM( XSH_MERGE3D_IFU, instrument));

  xsh_msg( "   Setting TAG '%s'", tag);
  check( cpl_frame_set_tag( result, tag));
  check( cpl_frame_set_group( result, CPL_FRAME_GROUP_PRODUCT));
  check( cpl_frame_set_level( result, CPL_FRAME_LEVEL_FINAL));
  check(cpl_frame_set_type( result, CPL_FRAME_TYPE_IMAGE));

  cleanup:
    xsh_pre_free( &pre_down);
    xsh_pre_free( &pre_cen);
    xsh_pre_free( &pre_up);
    xsh_pre_3d_free( &pre_3d);
    return result;
}
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/**
  @brief Create a sky mask from a SKY LINE lIST where flag sky lines are 1.
  @param[in] sky_line_frame A Sky line list
  @param[in] size Size of mask
  @param[in] chunk_size Size of chunk along wavelength direction 
*/
/*---------------------------------------------------------------------------*/
static int* create_sky_mask( cpl_frame *sky_line_frame, int size, 
  double lambda_min, double lambda_step,
  xsh_instrument *instrument)
{
  int *sky_mask = NULL;
  const char* skymask_name = NULL;
  cpl_table *skymask_table = NULL;
  float *skymask_data = NULL;
  int irow, nrow;
  double fwhm =0.0, sky_min, sky_max;
  int isky_min, isky_max, imask;
  double resolution;
  
  XSH_CALLOC( sky_mask, int, size);
  
  xsh_msg("%s %s", xsh_instrument_arm_tostring( instrument), xsh_instrument_mode_tostring( instrument)); 
  if ( sky_line_frame != NULL){
    resolution = xsh_resolution_get( instrument, 0);
    check( skymask_name = cpl_frame_get_filename( sky_line_frame));
    XSH_TABLE_LOAD( skymask_table, skymask_name);
    /* sort wavelength */
    check( xsh_sort_table_1( skymask_table, "WAVELENGTH", CPL_FALSE));
    check( skymask_data = cpl_table_get_data_float( skymask_table,
      "WAVELENGTH"));
    check( nrow = cpl_table_get_nrow( skymask_table));

    xsh_msg_dbg_low("lambda min %f, step %f", lambda_min, lambda_step);

    for( irow=0; irow < nrow; irow++){
      fwhm = skymask_data[irow]/resolution;
      sky_min = skymask_data[irow]-fwhm;
      sky_max = skymask_data[irow]+fwhm;
      isky_min =  (int)xsh_round_double((sky_min-lambda_min)/lambda_step);
      isky_max = (int)xsh_round_double((sky_max-lambda_min)/lambda_step);
      for( imask=isky_min; imask <=isky_max; imask++){
        sky_mask[imask] = 1;
      }
    }
  }
  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      XSH_FREE( sky_mask);
    }
    XSH_TABLE_FREE( skymask_table);
    return sky_mask;
}
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/**
  @brief Shift a cube to center object at 0 arcsec
  @param[in,out] cube The Data cube
  @param[in] sky lines frame The sky lines table or NULL
  @param[in] chunk_size Size of chunk along wavelength direction 
*/
/*---------------------------------------------------------------------------*/
void xsh_center_cube( cpl_frame *cube_frame, cpl_frame *sky_line_frame,
  int chunk_size, xsh_instrument *instrument)
{
  xsh_pre_3d *cube = NULL;
  xsh_image_3d *cube_data_img = NULL;
  int cube_x, cube_y, cube_z;
  float *cube_data = NULL; 
  cpl_propertylist *header = NULL;
  //double crval1;
  double crval2, crval3;
  //double crpix1;
  //double crpix2;
  //double crpix3;
  //double cdelt1;
  double cdelt2, cdelt3;
  double *median = NULL;
  int y, z, zmed;
  cpl_vector *coadd_vect = NULL;
  cpl_vector *positions = NULL;
  double x0= 0.0,sigma =0.0, area=0.0, offset=0.0;
  int median_size, bad_fit, sky_line;
  cpl_vector *med_vect = NULL;
  double shift_z;
  int *sky_mask = NULL;
  
  XSH_ASSURE_NOT_NULL( cube_frame);
  XSH_ASSURE_NOT_NULL( instrument);
  
  check( cube = xsh_pre_3d_load( cube_frame));
  header = cube->data_header;

  check( cube_data_img = xsh_pre_3d_get_data( cube));
  check( cube_x = xsh_image_3d_get_size_x( cube_data_img));
  check( cube_y = xsh_image_3d_get_size_y( cube_data_img));
  check( cube_z = xsh_image_3d_get_size_z( cube_data_img));
  
  //check( crpix1 = xsh_pfits_get_crpix1( header));
  //check( crval1 = xsh_pfits_get_crval1( header));
  //check( cdelt1 = xsh_pfits_get_cdelt1( header));

  //check( crpix2 = xsh_pfits_get_crpix2( header));
  check( crval2 = xsh_pfits_get_crval2( header));
  check( cdelt2 = xsh_pfits_get_cdelt2( header));

  //check( crpix3 = xsh_pfits_get_crpix3( header));
  check( crval3 = xsh_pfits_get_crval3( header));
  check( cdelt3 = xsh_pfits_get_cdelt3( header));
  
  check( cube_data = (float*)xsh_image_3d_get_data( cube_data_img));
 
 
  /* create sky mask */
  check( sky_mask = create_sky_mask( sky_line_frame, cube_z, crval3, cdelt3,
    instrument));
  
  XSH_MALLOC( median, double, cube_z);
  median_size = 0;
  bad_fit = 0;
  sky_line = 0;
  check( coadd_vect = cpl_vector_new( cube_y));
  check( positions = cpl_vector_new( cube_y));
  
  for( y=0; y<cube_y; y++){
    cpl_vector_set( positions, y, y);
  }
  
  for(z=0; z< (cube_z-chunk_size); z+=chunk_size){
    for( y=0; y<cube_y; y++){
      cpl_vector_set( coadd_vect, y, 0);
    }
    for( zmed=z; zmed< (z+chunk_size); zmed++){
      if (sky_mask[zmed] == 0){
        for( y=0; y<cube_y; y++){
          double y_val;
	
	  y_val = cpl_vector_get( coadd_vect, y);
	  y_val += cube_data[cube_x*cube_y*zmed+cube_x*y+1];
          cpl_vector_set( coadd_vect, y, y_val);
        }
      }
      else{
        sky_line++;
      }
    }
    cpl_vector_fit_gaussian( positions, NULL, coadd_vect, NULL,CPL_FIT_ALL,
      &x0,&sigma,&area,&offset,NULL,NULL,NULL);
      
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_error_reset();
      bad_fit++;
    }
    else{
      double slit_cen_fit;
	
      slit_cen_fit = crval2+x0*cdelt2;
      median[median_size] = slit_cen_fit;
      median_size++; 
    }
  }
  xsh_msg(" Statistics of gaussian fit bad %d lines, good %d lines, sky lines %d",
    bad_fit, median_size, sky_line);
  check( med_vect = cpl_vector_wrap( median_size, median));
  shift_z = cpl_vector_get_median( med_vect);
  xsh_msg(" Measured object shift from gaussian fit: %f arcsec\n", shift_z);

  /* apply shift in wcs */
  crval2 += shift_z;
    
  cleanup:
    xsh_pre_3d_free( &cube);
    XSH_FREE( sky_mask);
    XSH_FREE( median);
    xsh_free_vector( &positions);
    xsh_free_vector( &coadd_vect);
    xsh_unwrap_vector( &med_vect);
    return;
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
/**
  @brief Create a cube
  @param[in] merge2d_frameset merge 2D IFU slitlets frameset
  @param[in] result_name Name of cube
  @param[in] instrument the instrument in use
  @param[in] rec_prefix prefix of recipe
*/
/*---------------------------------------------------------------------------*/
cpl_frame* xsh_cube( cpl_frameset *merge2d_frameset,
  xsh_instrument * instrument,
  const char* rec_prefix)
{

  cpl_frame *down_frame = NULL, *cen_frame = NULL, *up_frame = NULL;
  cpl_frame *result = NULL;
  xsh_pre *pre_down = NULL, *pre_cen = NULL, *pre_up = NULL;
  int nslitlet = 3, nslit, nlambda;
  xsh_pre_3d * pre_3d = NULL;
  double crpix2=0.0, crval2=0.0, cdelt2=0.0;
  double crpix3=0.0, crval3=0.0, cdelt3=0.0;
  char pcatg[256];
  char result_name[256];
  double waveref, sref_down, sref_up, sref_cen;

  XSH_ASSURE_NOT_NULL( merge2d_frameset);
  XSH_ASSURE_NOT_NULL( instrument);
  XSH_ASSURE_NOT_NULL( rec_prefix);

  check( down_frame = cpl_frameset_get_frame( merge2d_frameset, 0));
  check( pre_down = xsh_pre_load( down_frame, instrument));
  check( cen_frame = cpl_frameset_get_frame( merge2d_frameset, 1));
  check( pre_cen = xsh_pre_load( cen_frame, instrument));
  check( up_frame = cpl_frameset_get_frame( merge2d_frameset, 2));
  check( pre_up = xsh_pre_load( up_frame, instrument));

  /* flip UP and DOWN images around the horizontal */
  check( xsh_pre_flip( pre_down, 0));
  check( xsh_pre_flip( pre_up, 0));

  /* Retrieve lambda_min, max and step for calculation of
     KeyWords CDELT3, CRVAL3, etc*/
  check( crpix3 = xsh_pfits_get_crpix1( pre_cen->data_header));
  check( crval3 = xsh_pfits_get_crval1( pre_cen->data_header));
  check( cdelt3 = xsh_pfits_get_cdelt1( pre_cen->data_header));

  check( nlambda = xsh_pre_get_nx( pre_down));
  check( nslit = xsh_pre_get_ny( pre_down));

  check( pre_3d = xsh_pre_3d_new( nslitlet, nslit, nlambda));
  make_data_cube( pre_3d, pre_down, pre_cen, pre_up);

 /* Get headers and set PRO.CATG */
  check( cpl_propertylist_append( pre_3d->data_header,
    pre_cen->data_header));
  check( cpl_propertylist_append( pre_3d->errs_header,
    pre_cen->errs_header));
  check( cpl_propertylist_append( pre_3d->qual_header,
    pre_cen->qual_header));

  sprintf(pcatg, "%s_%s",rec_prefix,
    XSH_GET_TAG_FROM_ARM( XSH_MERGE3D_IFU, instrument));

  check( xsh_pfits_set_pcatg( pre_3d->data_header, pcatg));
  check( xsh_pfits_set_pcatg( pre_3d->errs_header, pcatg));
  check( xsh_pfits_set_pcatg( pre_3d->qual_header, pcatg));

  /* slitlet direction */
  check(xsh_pfits_set_wcs1(pre_3d->data_header, 1.0, -0.6, 0.6));
  check(xsh_pfits_set_wcs1(pre_3d->errs_header, 1.0, -0.6, 0.6));
  check(xsh_pfits_set_wcs1(pre_3d->qual_header, 1.0, -0.6, 0.6));

  check( xsh_pfits_set_cunit1( pre_3d->data_header, "arcsec"));
  check( xsh_pfits_set_cunit1( pre_3d->errs_header, "arcsec"));
  check( xsh_pfits_set_cunit1( pre_3d->qual_header, "arcsec"));

  /* slit direction */
  check( crpix2 = xsh_pfits_get_crpix2(pre_cen->data_header));
  check( crval2 = xsh_pfits_get_crval2(pre_cen->data_header));
  check( cdelt2 = xsh_pfits_get_cdelt2(pre_cen->data_header));
  
  check(xsh_pfits_set_wcs2(pre_3d->data_header, crpix2, crval2, cdelt2));
  check(xsh_pfits_set_wcs2(pre_3d->errs_header, crpix2, crval2, cdelt2));
  check(xsh_pfits_set_wcs2(pre_3d->qual_header, crpix2, crval2, cdelt2));

  check( xsh_pfits_set_cunit2( pre_3d->data_header, "arcsec"));
  check( xsh_pfits_set_cunit2( pre_3d->errs_header, "arcsec"));
  check( xsh_pfits_set_cunit2( pre_3d->qual_header, "arcsec"));

  /* wavelength direction */
  check( crpix3 = xsh_pfits_get_crpix1( pre_cen->data_header));  

  check(xsh_pfits_set_wcs3(pre_3d->data_header, crpix3, crval3, cdelt3));
  check(xsh_pfits_set_wcs3(pre_3d->errs_header, crpix3, crval3, cdelt3));
  check(xsh_pfits_set_wcs3(pre_3d->qual_header, crpix3, crval3, cdelt3));

  check( xsh_pfits_set_cunit3( pre_3d->data_header, "nm"));
  check( xsh_pfits_set_cunit3( pre_3d->errs_header, "nm"));
  check( xsh_pfits_set_cunit3( pre_3d->qual_header, "nm"));

  /* the fdollowing should be done using more compact functions as: */
  check( xsh_set_cd_matrix3d( pre_3d->data_header));
  check( xsh_set_cd_matrix3d( pre_3d->errs_header));
  check( xsh_set_cd_matrix3d( pre_3d->qual_header));


  /* add LAMBDA_REF / SLIT_REF if they are relevant */
  waveref = xsh_pfits_get_shiftifu_lambdaref( pre_down->data_header);
  sref_down = xsh_pfits_get_shiftifu_slitref( pre_down->data_header);
  sref_cen = xsh_pfits_get_shiftifu_slitref( pre_cen->data_header);
  sref_up = xsh_pfits_get_shiftifu_slitref( pre_up->data_header);

  if ( cpl_error_get_code() == CPL_ERROR_NONE){
    check( xsh_pfits_set_shiftifu_lambdaref( pre_3d->data_header, waveref));
    check( xsh_pfits_set_shiftifu_slitdownref( pre_3d->data_header, sref_down));
    check( xsh_pfits_set_shiftifu_slitcenref( pre_3d->data_header, sref_cen));
    check( xsh_pfits_set_shiftifu_slitupref( pre_3d->data_header, sref_up));  
  }
  xsh_error_reset(); 

  sprintf( result_name, "%s.fits", pcatg);

  check( result = xsh_pre_3d_save( pre_3d, result_name, 1));

  check( cpl_frame_set_tag( result, pcatg));
  check( cpl_frame_set_group( result, CPL_FRAME_GROUP_PRODUCT));
  check( cpl_frame_set_level( result, CPL_FRAME_LEVEL_FINAL));
  check(cpl_frame_set_type( result, CPL_FRAME_TYPE_IMAGE));

  cleanup:
    if ( cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_frame( &result);
    }
    xsh_pre_free( &pre_down);
    xsh_pre_free( &pre_cen);
    xsh_pre_free( &pre_up);
    xsh_pre_3d_free( &pre_3d);
    return result;
}
/*---------------------------------------------------------------------------*/

/**@}*/

