/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004,2005 European Southern Observatory
 *
 * This program 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
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <cpl.h>
#include "sinfo_pro_save.h"
#include "sinfo_key_names.h"
#include "sinfo_functions.h"
#include "sinfo_utilities.h" 
#include "sinfo_globals.h" 

static int 
sinfo_pfits_put_qc(
		   cpl_propertylist       *   plist,
		   cpl_table          *   qclog);

static void
sinfo_log_pro(char* name_o,
              const char* pro_catg, 
              int frm_type, 
              cpl_frameset* ref_set,
              cpl_frameset** out_set, 
              cpl_propertylist** plist,
              cpl_parameterlist* parlist, 
              const char* recid);


static void
sinfo_check_name(const char* in, char** ou, int type, char** paf);

static void
sinfo_clean_header(cpl_propertylist** header);
static void
sinfo_clean_cube_header(cpl_propertylist** header);


static FILE * 
sinfo_paf_print_header(
		       const char    *   filename,
		       const char    *   paf_id,
		       const char    *   paf_desc,
		       const char    *   login_name,
		       char    *   datetime) ;



/**@{*/
/**
 * @defgroup sinfo_pro_save Functions to save a product
 *
 * TBD
 */
/*--------------------------------------------------------------------------*/
/**
   @brief    Open a new PAF file, output a default header.
   @param    filename    Name of the file to create.
   @param    paf_id        PAF identificator.
   @param    paf_desc    PAF description.
   @param    login_name  Login name
   @param    datetime    Date
   @return    Opened file pointer.

   This function creates a new PAF file with the requested file name.
   If another file already exists with the same name, it will be
   overwritten (if the file access rights allow it).

   A default header is produced according to the VLT DICB standard. You
   need to provide an identificator (paf_id) of the producer of the
   file. Typically, something like "ISAAC/zero_point".

   The PAF description (paf_desc) is meant for humans. Typically,
   something like "Zero point computation results".

   This function returns an opened file pointer, ready to receive more
   data through fprintf's. The caller is responsible for fclose()ing
   the file.
*/
/*--------------------------------------------------------------------------*/
static FILE * 
sinfo_paf_print_header(
		       const char    *   filename,
		       const char    *      paf_id,
		       const char    *    paf_desc,
		       const char    *   login_name,
		       char    *   datetime)
{
  FILE * paf ;
    
  if ((paf=fopen(filename, "w"))==NULL) {
    sinfo_msg_error("cannot create PAF file [%s]", filename);
    return NULL ;
  }
  fprintf(paf, "PAF.HDR.START         ;# start of header\n");
  fprintf(paf, "PAF.TYPE              \"pipeline product\" ;\n");
  fprintf(paf, "PAF.ID                \"%s\"\n", paf_id);
  fprintf(paf, "PAF.NAME              \"%s\"\n", filename);
  fprintf(paf, "PAF.DESC              \"%s\"\n", paf_desc);
  fprintf(paf, "PAF.CRTE.NAME         \"%s\"\n", login_name) ;
  fprintf(paf, "PAF.CRTE.DAYTIM       \"%s\"\n", datetime) ;
  fprintf(paf, "PAF.LCHG.NAME         \"%s\"\n", login_name) ;
  fprintf(paf, "PAF.LCHG.DAYTIM       \"%s\"\n", datetime) ;
  fprintf(paf, "PAF.CHCK.CHECKSUM     \"\"\n");
  fprintf(paf, "PAF.HDR.END           ;# end of header\n");
  fprintf(paf, "\n");
  return paf ;
}
/**
   @name sinfo_update_fits_card_int
   @brief update an integer card in the header of a FITS file
   @param file file name
   @param card card name
   @param value card value
   @return if success 0 ; else -1
*/

int 
sinfo_update_fits_card_int(const char* file,const char* card,int value)
{
  cpl_propertylist *   plist =NULL;
  if ((plist = cpl_propertylist_load(file, 0)) == NULL) {
    sinfo_msg_error( "getting header from file %s",file);
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }

  if (CPL_ERROR_NONE!=cpl_propertylist_set_int(plist,card,value)){
    sinfo_msg_error( "setting header of file %s",file);
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }
  cpl_propertylist_delete(plist) ;
  return 0;
}

/**
   @name sinfo_update_fits_card_float
   @brief update a float card in the header of a FITS file
   @param file file name
   @param card card name
   @param value card value
   @return if success 0 ; else -1
*/

int 
sinfo_update_fits_card_float(const char* file,const char* card,float value)
{
  cpl_propertylist *   plist =NULL;
  if ((plist = cpl_propertylist_load(file, 0)) == NULL) {
    sinfo_msg_error( "getting header from file %s",file);
    sinfo_msg_error("%s", (char* ) cpl_error_get_message());
    sinfo_msg_error("%s", (char* ) cpl_error_get_where());
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }

  if (CPL_ERROR_NONE!=cpl_propertylist_set_float(plist,card,value)){
    sinfo_msg_error( "setting header of file %s",file);
    sinfo_msg_error("%s", (char* ) cpl_error_get_message());
    sinfo_msg_error("%s", (char* ) cpl_error_get_where());
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }
  cpl_propertylist_delete(plist) ;
  return 0;
}


/**
   @name sinfo_update_fits_card_double
   @brief update a double card in the header of a FITS file
   @param file file name
   @param card card name
   @param value card value
   @return if success 0 ; else -1
*/

