/*                                                                           *
 *   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:09:41 $
 * $Revision: 1.29 $
 * $Name: not supported by cvs2svn $
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup xsh_data_spectrum Spectrum 1D and 2D
 * @ingroup data_handling
 */
/*---------------------------------------------------------------------------*/

/**@{*/


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

#include <math.h>
#include <xsh_data_spectrum.h>
#include <xsh_utils.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_pfits.h>
#include <xsh_dfs.h>
#include <cpl.h>

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

/*---------------------------------------------------------------------------*/
/** 
 * @brief Create a 1D spectrum  structure
 * 
 * @param[in] lambda_min minimum wavelength of spectrum
 * @param[in] lambda_max maximum wavelength of spectrum
 * @param[in] lambda_step lambda binning 
 *
 * @return the spectrum structure
 */
/*---------------------------------------------------------------------------*/
xsh_spectrum* xsh_spectrum_1D_create( double lambda_min, double lambda_max,    
  double lambda_step)
{
  xsh_spectrum* result = NULL;


  /* check input parameters */
  XSH_ASSURE_NOT_ILLEGAL( lambda_min >= 0.0 && lambda_min <= lambda_max);
  XSH_ASSURE_NOT_ILLEGAL( lambda_step >=0);

  XSH_CALLOC(result, xsh_spectrum,1);
   
  result->lambda_min = lambda_min;
  result->lambda_max = lambda_max;
  result->lambda_step = lambda_step;

  XSH_NEW_PROPERTYLIST( result->flux_header);
  check(xsh_pfits_set_wcs1(result->flux_header, 1.0, lambda_min, lambda_step));

  XSH_NEW_PROPERTYLIST( result->errs_header);
  check( xsh_pfits_set_extname ( result->errs_header, "ERRS"));
  check(xsh_pfits_set_wcs1(result->errs_header, 1.0, lambda_min, lambda_step));

  XSH_NEW_PROPERTYLIST( result->qual_header);
  check( xsh_pfits_set_extname ( result->qual_header, "QUAL"));
  

  result->size_lambda = (int)((lambda_max-lambda_min)/lambda_step+0.5)+1;
  result->size_slit = 1;
  result->slit_min = 0;
  result->slit_max = 0;
  result->size = result->size_lambda;
  
  check( result->flux = cpl_image_new( result->size_lambda, 1, 
    CPL_TYPE_DOUBLE));
  check( result->errs = cpl_image_new( result->size_lambda, 1, 
    CPL_TYPE_DOUBLE));
  check( result->qual = cpl_image_new( result->size_lambda, 1, 
    CPL_TYPE_INT));

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

/*---------------------------------------------------------------------------*/
/** 
 * @brief Create a 2D spectrum structure
 * 
 * @param[in] lambda_min minimum wavelength of spectrum
 * @param[in] lambda_max maximum wavelength of spectrum
 * @param[in] lambda_step lambda binning 
 * @param[in] slit_min minimum slit value
 * @param[in] slit_max maximum slit value
 * @param[in] slit_step slit step
 *
 * @return the spectrum structure
 */
/*---------------------------------------------------------------------------*/
xsh_spectrum* xsh_spectrum_2D_create( double lambda_min, double lambda_max,    
  double lambda_step, double slit_min, double slit_max, double slit_step)
{
  xsh_spectrum* result = NULL;

  /* check input parameters */
  XSH_ASSURE_NOT_ILLEGAL( lambda_min >= 0.0 && lambda_min <= lambda_max);
  XSH_ASSURE_NOT_ILLEGAL( lambda_step >=0);
  XSH_ASSURE_NOT_ILLEGAL( slit_min <= slit_max);
  XSH_ASSURE_NOT_ILLEGAL( slit_step >=0);


  XSH_CALLOC(result, xsh_spectrum,1);
   
  result->lambda_min = lambda_min;
  result->lambda_max = lambda_max;
  result->lambda_step = lambda_step;
  result->slit_min = slit_min;
  result->slit_max =  slit_max;
  result->slit_step = slit_step; 

  XSH_NEW_PROPERTYLIST( result->flux_header);
  check(xsh_pfits_set_wcs1(result->flux_header, 1.0, lambda_min, lambda_step));
  check(xsh_pfits_set_wcs2(result->flux_header, 1.0, slit_min, slit_step));

  check(xsh_set_cd_matrix2d(result->flux_header));

  XSH_NEW_PROPERTYLIST( result->errs_header);
  check( xsh_pfits_set_extname ( result->errs_header, "ERRS"));
  XSH_NEW_PROPERTYLIST( result->qual_header);
  check( xsh_pfits_set_extname ( result->qual_header, "QUAL"));
  

  result->size_lambda = (int)((lambda_max-lambda_min)/lambda_step+0.5)+1;
  result->size_slit = (int)((slit_max-slit_min)/slit_step+0.5)+1;
  result->size = result->size_lambda * result->size_slit;
  check( result->flux = cpl_image_new( result->size_lambda, result->size_slit, 
    CPL_TYPE_DOUBLE));
  check( result->errs = cpl_image_new( result->size_lambda, result->size_slit, 
    CPL_TYPE_DOUBLE));
  check( result->qual = cpl_image_new( result->size_lambda, result->size_slit, 
    CPL_TYPE_INT));

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


/*---------------------------------------------------------------------------*/
/** 
 * @brief Load a 1D spectrum  structure
 * 
 * @param[in] s1d_frame the 1D spectrum frame
 * @return the spectrum structure
 */
/*---------------------------------------------------------------------------*/
xsh_spectrum* xsh_spectrum_load( cpl_frame* s1d_frame)
{
  xsh_spectrum* result = NULL;
  const char *s1dname = NULL;
  int naxis;

  XSH_ASSURE_NOT_NULL( s1d_frame);

  XSH_ASSURE_NOT_ILLEGAL(cpl_frame_get_nextensions(s1d_frame) == 2);
  
  check( s1dname = cpl_frame_get_filename( s1d_frame));

  XSH_CALLOC(result, xsh_spectrum,1);

  check( result->flux_header = cpl_propertylist_load( s1dname,0));
  check( result->errs_header = cpl_propertylist_load( s1dname,1));
  check( result->qual_header = cpl_propertylist_load( s1dname,2));

  check( result->lambda_min = xsh_pfits_get_crval1( result->flux_header));
  check( result->lambda_step = xsh_pfits_get_cdelt1( result->flux_header));
  check( result->size =  xsh_pfits_get_naxis1( result->flux_header));
  check( result->size_lambda =  xsh_pfits_get_naxis1( result->flux_header));
  result->lambda_max = result->lambda_min+
    result->lambda_step*(result->size-1);

  check( naxis = xsh_pfits_get_naxis( result->flux_header));

  if (naxis > 1){
    check( result->slit_min = xsh_pfits_get_crval2( result->flux_header));
    check( result->slit_step = xsh_pfits_get_cdelt2( result->flux_header));
    check( result->size_slit = xsh_pfits_get_naxis2( result->flux_header));
    result->slit_max = result->slit_min+
      result->slit_step*(result->size_slit-1);

    check( result->flux = cpl_image_load( s1dname, CPL_TYPE_DOUBLE, 0, 0));
    check( result->errs = cpl_image_load( s1dname, CPL_TYPE_DOUBLE, 0, 1));
    check( result->qual = cpl_image_load( s1dname, CPL_TYPE_INT, 0, 2));
  }
  else{
    double *flux_data = NULL;
    double *errs_data = NULL;
    int *qual_data = NULL;
    int i;
    cpl_vector *flux = NULL;
    cpl_vector *errs = NULL;
    cpl_vector *qual = NULL;
    int size = 0;

    check( flux = cpl_vector_load( s1dname, 0));
    check( size = cpl_vector_get_size( flux));
    check( errs = cpl_vector_load( s1dname, 1));
    check( qual = cpl_vector_load( s1dname, 2));
    check( flux_data = cpl_vector_get_data( flux));
    check( result->flux = cpl_image_wrap_double( size, 1 , flux_data));
    xsh_unwrap_vector( &flux);

    check( errs_data = cpl_vector_get_data( errs));
    check( result->errs = cpl_image_wrap_double( size, 1, errs_data));

    check( result->qual = cpl_image_new ( size, 1, CPL_TYPE_INT));
    check( qual_data = cpl_image_get_data_int( result->qual));
    for(i=0; i< size; i++){
      check( qual_data[i] = (int)cpl_vector_get(qual,i));
    }
    xsh_unwrap_vector( &errs);
    xsh_free_vector( &qual);
  }
  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_spectrum_free(&result);
    } 
    return result;
}


/*---------------------------------------------------------------------------*/
/** 
 * @brief Load a 1D spectrum  structure
 * 
 * @param[in] s1d_frame the 1D spectrum frame
 * @param[in] instr instrument structure
 * @param[in] order order to be loaded
 * @return the spectrum structure
 */
/*---------------------------------------------------------------------------*/
xsh_spectrum* xsh_spectrum_load_order( cpl_frame* s1d_frame, 
                                       xsh_instrument* instr,
                                       const int order)
{
  xsh_spectrum* result = NULL;
  const char *s1dname = NULL;
  int naxis;

  XSH_ASSURE_NOT_NULL( s1d_frame);
  XSH_ASSURE_NOT_NULL( instr);

  //XSH_ASSURE_NOT_ILLEGAL(cpl_frame_get_nextensions(s1d_frame) == 2);
  
  check( s1dname = cpl_frame_get_filename( s1d_frame));

  XSH_CALLOC(result, xsh_spectrum,1);

  check( result->flux_header = cpl_propertylist_load( s1dname,order+0));
  check( result->errs_header = cpl_propertylist_load( s1dname,order+1));
  check( result->qual_header = cpl_propertylist_load( s1dname,order+2));

  check( result->lambda_min = xsh_pfits_get_crval1( result->flux_header));
  check( result->lambda_step = xsh_pfits_get_cdelt1( result->flux_header));
  check( result->size =  xsh_pfits_get_naxis1( result->flux_header));
  check( result->size_lambda =  xsh_pfits_get_naxis1( result->flux_header));
  result->lambda_max = result->lambda_min+
    (result->lambda_step*result->size-1);

  check( naxis = xsh_pfits_get_naxis( result->flux_header));

  if (naxis > 1){
    check( result->slit_min = xsh_pfits_get_crval2( result->flux_header));
    check( result->slit_step = xsh_pfits_get_cdelt2( result->flux_header));
    check( result->size_slit = xsh_pfits_get_naxis2( result->flux_header));
    result->slit_max = result->slit_min+
      result->slit_step*(result->size_slit-1);

    check( result->flux = cpl_image_load( s1dname, CPL_TYPE_DOUBLE, 0,order+0));
    check( result->errs = cpl_image_load( s1dname, CPL_TYPE_DOUBLE, 0,order+1));
    check( result->qual = cpl_image_load( s1dname, CPL_TYPE_INT, 0, order+2));
  }
  else{
    double *flux_data = NULL;
    double *errs_data = NULL;
    int *qual_data = NULL;
    int i;
    cpl_vector *flux = NULL;
    cpl_vector *errs = NULL;
    cpl_vector *qual = NULL;
    int size = 0;

    check( flux = cpl_vector_load( s1dname, order+0));
    check( size = cpl_vector_get_size( flux));
    check( errs = cpl_vector_load( s1dname, order+1));
    check( qual = cpl_vector_load( s1dname, order+2));
    check( flux_data = cpl_vector_get_data( flux));
    check( result->flux = cpl_image_wrap_double( size, 1 , flux_data));
    xsh_unwrap_vector( &flux);

    check( errs_data = cpl_vector_get_data( errs));
    check( result->errs = cpl_image_wrap_double( size, 1, errs_data));

    check( result->qual = cpl_image_new ( size, 1, CPL_TYPE_INT));
    check( qual_data = cpl_image_get_data_int( result->qual));
    for(i=0; i< size; i++){
      check( qual_data[i] = (int)cpl_vector_get(qual,i));
    }
    xsh_unwrap_vector( &errs);
    xsh_free_vector( &qual);
  }
  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE) {
      xsh_spectrum_free(&result);
    } 
    return result;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief Get size of spectrum
 * 
 * @param[in] s spectrum structure
 *
 * @return the size of flux data in spectrum
 */
/*---------------------------------------------------------------------------*/
int xsh_spectrum_get_size( xsh_spectrum* s)
{
  int res=0;

  XSH_ASSURE_NOT_NULL( s);

  res = s->size;

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief Get lambda axis size of spectrum
 * 
 * @param[in] s spectrum structure
 *
 * @return the lambda axis size of flux data in spectrum
 */
/*---------------------------------------------------------------------------*/
int xsh_spectrum_get_size_lambda( xsh_spectrum* s)
{
  int res=0;

  XSH_ASSURE_NOT_NULL( s);

  res = s->size_lambda;

  cleanup:
    return res;
}


/*---------------------------------------------------------------------------*/
/** 
 * @brief Get slit axis ize of spectrum
 * 
 * @param[in] s spectrum structure
 *
 * @return the slit axis size of flux data in spectrum
 */
/*---------------------------------------------------------------------------*/
int xsh_spectrum_get_size_slit( xsh_spectrum* s)
{
  int res=0;

  XSH_ASSURE_NOT_NULL( s);

  res = s->size_slit;

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief Get minimum lambda of spectrum
 * 
 * @param[in] s spectrum structure
 *
 * @return minimum lambda spectrum
 */
/*---------------------------------------------------------------------------*/
double xsh_spectrum_get_lambda_min( xsh_spectrum* s)
{
  double res=0.0;

  XSH_ASSURE_NOT_NULL( s);

  res = s->lambda_min;

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief Get maximum lambda of spectrum
 * 
 * @param[in] s spectrum structure
 *
 * @return maximum lambda of spectrum
 */
/*---------------------------------------------------------------------------*/
double xsh_spectrum_get_lambda_max( xsh_spectrum* s)
{
  double res=0.0;

  XSH_ASSURE_NOT_NULL( s);

  res = s->lambda_max;

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief Get bin in lambda of spectrum
 * 
 * @param[in] s spectrum structure
 *
 * @return bin in lambda of spectrum
 */
/*---------------------------------------------------------------------------*/
double xsh_spectrum_get_lambda_step( xsh_spectrum* s)
{
  double res=0.0;

  XSH_ASSURE_NOT_NULL( s);

  res = s->lambda_step;

  cleanup:
    return res;
}


/*---------------------------------------------------------------------------*/
/** 
 * @brief Get flux of spectrum
 * 
 * @param[in] s spectrum structure
 *
 * @return the flux data of spectrum
 */
/*---------------------------------------------------------------------------*/
double* xsh_spectrum_get_flux( xsh_spectrum* s)
{
  double *res=NULL;

  XSH_ASSURE_NOT_NULL( s);

  check( res = cpl_image_get_data_double( s->flux));

  cleanup:
    return res;
}


/*---------------------------------------------------------------------------*/
/** 
 * @brief Get errs of spectrum
 * 
 * @param[in] s spectrum structure
 *
 * @return the errs data of spectrum
 */
/*---------------------------------------------------------------------------*/
double* xsh_spectrum_get_errs( xsh_spectrum* s)
{
  double *res=NULL;

  XSH_ASSURE_NOT_NULL( s);

  check( res = cpl_image_get_data_double( s->errs));

  cleanup:
    return res;
}


/*---------------------------------------------------------------------------*/
/** 
 * @brief Get qual of spectrum
 * 
 * @param[in] s spectrum structure
 *
 * @return the qual data of spectrum
 */
/*---------------------------------------------------------------------------*/
int* xsh_spectrum_get_qual( xsh_spectrum* s)
{
  int* res = NULL;

  XSH_ASSURE_NOT_NULL( s);

  check( res = cpl_image_get_data_int( s->qual));

  cleanup:
    return res;
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief Get flux of spectrum as image
 *
 * @param[in] s spectrum structure
 *
 * @return the flux data of spectrum
 */
/*---------------------------------------------------------------------------*/
cpl_image* xsh_spectrum_get_flux_ima( xsh_spectrum* s)
{

  XSH_ASSURE_NOT_NULL( s);

  cleanup:
    return s->flux;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Get flux of spectrum as image
 *
 * @param[in] s spectrum structure
 *
 * @return the flux data of spectrum
 */
/*---------------------------------------------------------------------------*/
cpl_image* xsh_spectrum_get_errs_ima( xsh_spectrum* s)
{

  XSH_ASSURE_NOT_NULL( s);

  cleanup:
    return s->errs;
}


/*---------------------------------------------------------------------------*/
/**
 * @brief Get flux of spectrum as image
 *
 * @param[in] s spectrum structure
 *
 * @return the flux data of spectrum
 */
/*---------------------------------------------------------------------------*/
cpl_image* xsh_spectrum_get_qual_ima( xsh_spectrum* s)
{

  XSH_ASSURE_NOT_NULL( s);

  cleanup:
    return s->qual;
}


/*---------------------------------------------------------------------------*/
/**
 * @brief free memory associated to an 1D spectrum
 * 
 * @param[in] s spectrum structure to free
*/
/*---------------------------------------------------------------------------*/
void xsh_spectrum_free( xsh_spectrum** s)
{
  if (s && *s){
    xsh_free_propertylist( &((*s)->flux_header));
    xsh_free_propertylist( &((*s)->errs_header));
    xsh_free_propertylist( &((*s)->qual_header));
    xsh_free_image( &((*s)->flux));
    xsh_free_image( &((*s)->errs));
    xsh_free_image( &((*s)->qual));
    XSH_FREE( (*s));
  }
}

/*---------------------------------------------------------------------------*/
/** 
 * @brief save a spectrum
 * 
 * @param[in] s spectrum structure to save
 * @param[in] filename name of the save file
 * @param[in] tag spectrum pro catg
 * 
 * @return 1D spectrum frame
 */
/*---------------------------------------------------------------------------*/
cpl_frame* xsh_spectrum_save( xsh_spectrum* s, const char* filename, 
			      const char* tag)
{
  cpl_frame *product_frame = NULL;

  XSH_ASSURE_NOT_NULL(s);
  XSH_ASSURE_NOT_NULL(filename);

  check( xsh_pfits_set_extname(s->flux_header , "FLUX"));
  check(xsh_plist_set_extra_keys(s->flux_header,"IMAGE","DATA","RMSE",
                                 "FLUX","ERRS","QUAL",0));

  check( xsh_pfits_set_extname(s->errs_header , "ERRS"));
  check(xsh_plist_set_extra_keys(s->errs_header,"IMAGE","DATA","RMSE",
                                 "FLUX","ERRS","QUAL",1));

  check( xsh_pfits_set_extname(s->qual_header , "QUAL"));
  check(xsh_plist_set_extra_keys(s->qual_header,"IMAGE","DATA","RMSE",
                                 "FLUX","ERRS","QUAL",2));
 
  /* Save the file */
   if ( s->size_slit > 1){
  
    double crval1=0;
    double crpix1=0;
    double cdelt1=0;

    double crval2=0;
    double crpix2=0;
    double cdelt2=0;

    crval1=xsh_pfits_get_crval1(s->flux_header);
    crpix1=xsh_pfits_get_crpix1(s->flux_header);
    cdelt1=xsh_pfits_get_cdelt1(s->flux_header);

    crval2=xsh_pfits_get_crval2(s->flux_header);
    crpix2=xsh_pfits_get_crpix2(s->flux_header);
    cdelt2=xsh_pfits_get_cdelt2(s->flux_header);

    xsh_pfits_set_wcs(s->errs_header,crpix1,crval1,cdelt1,crpix2,crval2,cdelt2);
    xsh_pfits_set_wcs(s->qual_header,crpix1,crval1,cdelt1,crpix2,crval2,cdelt2);

    check( xsh_pfits_set_pcatg( s->flux_header, tag));
    check_msg (cpl_image_save ( s->flux, filename, XSH_SPECTRUM_DATA_BPP,
      s->flux_header, CPL_IO_DEFAULT),
      "Could not save data to %s extension 0", filename);
    check_msg (cpl_image_save ( s->errs, filename, XSH_SPECTRUM_ERRS_BPP,
      s->errs_header, CPL_IO_EXTEND),
      "Could not save errs to %s extension 1", filename);
    check_msg (cpl_image_save ( s->qual, filename, XSH_SPECTRUM_QUAL_BPP,
      s->qual_header, CPL_IO_EXTEND),
      "Could not save qual to %s extension 2", filename);
  }
  else{
    cpl_vector *flux1D = NULL;
    cpl_vector *err1D = NULL;
    cpl_vector *qual1D = NULL;

    double crval1=0;
    double crpix1=0;
    double cdelt1=0;

    crval1=xsh_pfits_get_crval1(s->flux_header);
    crpix1=xsh_pfits_get_crpix1(s->flux_header);
    cdelt1=xsh_pfits_get_cdelt1(s->flux_header);



    xsh_pfits_set_ctype1(s->flux_header,"LINEAR");
    xsh_pfits_set_cunit1(s->flux_header,"nm");
    cpl_propertylist_erase_regexp(s->flux_header, "^CTYPE2", 0);



    check(xsh_pfits_set_wcs1(s->errs_header, crpix1, crval1, cdelt1));
    xsh_pfits_set_cunit1(s->errs_header,"nm");

    check(xsh_pfits_set_wcs1(s->qual_header, crpix1, crval1, cdelt1));
    xsh_pfits_set_cunit1(s->qual_header,"nm");
    xsh_pfits_set_bunit(s->qual_header,XSH_BUNIT_NONE_C);

    check( flux1D = cpl_vector_new_from_image_row( s->flux, 1));
    check( err1D = cpl_vector_new_from_image_row( s->errs, 1));
    check( qual1D = cpl_vector_new_from_image_row( s->qual, 1));
    check( cpl_vector_save( flux1D, filename, XSH_SPECTRUM_DATA_BPP, 
      s->flux_header, CPL_IO_DEFAULT));
    check( cpl_vector_save( err1D, filename, XSH_SPECTRUM_ERRS_BPP,
      s->errs_header, CPL_IO_EXTEND));
    check( cpl_vector_save( qual1D, filename, XSH_SPECTRUM_QUAL_BPP,
      s->qual_header, CPL_IO_EXTEND));
    xsh_free_vector( &flux1D);
    xsh_free_vector( &err1D);
    xsh_free_vector( &qual1D);
  }

  
  check( product_frame = cpl_frame_new() ) ;
  check( cpl_frame_set_filename( product_frame,filename ));
  check( cpl_frame_set_type( product_frame, CPL_FRAME_TYPE_IMAGE )) ;
  check( cpl_frame_set_level( product_frame, CPL_FRAME_LEVEL_FINAL )) ;
  check( cpl_frame_set_group( product_frame, CPL_FRAME_GROUP_PRODUCT ));

  cleanup:
    if (cpl_error_get_code () != CPL_ERROR_NONE) {
      xsh_free_frame(&product_frame);
      product_frame = NULL;
    }
    return product_frame;
}




/*---------------------------------------------------------------------------*/
/** 
 * @brief save a spectrum
 * 
 * @param[in] s spectrum structure to save
 * @param[in] filename name of the save file
 * @param[in] tag spectrum pro catg
 * @param[in] order spectrum relative order (if >0 spectrum is saved as extend) 
 * 
 * @return 1D spectrum frame
 */
/*---------------------------------------------------------------------------*/
cpl_frame* xsh_spectrum_save_order( xsh_spectrum* s, const char* filename, 
                                    const char* tag,const int order)
{
  cpl_frame *product_frame = NULL;

  XSH_ASSURE_NOT_NULL(s);
  XSH_ASSURE_NOT_NULL(filename);

 
  /* Save the file */
  if ( s->size_slit > 1){
  
     check( xsh_pfits_set_pcatg( s->flux_header, tag));
     if(order==0) {
        check_msg (cpl_image_save ( s->flux, filename, XSH_SPECTRUM_DATA_BPP,
                                    s->flux_header, CPL_IO_DEFAULT),
                   "Could not save data to %s extension 0", filename);
     } else {
        check_msg (cpl_image_save ( s->errs, filename, XSH_SPECTRUM_ERRS_BPP,
                                    s->errs_header, CPL_IO_EXTEND),
                   "Could not save errs to %s extension 1", filename);
        check_msg (cpl_image_save ( s->qual, filename, XSH_SPECTRUM_QUAL_BPP,
                                    s->qual_header, CPL_IO_EXTEND),
                   "Could not save qual to %s extension 2", filename);
     }
  }
  else{
     cpl_vector *flux1D = NULL;
     cpl_vector *err1D = NULL;
     cpl_vector *qual1D = NULL;

     check( flux1D = cpl_vector_new_from_image_row( s->flux, 1));
     check( err1D = cpl_vector_new_from_image_row( s->errs, 1));
     check( qual1D = cpl_vector_new_from_image_row( s->qual, 1));
     if(order==0) {
        check( cpl_vector_save( flux1D, filename, XSH_SPECTRUM_DATA_BPP,
                                s->flux_header, CPL_IO_DEFAULT));
     } else {
        check( cpl_vector_save( flux1D, filename, XSH_SPECTRUM_DATA_BPP, 
                                s->flux_header, CPL_IO_EXTEND));
     }
     check( cpl_vector_save( err1D, filename, XSH_SPECTRUM_ERRS_BPP,
                             s->errs_header, CPL_IO_EXTEND));
     check( cpl_vector_save( qual1D, filename, XSH_SPECTRUM_QUAL_BPP,
                             s->qual_header, CPL_IO_EXTEND));
     xsh_free_vector( &flux1D);
     xsh_free_vector( &err1D);
     xsh_free_vector( &qual1D);
  }

  
  check( product_frame = cpl_frame_new() ) ;
  check( cpl_frame_set_filename( product_frame,filename ));
  check( cpl_frame_set_type( product_frame, CPL_FRAME_TYPE_IMAGE )) ;
  check( cpl_frame_set_level( product_frame, CPL_FRAME_LEVEL_FINAL )) ;
  check( cpl_frame_set_group( product_frame, CPL_FRAME_GROUP_PRODUCT ));

  cleanup:
    if (cpl_error_get_code () != CPL_ERROR_NONE) {
      xsh_free_frame(&product_frame);
      product_frame = NULL;
    }
    return product_frame;
}


xsh_spectrum * xsh_spectrum_duplicate( xsh_spectrum * org )
{
  xsh_spectrum * result = NULL ;

  XSH_ASSURE_NOT_NULL( org ) ;

  /* Duplicate the spectrum */

  XSH_CALLOC( result, xsh_spectrum, 1 ) ;

  result->lambda_min = org->lambda_min;
  result->lambda_max = org->lambda_max;
  result->lambda_step = org->lambda_step;
  result->size_lambda = org->size_lambda ;
  result->slit_min = org->slit_min;
  result->slit_max = org->slit_max;
  result->size = org->size;
  result->size_slit = org->size_slit ;
  check( result->flux = cpl_image_duplicate( org->flux ) ) ;
  check( result->flux_header = cpl_propertylist_duplicate( org->flux_header ));
  check( result->errs = cpl_image_duplicate( org->errs ) ) ;
  check( result->errs_header = cpl_propertylist_duplicate( org->errs_header ));
  check( result->qual = cpl_image_duplicate( org->qual ) ) ;
  check( result->qual_header = cpl_propertylist_duplicate( org->qual_header ));

 cleanup:
  return result ;
}


xsh_spectrum *
xsh_spectrum_extract_range( xsh_spectrum * org, const double wmin,const double wmax )
{
  xsh_spectrum * result = NULL ;

  XSH_ASSURE_NOT_NULL( org ) ;
  int size_x=0;
  int size_y=0;
  /* Duplicate the spectrum */

  XSH_CALLOC( result, xsh_spectrum, 1 ) ;

  result->lambda_min = wmin;
  result->lambda_max = wmax;
  result->lambda_step = org->lambda_step;
  size_x=(int)((wmax-wmin)/org->lambda_step+0.5);
  result->size_lambda = size_x ;
  result->slit_min = org->slit_min;
  result->slit_max = org->slit_max;
  result->size_slit = org->size_slit ;
  check(result->size = size_x);
  /*
  xsh_msg("size_lambda=%d",org->size_lambda);
  xsh_msg("size_slit=%d",org->size_slit);
  xsh_msg("slit_min=%g",org->slit_min);
  xsh_msg("slit_max=%g",org->slit_max);
  xsh_msg("lambda_min=%g",org->lambda_min);
  xsh_msg("lambda_max=%g",org->lambda_max);
  xsh_msg("size=%d",org->size);
  xsh_msg("out spectrum  size=%d",size_x);

  xsh_msg("ima sx=%d",(int)cpl_image_get_size_x(org->flux));
  xsh_msg("ima sy=%d",(int)cpl_image_get_size_y(org->flux));
  */

  size_y=(result->size_slit>1) ? result->size_slit: 1;
  //xsh_msg("out spectrum X size=%d",size_x);
  //xsh_msg("out spectrum Y size=%d",size_y);
  check( result->flux = cpl_image_extract( org->flux,1,1,size_x,size_y));
  check( result->flux_header = cpl_propertylist_duplicate( org->flux_header ));

  check( result->errs = cpl_image_extract( org->errs,1,1,size_x,size_y));
  check( result->errs_header = cpl_propertylist_duplicate( org->errs_header ));

  check( result->qual = cpl_image_extract( org->qual,1,1,size_x,size_y));
  check( result->qual_header = cpl_propertylist_duplicate( org->qual_header ));

 cleanup:
  return result ;
}


cpl_error_code
xsh_spectrum_cut_dichroic_uvb(cpl_frame* frame1d)
{
    cpl_propertylist* phead=NULL;
    xsh_spectrum* spectrum_i=NULL;
    xsh_spectrum* spectrum_o=NULL;

    const char* pcatg=NULL;
    const char* fname=NULL;
    char oname[128];

    int naxis1=0;
    int xcut=0;

    double wave_cut=XSH_UVB_DICHROIC_WAVE_CUT; /* 556 nm dichroic cut */
    double wave_min=0;
    double wave_max=0;
    double wave_del=0;
    char cmd[256];
    fname=cpl_frame_get_filename(frame1d);

    phead = cpl_propertylist_load(fname, 0);
    pcatg=xsh_pfits_get_pcatg(phead) ;

    //xsh_msg("fname=%s",fname);
    spectrum_i=xsh_spectrum_load( frame1d);

    check(wave_min=spectrum_i->lambda_min);
    check(wave_max=spectrum_i->lambda_max);
    check(wave_del=spectrum_i->lambda_step);
    check(naxis1=spectrum_i->size);

    cpl_ensure_code(wave_max > wave_cut, CPL_ERROR_ILLEGAL_INPUT);
    xcut = (int) ( (wave_cut-wave_min) / wave_del + 0.5 );
    cpl_ensure_code(xcut <= naxis1, CPL_ERROR_ILLEGAL_INPUT);

    if (xcut == naxis1) {
        return CPL_ERROR_NONE;
    }
    sprintf(oname,"tmp_%s",fname);
    /*
    xsh_msg("wave_min=%g",wave_min);
    xsh_msg("wave_max=%g",wave_max);
    xsh_msg("wave_del=%g",wave_del);
    xsh_msg("wave_cut=%g",wave_cut);
    xsh_msg("naxis1=%d",naxis1);
    xsh_msg("xcut=%d",xcut);
    */
    check(spectrum_o=xsh_spectrum_extract_range(spectrum_i, wave_min, wave_cut));
    cpl_frame* frm=xsh_spectrum_save(spectrum_o, oname,pcatg);
    xsh_free_frame(&frm);
    sprintf(cmd,"mv  %s %s",oname,fname);
    assure(system(cmd)==0,CPL_ERROR_UNSPECIFIED,"unable to mv file");
    //cpl_frame_set_filename(frame1d,oname);

    cleanup:
    xsh_spectrum_free(&spectrum_i);
    xsh_spectrum_free(&spectrum_o);
    xsh_free_propertylist(&phead);

    return cpl_error_get_code();
}


cpl_error_code
xsh_spectrum_orders_cut_dichroic_uvb(cpl_frame* frame1d,xsh_instrument* instr)
{
    cpl_propertylist* phead=NULL;
    xsh_spectrum* spectrum_i=NULL;
    xsh_spectrum* spectrum_o=NULL;

    const char* pcatg=NULL;
    const char* fname=NULL;
    char oname[128];

    int naxis1=0;
    int xcut=0;
    int next=0;

    double wave_cut=XSH_UVB_DICHROIC_WAVE_CUT; /* 556 nm dichroic cut */
    double wave_min=0;
    double wave_max=0;
    double wave_del=0;
    char cmd[256];
    fname=cpl_frame_get_filename(frame1d);
    next=cpl_frame_get_nextensions(frame1d);

    phead = cpl_propertylist_load(fname, 0);
    pcatg=xsh_pfits_get_pcatg(phead) ;

    //xsh_msg("fname=%s",fname);

    spectrum_i=xsh_spectrum_load_order( frame1d, instr,0);

    check(wave_min=spectrum_i->lambda_min);
    check(wave_max=spectrum_i->lambda_max);
    check(wave_del=spectrum_i->lambda_step);
    check(naxis1=spectrum_i->size);

    cpl_ensure_code(wave_max > wave_cut, CPL_ERROR_ILLEGAL_INPUT);
    xcut = (int) ( (wave_cut-wave_min) / wave_del + 0.5 );
    cpl_ensure_code(xcut <= naxis1, CPL_ERROR_ILLEGAL_INPUT);


    if (xcut == naxis1) {
        return CPL_ERROR_NONE;
    }
    sprintf(oname,"tmp_%s",fname);

    check(spectrum_o=xsh_spectrum_extract_range(spectrum_i, wave_min, wave_cut));
    cpl_frame* frm=xsh_spectrum_save(spectrum_o, oname,pcatg);
    xsh_free_frame(&frm);
    /* loop over each frame extensions */
    cpl_frame* frame;
     for(int i=3;i<next;i+=3) {

         spectrum_i=xsh_spectrum_load_order( frame1d, instr,i);
         frame=xsh_spectrum_save_order(spectrum_i, oname, pcatg,i);
         xsh_spectrum_free(&spectrum_i);
         xsh_free_frame(&frame);

     }


     sprintf(cmd,"mv  %s %s",oname,fname);
     assure(system(cmd)==0,CPL_ERROR_UNSPECIFIED,"unable to mv file");

    cleanup:

    xsh_spectrum_free(&spectrum_i);
    xsh_spectrum_free(&spectrum_o);
    xsh_free_propertylist(&phead);

    return cpl_error_get_code();
}

/** 
 * @brief save a spectrum
 * 
 * @param[in] s spectrum structure to save
 * @param[in] filename name of the save file
 * @param[in] instr
 *   instrument
 * 
 * @return 1D spectrum frame
 */
/*---------------------------------------------------------------------------*/
cpl_frame* xsh_phys_spectrum_save( xsh_spectrum* s, const char* filename, 
				   xsh_instrument* instr)
{
  cpl_frame *product_frame = NULL;
  const char* tag = NULL;

  XSH_ASSURE_NOT_NULL(s);
  XSH_ASSURE_NOT_NULL(filename);

 
  /* Save the file */
   if ( s->size_slit > 1){
    tag =  XSH_GET_TAG_FROM_ARM( XSH_PHYS_MERGE2D, instr);
    check( xsh_pfits_set_pcatg( s->flux_header, tag));
    check_msg (cpl_image_save ( s->flux, filename, XSH_SPECTRUM_DATA_BPP,
      s->flux_header, CPL_IO_DEFAULT),
      "Could not save data to %s extension 0", filename);
    check_msg (cpl_image_save ( s->errs, filename, XSH_SPECTRUM_ERRS_BPP,
      s->errs_header, CPL_IO_EXTEND),
      "Could not save errs to %s extension 1", filename);
    check_msg (cpl_image_save ( s->qual, filename, XSH_SPECTRUM_QUAL_BPP,
      s->qual_header, CPL_IO_EXTEND),
      "Could not save qual to %s extension 2", filename);
  }
  else{
    cpl_vector *flux1D = NULL;
    cpl_vector *err1D = NULL;
    cpl_vector *qual1D = NULL;

    tag =  XSH_GET_TAG_FROM_ARM( XSH_PHYS_MERGE1D, instr);

    check( xsh_pfits_set_pcatg( s->flux_header, tag));
    check( flux1D = cpl_vector_new_from_image_row( s->flux, 1));
    check( err1D = cpl_vector_new_from_image_row( s->errs, 1));
    check( qual1D = cpl_vector_new_from_image_row( s->qual, 1));
    check( cpl_vector_save( flux1D, filename, XSH_SPECTRUM_DATA_BPP, 
      s->flux_header, CPL_IO_DEFAULT));
    check( cpl_vector_save( err1D, filename, XSH_SPECTRUM_ERRS_BPP,
      s->errs_header, CPL_IO_EXTEND));
    check( cpl_vector_save( qual1D, filename, XSH_SPECTRUM_QUAL_BPP,
      s->qual_header, CPL_IO_EXTEND));
    xsh_free_vector( &flux1D);
    xsh_free_vector( &err1D);
    xsh_free_vector( &qual1D);
  }

  
  check( product_frame = cpl_frame_new() ) ;
  check( cpl_frame_set_filename( product_frame,filename ));
  check( cpl_frame_set_type( product_frame, CPL_FRAME_TYPE_IMAGE )) ;
  check( cpl_frame_set_level( product_frame, CPL_FRAME_LEVEL_FINAL )) ;
  check( cpl_frame_set_group( product_frame, CPL_FRAME_GROUP_PRODUCT ));

  cleanup:
    if (cpl_error_get_code () != CPL_ERROR_NONE) {
      xsh_free_frame(&product_frame);
      product_frame = NULL;
    }
    return product_frame;
}


/**@}*/