int 
sinfo_update_fits_card_double(const char* file,const char* card,double value)
{
  cpl_propertylist *   plist =NULL;
  if ((plist = cpl_propertylist_load(file, 0)) == NULL) {
    sinfo_msg_error( "getting header from file %s",file);
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }

  if (CPL_ERROR_NONE!=cpl_propertylist_set_double(plist,card,value)){
    sinfo_msg_error( "gsetting header of file %s",file);
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }
  cpl_propertylist_delete(plist) ;
  return 0;
}

/**
   @name sinfo_update_fits_card_long
   @brief update a long card in the header of a FITS file
   @param file file name
   @param card card name
   @param value card value
   @return if success 0 ; else -1
*/

int 
sinfo_update_fits_card_long(const char* file,const char* card,long value)
{
  cpl_propertylist *   plist =NULL;
  if ((plist = cpl_propertylist_load(file, 0)) == NULL) {
    sinfo_msg_error( "getting header from file %s",file);
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }
  if (CPL_ERROR_NONE!=cpl_propertylist_set_long(plist,card,value)){
    sinfo_msg_error( "setting header of file %s",file);
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }
  cpl_propertylist_delete(plist) ;
  return 0;
}

/**
   @name sinfo_update_fits_card_string
   @brief update a string card in the header of a FITS file
   @param file file name
   @param card card name
   @param value card value
   @return if success 0 ; else -1
*/

int 
sinfo_update_ims_fits_card_string(cpl_imagelist* iml,
                                  const char* file, 
                                  const char* card,
                                  const char* value)
{

  cpl_propertylist *   plist =NULL;
  if ((plist = cpl_propertylist_load(file, 0)) == NULL) {
    sinfo_msg_error( "getting header from reference ima frame %s",file);
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }


  if (CPL_ERROR_NONE!=cpl_propertylist_set_string(plist,card,value)){
    sinfo_msg_error( "getting header from reference ima frame %s",file);
    cpl_propertylist_delete(plist) ;
    return -1 ;
  }

  if (cpl_imagelist_save(iml,file,CPL_BPP_IEEE_FLOAT,
                         plist,CPL_IO_DEFAULT)!=CPL_ERROR_NONE) {
    sinfo_msg_error( "getting header from reference ima frame %s",file);
    cpl_propertylist_delete(plist) ;
  }
  cpl_propertylist_delete(plist) ;
  return 0;
}
/* Not used 
static int sinfo_save_paf(char* name_p, 
			  const char* rec_id, 
			  cpl_table* qclog,
			  cpl_propertylist*   plist,
			  const char*      pro_catg)
{



  FILE            *   paf ;
  const char            *   sval ;
  char            key_name[FILE_NAME_SZ] ;
  char            key_paf[FILE_NAME_SZ] ;
  char            key_dpaf[FILE_NAME_SZ] ;
  char            key_type[FILE_NAME_SZ] ;
  char            key_value[FILE_NAME_SZ] ;
  double dval=0;

  int i =0;
  int n=0;
  sinfo_msg( "Writing %s" , name_p) ;
  // Create the default PAF header
  if ((paf = sinfo_paf_print_header(name_p,rec_id,"QC file","login-name",
				    sinfo_get_datetime_iso8601())) == NULL) {
    sinfo_msg_error( "cannot open file [%s] for output", name_p) ;
    return -1 ;
  }
  if (sinfo_check_rec_status(0) == -1) {
    sinfo_msg_error( "Something was wrong") ;
    return -1 ;
  }

  // Test entries
  sinfo_blank2dot(PAF_NAME_PIPE_ID,key_dpaf);
  fprintf(paf,"%-21s \"%s\" ;# %s\" \n", key_dpaf,
	  VERSION,KEY_HELP_PIPE_ID);

  strcpy(key_name,KEY_NAME_PIPEFILE);        
  strcpy(key_paf,KEY_NAME_PIPEFILE);        
  sinfo_blank2dot(key_paf,key_dpaf);
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf,"%-21s \"%s\" ;# %s\" \n",key_dpaf,
	    cpl_propertylist_get_string(plist,key_name),KEY_HELP_PIPEFILE);
  }

  // Value: "TEMPORARY", "PREPROCESSED", "REDUCED" or "QCPARAM".
  strcpy(key_name,KEY_NAME_PRO_TYPE);
  strcpy(key_paf,PAF_NAME_PRO_TYPE);    
  sinfo_blank2dot(key_paf,key_dpaf);
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf,"%-21s \"%s\" ;# %s\" \n", key_dpaf,
	    cpl_propertylist_get_string(plist,key_name),KEY_HELP_PRO_TYPE);
  }

  strcpy(key_name,KEY_NAME_PRO_DATANCOM); 
  strcpy(key_paf,PAF_NAME_PRO_DATANCOM); 
  sinfo_blank2dot(key_paf,key_dpaf);
  if (sinfo_propertylist_has(plist,key_name)) {
    fprintf(paf,"%-21s %d ;# %s\" \n",key_dpaf,
	    cpl_propertylist_get_int(plist,key_name),KEY_HELP_PRO_DATANCOM);
  }
     
  strcpy(key_name,KEY_NAME_NCORRS_NAME);
  strcpy(key_paf,PAF_NAME_NCORRS_NAME);
  sinfo_blank2dot(key_paf,key_dpaf);
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf,"%-21s \"%s\" ;# %s \n", key_dpaf,
	    cpl_propertylist_get_string(plist,key_name),KEY_HELP_NCORRS_NAME);
  }

  strcpy(key_name,KEY_NAME_DET_NDSAMPLES);
  strcpy(key_paf,PAF_NAME_DET_NDSAMPLES);
  sinfo_blank2dot(key_paf,key_dpaf);        
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf,"%-21s %d ;# %s \n",key_dpaf, 
	    cpl_propertylist_get_int(plist,key_name),KEY_HELP_DET_NDSAMPLES);
  }

  strcpy(key_name,KEY_NAME_FILT_NAME);      
  strcpy(key_paf,PAF_NAME_FILT_NAME);      
  sinfo_blank2dot(key_paf,key_dpaf);        
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf,"%-21s \"%s\" ;# %s \n", key_dpaf,
	    cpl_propertylist_get_string(plist,key_name),KEY_HELP_FILT_NAME);
  }

  strcpy(key_name,KEY_NAME_FILT_ID);        
  strcpy(key_paf,PAF_NAME_FILT_ID);        
  sinfo_blank2dot(key_paf,key_dpaf);        
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf,"%-21s \"%s\" ;# %s \n",key_dpaf, 
	    cpl_propertylist_get_string(plist,key_name),KEY_HELP_FILT_ID);
  }

  strcpy(key_name,KEY_NAME_PREOPTICS);        
  strcpy(key_paf,PAF_NAME_PREOPTICS);        
  sinfo_blank2dot(key_paf,key_dpaf);        
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf,"%-21s \"%s\" ;# %s \n",key_dpaf, 
	    cpl_propertylist_get_string(plist,key_name),KEY_HELP_PREOPTICS);
  }

  strcpy(key_name,KEY_NAME_GRAT_NAME);        
  strcpy(key_paf,PAF_NAME_GRAT_NAME);        
  sinfo_blank2dot(key_paf,key_dpaf);        
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf, "%-21s \"%s\" ;# %s \n",key_dpaf, 
	    cpl_propertylist_get_string(plist,key_name),KEY_HELP_GRAT_NAME);
  }
    
  strcpy(key_name,KEY_NAME_GRAT_WLEN);        
  strcpy(key_paf,PAF_NAME_GRAT_WLEN);        
  sinfo_blank2dot(key_paf,key_dpaf);        
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf, "%-21s %f ;# %s \n", key_dpaf,
	    cpl_propertylist_get_double(plist,key_name),KEY_HELP_GRAT_WLEN);
  }

  strcpy(key_name,KEY_NAME_PRO_RECID);  
  strcpy(key_paf,PAF_NAME_PRO_RECID);  
  sinfo_blank2dot(key_paf,key_dpaf);        
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf,"%-21s \"%s\" ;# %s \n", key_dpaf,
	    cpl_propertylist_get_string(plist,key_name),KEY_HELP_PRO_RECID);
  }


  // snprintf(cval, MAX_NAME_SIZE-1,"CPL-%s", get_cpl_version());
  strcpy(key_name,KEY_NAME_PRO_DRSID); 
  strcpy(key_paf,PAF_NAME_PRO_DRSID); 
  sinfo_blank2dot(key_paf,key_dpaf);        
  if (sinfo_propertylist_has(plist, key_name)) {
    fprintf(paf,"%-21s \"%s\" ;# %s \n",key_dpaf, 
	    cpl_propertylist_get_string(plist,key_name),KEY_HELP_PRO_DRSID);
  }

  if (sinfo_propertylist_has(plist,KEY_NAME_DATE_OBS)) {
    sval = sinfo_pfits_get_date_obs(plist);
    strcpy(key_paf,KEY_NAME_DATE_OBS); 
    sinfo_blank2dot(key_paf,key_dpaf);        
    fprintf(paf, "%-21s \"%s\" ;# %s\n",key_dpaf, 
	    sval,KEY_HELP_DATE_OBS) ;
  }

  
  if (sinfo_propertylist_has(plist,KEY_NAME_TEL_AIRM_START)) {
    dval = sinfo_pfits_get_airmass_start(plist);
    strcpy(key_paf,PAF_NAME_TEL_AIRM_START); 
    sinfo_blank2dot(key_paf,key_dpaf);        
    fprintf(paf, "%-21s \"%f\" ;# %s \n",key_dpaf, 
	    dval,KEY_HELP_TEL_AIRM_START) ;
  }
   
  if (sinfo_propertylist_has(plist,KEY_NAME_ARCFILE)) {
    sval = sinfo_pfits_get_arcfile(plist);
    strcpy(key_paf,KEY_NAME_ARCFILE);
    sinfo_blank2dot(key_paf,key_dpaf);      
    fprintf(paf, "%-21s \"%s\" ;# %s \n", key_dpaf,sval,KEY_HELP_ARCFILE) ;

  } else if (sinfo_propertylist_has(plist,KEY_NAME_PRO_REC1_RAW1_NAME)) {
    sval = sinfo_pfits_get_rec1raw1name(plist);
    strcpy(key_paf,KEY_NAME_ARCFILE);
    sinfo_blank2dot(key_paf,key_dpaf);       
    fprintf(paf, "%-21s \"%s\" ;# %s \n", key_dpaf,sval,KEY_HELP_ARCFILE) ;
  } else {
    sinfo_msg_error("%s is missing QC LOG will fail!",KEY_NAME_ARCFILE);
  }

  if (sinfo_propertylist_has(plist,KEY_NAME_TPL_ID)) {
    sval = sinfo_pfits_get_templateid(plist);
    strcpy(key_paf,PAF_NAME_TPL_ID);
    sinfo_blank2dot(key_paf,key_dpaf);       
    fprintf(paf, "%-21s \"%s\" ; # %s\n", key_dpaf,
	    sval,KEY_HELP_TPL_ID) ;
 
  }

  if (sinfo_propertylist_has(plist,KEY_NAME_DET_DIT)) { 
    strcpy(key_paf,PAF_NAME_DET_DIT); 
    sinfo_blank2dot(key_paf,key_dpaf);        
    fprintf(paf,"%-21s %f ; # %s\n", key_dpaf,
	    sinfo_pfits_get_dit(plist),KEY_HELP_DET_DIT) ;
  }


  if (sinfo_propertylist_has(plist,KEY_NAME_DET_NDIT)) {
    strcpy(key_paf,PAF_NAME_DET_NDIT); 
    sinfo_blank2dot(key_paf,key_dpaf); 
    fprintf(paf,"%-21s %d ; # %s\n", key_dpaf,
	    sinfo_pfits_get_ndit(plist),KEY_HELP_DET_NDIT) ;
  }

  if (sinfo_propertylist_has(plist,KEY_NAME_NCORRS_NAME)) {
    sval = sinfo_pfits_get_ncorrs_name(plist);
    strcpy(key_paf,PAF_NAME_NCORRS_NAME); 
    sinfo_blank2dot(key_paf,key_dpaf);        

    fprintf(paf, "%-21s \"%s\" ; # %s\n", key_dpaf,
	    sval, KEY_HELP_NCORRS_NAME) ;
  }
    
  if (sinfo_propertylist_has(plist,KEY_NAME_DPR_TYPE)) {
    sval = sinfo_pfits_get_dpr_type(plist);
    strcpy(key_paf,PAF_NAME_DPR_TYPE); 
    sinfo_blank2dot(key_paf,key_dpaf);        
    fprintf(paf, "%-21s \"%s\" ; # %s\n", key_dpaf,
	    sval, KEY_HELP_DPR_TYPE) ;
  }

  if (sinfo_propertylist_has(plist,KEY_NAME_DPR_TECH)) {
    sval = sinfo_pfits_get_dpr_tech(plist);
    strcpy(key_paf,PAF_NAME_DPR_TECH); 
    sinfo_blank2dot(key_paf,key_dpaf);   
    fprintf(paf, "%-21s \"%s\" ; # %s\n", key_dpaf,
	    sval, KEY_HELP_DPR_TECH) ;
  }
    

  if (sinfo_propertylist_has(plist,KEY_NAME_DPR_CATG)) {
    sval = sinfo_pfits_get_dpr_catg(plist);
    strcpy(key_paf,PAF_NAME_DPR_CATG); 
    sinfo_blank2dot(key_paf,key_dpaf); 
    fprintf(paf, "%-21s \"%s\" ; # %s\n", key_dpaf,
	    sval, KEY_HELP_DPR_CATG) ;
  }
    
  strcpy(key_paf,PAF_NAME_PRO_CATG); 
  sinfo_blank2dot(key_paf,key_dpaf); 
  fprintf(paf, "%-21s \"%s\" ; # %s\n", key_dpaf,
	  pro_catg, KEY_HELP_PRO_CATG) ;

  if (sinfo_propertylist_has(plist,KEY_NAME_INS_SETUP)) {
    sval = sinfo_pfits_get_ins_setup(plist);
    strcpy(key_paf,PAF_NAME_INS_SETUP); 
    sinfo_blank2dot(key_paf,key_dpaf); 
    fprintf(paf, "%-21s \"%s\" ; # %s\n", key_dpaf,
	    sval, KEY_HELP_INS_SETUP) ;
  }

  n=cpl_table_get_nrow(qclog);
  for(i=0;i<n;i++) {
    strcpy(key_paf,cpl_table_get_string(qclog,"key_name",i));
    sinfo_blank2dot(key_paf,key_name);
    strcpy(key_type,cpl_table_get_string(qclog,"key_type",i));
    strcpy(key_value,cpl_table_get_string(qclog,"key_value",i));
    if(strcmp(key_type,"CPL_TYPE_STRING") == 0) {
      strcat(key_name,"               \"%s\"\n");
      fprintf(paf, key_name, key_value) ;
    } else if(strcmp(key_type,"CPL_TYPE_BOOL") == 0) {
      strcat(key_name,"               ");
      strcat(key_name,"%c\n");
      fprintf(paf, key_name, atoi(key_value)) ;
    } else if(strcmp(key_type,"CPL_TYPE_INT") == 0) {
      strcat(key_name,"               ");
      strcat(key_name,"%d\n");
      fprintf(paf, key_name, atoi(key_value)) ;
    } else if(strcmp(key_type,"CPL_TYPE_FLOAT") == 0) {
      strcat(key_name,"               ");
      strcat(key_name,"%g\n");
      fprintf(paf, key_name, (float) atof(key_value)) ;
    } else if(strcmp(key_type,"CPL_TYPE_DOUBLE") == 0) {
      strcat(key_name,"               ");
      strcat(key_name,"%g\n");
      fprintf(paf, key_name, atof(key_value)) ;
    }

  }
  fprintf(paf, "\n");
  fclose(paf) ;

  if (sinfo_check_rec_status(1) == -1) {
    sinfo_msg_error( "Something was wrong reading FITS keys") ;
    return -1 ;
  }
  return 0;

}

*/

/*---------------------------------------------------------------------------*/
/**
   @brief    Save the image product on disk
   @param    ima      the input imagelist 
   @param    ref      the reference frame set 
   @param    set      the output frame set
   @param    out_file the output file name
   @param    pro_catg the frame product category
   @param    qclog    the table to store quality control parameters
   @param    recid    the recipe id
   @param    parlist  the recipe parameter list
   @return   0 if everything is ok, -1 otherwise
*/
/*---------------------------------------------------------------------------*/


int sinfo_pro_save_ima(
		       cpl_image       *   ima,
		       cpl_frameset    *   ref,
		       cpl_frameset    *   set,
		       const char      *   out_file,
		       const char      *   pro_catg,
		       cpl_table       *   qclog,
		       const char      *   recid,
		       cpl_parameterlist* parlist)

{
  char      *         name_o ;
  char      *         name_p ;

  cpl_propertylist *   plist =NULL;
  cpl_frame       *   first_frame=NULL;
  char            *   ref_file=NULL;

  /* Get the reference file  */
  first_frame = cpl_frameset_get_first(ref) ;
  ref_file = (char*) cpl_strdup(cpl_frame_get_filename(first_frame)) ;
 
  name_o = cpl_malloc(FILE_NAME_SZ * sizeof(char));
  name_p = cpl_malloc(FILE_NAME_SZ * sizeof(char));
  sinfo_check_name(out_file, &name_o, CPL_FRAME_TYPE_IMAGE, &name_p);
  sinfo_msg( "Writing ima %s pro catg %s" , name_o, pro_catg) ;

  /* Get FITS header from reference file */
  if ((cpl_error_code)((plist=cpl_propertylist_load(ref_file, 0)) == NULL)) {
    sinfo_msg_error( "getting header from reference ima frame %s",ref_file);
    cpl_propertylist_delete(plist) ;
    cpl_free(ref_file);
    return -1 ;
  }

  sinfo_clean_header(&plist);
  if ( ( strstr(pro_catg,"MASTER_PSF") != NULL ) || 
       ( strstr(pro_catg,"STD_STAR_SPECTRUM") != NULL ) || 
       ( strstr(pro_catg,"STD_STAR_SPECTRA") != NULL ) ) {
    sinfo_clean_cube_header(&plist);
  }

  /* Add DataFlow keywords and log the saved file in the input frameset */
  sinfo_log_pro(name_o, pro_catg, CPL_FRAME_TYPE_IMAGE, 
		ref,&set,&plist,parlist,recid);
  if(qclog != NULL) {
    sinfo_pfits_put_qc(plist, qclog) ;
  }

  /* Save the file */
  if (cpl_image_save(ima, name_o, CPL_BPP_IEEE_FLOAT, 
		     plist,CPL_IO_DEFAULT)!=CPL_ERROR_NONE) {
    sinfo_msg_error( "Cannot save the product %s",name_o);
    cpl_propertylist_delete(plist) ;
    cpl_free(ref_file);
    cpl_free(name_o);
    cpl_free(name_p);
    return -1 ;
  }

  /* THE PAF FILE FOR QC PARAMETERS 
     if( qclog != NULL) {
     sinfo_save_paf(name_p,recid,qclog,plist,pro_catg);  
     }
  */

  cpl_propertylist_delete(plist) ;
  cpl_msg_indent_less() ;
  cpl_free(name_o);
  cpl_free(name_p);
  cpl_free(ref_file);

  return 0 ;
}


/*---------------------------------------------------------------------------*/
/**
   @brief    Save the table products on disk
   @param    table    the input table 
   @param    ref      the reference frame set 
   @param    set      the output frame set
   @param    out_file the output file name
   @param    pro_catg the frame product category
   @param    qclog    the table to store quality control parameters
   @param    recid    the recipe id
   @param    parlist  the recipe parameter list
   @return   0 if everything is ok, -1 otherwise
*/
/*---------------------------------------------------------------------------*/



int sinfo_pro_save_tbl(
		       cpl_table       *   table,
		       cpl_frameset    *   ref,
		       cpl_frameset    *   set,
		       const char      *   out_file,
		       const char      *   pro_catg,      
		       cpl_table       *   qclog,
		       const char      *   recid,
		       cpl_parameterlist* parlist)

{
  char *    name_o =NULL;
  char *    name_p =NULL;
  cpl_propertylist *   plist=NULL ;
  cpl_frame* first_frame=NULL;
  char* ref_file=NULL;
  /* Get the reference file  */
  first_frame = cpl_frameset_get_first(ref) ;
  ref_file = cpl_strdup(cpl_frame_get_filename(first_frame)) ;
 
  name_o = cpl_malloc(FILE_NAME_SZ * sizeof(char));
  name_p = cpl_malloc(FILE_NAME_SZ * sizeof(char));
  sinfo_check_name(out_file, &name_o, CPL_FRAME_TYPE_TABLE, &name_p);
  sinfo_msg( "Writing tbl %s pro catg %s" , name_o, pro_catg) ;

  /* Get FITS header from reference file */
  if ((cpl_error_code)((plist = cpl_propertylist_load(ref_file,0)) == NULL)) 
    {
      sinfo_msg_error( "getting header from tbl frame %s",ref_file);
      cpl_propertylist_delete(plist) ;
      cpl_free(ref_file);
      cpl_free(name_o);
      cpl_free(name_p);
      return -1 ;
    }
  sinfo_clean_header(&plist);

  /* Add DataFlow keywords and log the saved file in the input frameset */
  sinfo_log_pro(name_o, pro_catg, CPL_FRAME_TYPE_TABLE, 
		ref,&set,&plist,parlist,recid);
  if(qclog != NULL) {
    sinfo_pfits_put_qc(plist, qclog) ;
  }
  /* Save the file */
  if (cpl_table_save(table, plist, NULL, name_o, CPL_IO_DEFAULT) 
      != CPL_ERROR_NONE) {
    sinfo_msg_error( "Cannot save the product: %s", name_o);
    cpl_propertylist_delete(plist) ;
    cpl_free(ref_file);
    cpl_free(name_o);
    cpl_free(name_p);
    return -1 ;
  }

 
  /* THE PAF FILE FOR QC PARAMETERS 
     if (qclog != NULL) {
     sinfo_save_paf(name_p,recid,qclog,plist,pro_catg);
     }
  */
  cpl_propertylist_delete(plist) ;
  cpl_msg_indent_less() ;
  cpl_free(name_o);
  cpl_free(name_p);
  cpl_free(ref_file);
  return 0 ;
}



/*---------------------------------------------------------------------------*/
/**
   @brief    Save the imagelist product on disk
   @param    ims      the input imagelist 
   @param    ref      the reference frame set 
   @param    set      the output frame set
   @param    out_file the output file name
   @param    pro_catg the frame product category
   @param    qclog    the table to store quality control parameters
   @param    recid    the recipe id
   @param    parlist  the recipe parameter list
   @return   0 if everything is ok, -1 otherwise
*/
/*---------------------------------------------------------------------------*/



int sinfo_pro_save_ims(
		       cpl_imagelist   *   ims,
		       cpl_frameset    *   ref,
		       cpl_frameset    *   set,
		       const char      *   out_file,
		       const char      *   pro_catg,      
		       cpl_table       *   qclog,
		       const char      *   recid,
		       cpl_parameterlist* parlist)

{
  char *               name_o=NULL;
  char *               name_p=NULL;

  cpl_propertylist * plist=NULL ;
  cpl_frame* first_frame=NULL;
  char* ref_file=NULL;
    
  /* Get the reference file  */
  first_frame = cpl_frameset_get_first(ref) ;
  ref_file = cpl_strdup(cpl_frame_get_filename(first_frame)) ;

  name_o = cpl_malloc(FILE_NAME_SZ * sizeof(char));
  name_p = cpl_malloc(FILE_NAME_SZ * sizeof(char));
  sinfo_check_name(out_file, &name_o, CPL_FRAME_TYPE_IMAGE, &name_p);
  sinfo_msg( "Writing ims %s pro catg %s" , name_o, pro_catg) ;
  /* Get FITS header from reference file */
  if ((cpl_error_code)((plist = cpl_propertylist_load(ref_file, 0)) == NULL))
    {
      sinfo_msg_error( "getting header from ims frame %s",ref_file);
      cpl_propertylist_delete(plist) ;
      cpl_free(ref_file);
      cpl_free(name_o);
      cpl_free(name_p);
      return -1 ;
    }
  sinfo_clean_header(&plist);
  if ( ( strstr(pro_catg,"STD") != NULL ) || 
       ( strstr(pro_catg,"PSF") != NULL ) || 
       ( strstr(pro_catg,"OBJ") != NULL ) ) {
    sinfo_clean_cube_header(&plist);
  }

  /* Add DataFlow keywords and log the saved file in the input frameset */
  sinfo_log_pro(name_o, pro_catg, CPL_FRAME_TYPE_IMAGE, 
		ref,&set,&plist,parlist,recid);
   
  if(qclog != NULL) {
    sinfo_pfits_put_qc(plist, qclog) ;
  }
   

  /* Save the file */
  if (cpl_imagelist_save(ims, name_o, CPL_BPP_IEEE_FLOAT, 
			 plist,CPL_IO_DEFAULT)!=CPL_ERROR_NONE) {
    sinfo_msg_error( "Cannot save the product %s",name_o);
    cpl_propertylist_delete(plist) ;
    cpl_free(ref_file);
    cpl_free(name_o);
    cpl_free(name_p);
    return -1 ;
  }

  /* THE PAF FILE FOR QC PARAMETERS 
     if (qclog != NULL) {
     sinfo_save_paf(name_p,recid,qclog,plist,pro_catg);
     }
  */
  cpl_propertylist_delete(plist) ;
  cpl_msg_indent_less() ;
  cpl_free(name_o);
  cpl_free(name_p);
  cpl_free(ref_file);
  return 0 ;
}



static void
sinfo_log_pro(char* name_o, 
              const char* pro_catg, 
              int frm_type, 
              cpl_frameset* ref_set,
              cpl_frameset** out_set,
              cpl_propertylist** plist,
              cpl_parameterlist* parlist, 
              const char* recid)
{
  cpl_frame* product_frame = NULL ;
  char * pipe_id=NULL;
  cpl_errorstate initial_errorstate = cpl_errorstate_get();

  pipe_id = cpl_calloc(FILE_NAME_SZ,sizeof(char));
  snprintf(pipe_id,MAX_NAME_SIZE-1,"%s%s","sinfo/",PACKAGE_VERSION);
  product_frame = cpl_frame_new() ;
  cpl_frame_set_filename(product_frame, name_o) ;
  cpl_frame_set_tag(product_frame, pro_catg) ;
  cpl_frame_set_type(product_frame, frm_type);
  cpl_frame_set_group(product_frame, CPL_FRAME_GROUP_PRODUCT);
  cpl_frame_set_level(product_frame, CPL_FRAME_LEVEL_FINAL);

#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0) 
  if(cpl_dfs_setup_product_header(*plist,product_frame,ref_set,parlist,recid,
				  pipe_id,KEY_VALUE_HPRO_DID,NULL) != CPL_ERROR_NONE) {
    sinfo_msg_warning("Problem in the product DFS-compliance");
    sinfo_msg_warning("%s", (char* ) cpl_error_get_message());
    cpl_errorstate_dump(initial_errorstate, CPL_FALSE, NULL);
    cpl_error_reset();
  }
#else
  if(cpl_dfs_setup_product_header(*plist,product_frame,ref_set,parlist,recid,
				  pipe_id,KEY_VALUE_HPRO_DID) != CPL_ERROR_NONE) {
    sinfo_msg_warning("Problem in the product DFS-compliance");
    sinfo_msg_warning((char* ) cpl_error_get_message());
    cpl_errorstate_dump(initial_errorstate, CPL_FALSE, NULL);
    cpl_error_reset();
  }
#endif
  cpl_frameset_insert(*out_set, product_frame);
  cpl_free(pipe_id);

}

static void
sinfo_check_name(const char* in, char** ou, int type, char** paf) {

  char* tmp=NULL;
  char  name_b[512] ;
  if (strstr(in, "." ) != NULL ) {
    tmp = sinfo_new_get_rootname(in);
    strcpy(name_b,tmp);
  } else {
    snprintf(name_b, MAX_NAME_SIZE-1,"%s", in) ;
  }
  strcpy(*ou,name_b);
  if (type == CPL_FRAME_TYPE_TABLE) {
    strcat(*ou,".fits");
  } else {
    strcat(*ou,".fits");
  }
  strcpy(*paf,name_b);
  strcat(*paf,".paf");

}


static void
sinfo_clean_header(cpl_propertylist** header)
{
  cpl_propertylist_erase_regexp(*header, "^ESO PRO .*",0);

}


static void
sinfo_clean_cube_header(cpl_propertylist** header)
{
  cpl_propertylist_erase_regexp(*header, "^CRVAL*",0);
  cpl_propertylist_erase_regexp(*header, "^CRPIX*",0);
  cpl_propertylist_erase_regexp(*header, "^CTYPE*",0);
  cpl_propertylist_erase_regexp(*header, "^CUNIT*",0);
  cpl_propertylist_erase_regexp(*header, "^CD1_1",0);
  cpl_propertylist_erase_regexp(*header, "^CD1_2",0);
  cpl_propertylist_erase_regexp(*header, "^CD2_1",0);
  cpl_propertylist_erase_regexp(*header, "^CD2_2",0);

}






static int 
sinfo_pfits_put_qc(
		   cpl_propertylist       *   plist,
		   cpl_table          *   qclog)
{
  char            key_name[FILE_NAME_SZ];
  char            key_value[FILE_NAME_SZ];
  char            key_type[FILE_NAME_SZ];
  char            key_help[FILE_NAME_SZ] ;

  int             i =0;
  int n =0;
  /* Test entries */
  if (plist == NULL) {
    sinfo_msg_error("plist=NULL, something strange");
    return -1 ;
  }
  /* Parameter Name:    PIPEFILE */

  n=cpl_table_get_nrow(qclog);
  for(i=0;i<n;i++) {
    strcpy(key_name,"ESO ");
    strcat(key_name,cpl_table_get_string(qclog,"key_name",i));
    strcpy(key_type,cpl_table_get_string(qclog,"key_type",i));
    strcpy(key_value,cpl_table_get_string(qclog,"key_value",i));
    strcpy(key_help,cpl_table_get_string(qclog,"key_help",i));

    /* sinfo_msg("name=%s type=%s value=%s\n",key_name,key_type,key_value); */
    if(!sinfo_propertylist_has(plist,key_name)) {
      if(strcmp(key_type,"CPL_TYPE_STRING") == 0) {
	cpl_propertylist_append_string(plist, key_name,key_value) ;
	cpl_propertylist_set_comment(plist, key_name,key_help) ;
      } else if(strcmp(key_type,"CPL_TYPE_BOOL") == 0) {
	cpl_propertylist_append_bool(plist, key_name,atoi(key_value)) ;
	cpl_propertylist_set_comment(plist, key_name,key_help) ;
      } else if(strcmp(key_type,"CPL_TYPE_INT") == 0) {
	cpl_propertylist_append_int(plist,key_name,atoi(key_value)) ;
	cpl_propertylist_set_comment(plist, key_name,key_help) ;
      } else if(strcmp(key_type,"CPL_TYPE_FLOAT") == 0) {
        cpl_propertylist_append_float(plist, key_name,(float)atof(key_value)) ;
        cpl_propertylist_set_comment(plist, key_name,key_help) ;
      } else if(strcmp(key_type,"CPL_TYPE_DOUBLE") == 0) {
        cpl_propertylist_append_double(plist, key_name,atof(key_value)) ;
        cpl_propertylist_set_comment(plist, key_name,key_help) ;
      }
    }

  }

  return 0 ;
}







cpl_table *
sinfo_qclog_init(void)
{

  cpl_table *table;

  table = cpl_table_new(0);
  cpl_table_new_column(table,"key_name", CPL_TYPE_STRING);
  cpl_table_new_column(table,"key_type", CPL_TYPE_STRING);
  cpl_table_new_column(table,"key_value", CPL_TYPE_STRING);
  cpl_table_new_column(table,"key_help", CPL_TYPE_STRING);

  return table;
}





int
sinfo_qclog_add_int(cpl_table* table,
		    const char*  key_name,  
		    const int    value,
		    const char*  key_help,
		    const char*  format)
{
  int sz = cpl_table_get_nrow(table);
  int raw = sz;
  char key_value[FILE_NAME_SZ];
  char key_type[FILE_NAME_SZ];

  snprintf(key_value,MAX_NAME_SIZE-1,format,value);
  strcpy(key_type,"CPL_TYPE_INT"); 
 
  cpl_table_set_size(table,sz+1);

  cpl_table_set_string(table,"key_name" ,raw,key_name);
  cpl_table_set_string(table,"key_type" ,raw,key_type);
  cpl_table_set_string(table,"key_value",raw,key_value);
  cpl_table_set_string(table,"key_help" ,raw,key_help);

  return 0;

}



int
sinfo_qclog_add_bool(cpl_table* table,
		     const char*  key_name,  
		     const char   value,
		     const char*  key_help,
		     const char*  format)
{
  int sz = cpl_table_get_nrow(table);
  int raw = sz;
  char key_value[FILE_NAME_SZ];
  char key_type[FILE_NAME_SZ];

  snprintf(key_value,MAX_NAME_SIZE-1,format,value);
  strcpy(key_type,"CPL_TYPE_BOOL"); 

  cpl_table_set_size(table,sz+1);

  cpl_table_set_string(table,"key_name" ,raw,key_name);
  cpl_table_set_string(table,"key_type" ,raw,key_type);
  cpl_table_set_string(table,"key_value",raw,key_value);
  cpl_table_set_string(table,"key_help" ,raw,key_help);

  return 0;

}



int
sinfo_qclog_add_float(cpl_table* table,
		      const char*  key_name,  
		      const float  value,
		      const char*  key_help,
		      const char*  format)
{
  int sz = cpl_table_get_nrow(table);
  int raw = sz;
  char key_value[FILE_NAME_SZ];
  char key_type[FILE_NAME_SZ];

  snprintf(key_value,MAX_NAME_SIZE-1,format,value);
  strcpy(key_type,"CPL_TYPE_FLOAT"); 
 
  cpl_table_set_size(table,sz+1);

  cpl_table_set_string(table,"key_name" ,raw,key_name);
  cpl_table_set_string(table,"key_type" ,raw,key_type);
  cpl_table_set_string(table,"key_value",raw,key_value);
  cpl_table_set_string(table,"key_help" ,raw,key_help);

  return 0;

}



int
sinfo_qclog_add_double(cpl_table* table,
		       const char*  key_name,  
		       const double value,
		       const char*  key_help,
		       const char*  format)
{
  int sz = cpl_table_get_nrow(table);
  int raw = sz;
  char key_value[FILE_NAME_SZ];
  char key_type[FILE_NAME_SZ];

  snprintf(key_value,MAX_NAME_SIZE-1,format,value);
  strcpy(key_type,"CPL_TYPE_DOUBLE"); 
 
  cpl_table_set_size(table,sz+1);

  cpl_table_set_string(table,"key_name" ,raw,key_name);
  cpl_table_set_string(table,"key_type" ,raw,key_type);
  cpl_table_set_string(table,"key_value",raw,key_value);
  cpl_table_set_string(table,"key_help" ,raw,key_help);

  return 0;

}

int
sinfo_qclog_add_string(cpl_table* table,
		       const char*  key_name,  
		       const char*  value,
		       const char*  key_help,
		       const char*  format)
{
  int sz = cpl_table_get_nrow(table);
  int raw = sz;
  char key_value[FILE_NAME_SZ];
  char key_type[FILE_NAME_SZ];

  snprintf(key_value,MAX_NAME_SIZE-1,format,value);
  strcpy(key_type,"CPL_TYPE_STRING"); 
 
  cpl_table_set_size(table,sz+1);

  cpl_table_set_string(table,"key_name" ,raw,key_name);
  cpl_table_set_string(table,"key_type" ,raw,key_type);
  cpl_table_set_string(table,"key_value",raw,key_value);
  cpl_table_set_string(table,"key_help" ,raw,key_help);

  return 0;

}




/**@}*/
