/*                                                                            *
 *   This file is part of the ESO UVES Pipeline                               *
 *   Copyright (C) 2004,2005 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: 2011-12-08 13:53:15 $
 * $Revision: 1.41 $
 * $Name: not supported by cvs2svn $
 * $Log: not supported by cvs2svn $
 * Revision 1.40  2010/12/20 16:27:24  amodigli
 * added check statement on cpl_table_insert()
 *
 * Revision 1.39  2010/09/24 09:31:30  amodigli
 * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
 *
 * Revision 1.37  2009/08/03 16:45:40  amodigli
 * cleaned output
 *
 * Revision 1.36  2009/08/03 13:18:17  amodigli
 * fixed compiler warnings
 *
 * Revision 1.35  2009/08/03 12:52:37  amodigli
 * some changes to improve robustness
 *
 * Revision 1.34  2008/09/29 06:51:54  amodigli
 * add #include <string.h>
 *
 * Revision 1.33  2008/06/20 14:23:14  amodigli
 * added flames_get_basename()
 *
 * Revision 1.32  2008/03/06 06:58:33  amodigli
 * _cor and _hwt (debug) tables not generated anymore to checak correlation to measure slitff size
 *
 * Revision 1.31  2008/03/03 10:34:09  amodigli
 * fixed errors getting pointer to image
 *
 * Revision 1.30  2008/02/25 07:10:08  amodigli
 * added param save_flat_size
 *
 * Revision 1.29  2008/02/22 13:23:52  amodigli
 * added filename dependance on debug products
 *
 * Revision 1.28  2008/02/18 16:23:08  amodigli
 * more robust SFLAT FWHM and YSHIFT determination
 *
 * Revision 1.27  2008/02/05 13:54:57  amodigli
 * fixed compilation warnings
 *
 * Revision 1.26  2008/01/29 08:03:46  amodigli
 * more robust YSHIFT determination
 *
 * Revision 1.25  2008/01/28 17:08:57  amodigli
 * added more check on accessed array indexes
 *
 * Revision 1.24  2008/01/27 13:53:33  amodigli
 * added flames_get_mff_hw_and_yshift which requires to include flames_midas_def.h
 *
 * Revision 1.23  2008/01/25 12:24:45  amodigli
 * added msffsz_flames
 *
 * Revision 1.22  2007/10/05 06:12:02  amodigli
 * cleaned code
 *
 * Revision 1.21  2007/06/28 09:31:49  jmlarsen
 * Bugfix in flames_set_crval_to_one
 *
 * Revision 1.20  2007/06/22 14:51:23  jmlarsen
 * Expanded, again, interface of uves_save_image()
 *
 * Revision 1.19  2007/06/22 11:35:59  amodigli
 * added true as last argument of uves_save_image()
 *
 * Revision 1.18  2007/06/20 11:12:01  jmlarsen
 * Moved flames_reset_crval_to_one()
 *
 * Revision 1.17  2007/06/06 08:17:33  amodigli
 * replace tab with 4 spaces
 *
 * Revision 1.16  2007/06/06 07:20:49  jmlarsen
 * Generalized flames_image_duplicate
 *
 * Revision 1.15  2007/05/31 16:06:23  jmlarsen
 * Added flames_select_all
 *
 * Revision 1.14  2007/05/30 15:38:18  jmlarsen
 * Check for CFITSIO runtime version
 *
 * Revision 1.13  2007/05/10 14:05:13  jmlarsen
 * Added flames_select_non_null()
 *
 * Revision 1.12  2007/05/08 14:02:21  amodigli
 * temporary def of flames_uves_save_ipercube
 *
 * Revision 1.11  2007/05/04 12:25:01  amodigli
 * added flames_new_ipercube and flames_new_table
 *
 * Revision 1.10  2007/04/26 13:25:42  jmlarsen
 * Added flames_sort_table_2()
 *
 * Revision 1.9  2007/04/24 12:49:34  jmlarsen
 * Replaced cpl_propertylist -> uves_propertylist which is much faster
 *
 * Revision 1.8  2007/04/23 07:09:02  amodigli
 * added flames_new_framelist
 *
 * Revision 1.7  2007/04/23 07:01:10  amodigli
 * uncommented BKG_XY_WIN_SZ_X BKG_XY_WIN_SZ_Y BKG_MAX_IO_WIN
 *
 * Revision 1.6  2007/04/20 14:49:23  jmlarsen
 * Added functions flames_merge/rename/sort_table()
 *
 * Revision 1.5  2007/04/03 11:08:40  jmlarsen
 * Merged table FITS headers when loading
 *
 * Revision 1.4  2007/01/29 13:09:55  jmlarsen
 * Work on conversion to CPL
 *
 * Revision 1.3  2007/01/10 11:08:34  jmlarsen
 * Don't define DRS_USE_ORDEF
 *
 * Revision 1.2  2006/12/01 12:52:31  jmlarsen
 * Added flames_pfits module
 *
 * Revision 1.1  2006/10/20 06:42:09  jmlarsen
 * Moved FLAMES source to flames directory
 *
 * Revision 1.114  2006/10/09 13:03:09  jmlarsen
 * Removed explicit uves_msg_softer/louder calls
 *
 */

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

/*---------------------------------------------------------------------------*/
/**
 * @defgroup flames_utils  Utility functions
 */
/*---------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
                            Includes
 ----------------------------------------------------------------------------*/
#include <flames_utils.h>
#include <flames_def_drs_par.h>
#include <flames_midas_def.h>

#include <uves_dfs.h>
#include <uves_utils_wrappers.h>
#include <uves_pfits.h>
#include <uves_error.h>
#include <uves_dump.h>

#include <fitsio.h>

#include <ctype.h>
#include <string.h>
/*-----------------------------------------------------------------------------
                            Defines
 ----------------------------------------------------------------------------*/


/**@{*/
/*-----------------------------------------------------------------------------
                            Functions prototypes
 ----------------------------------------------------------------------------*/

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

/*---------------------------------------------------------------------------*/
/**
  @brief get file base name
  @param filename 
  @return base filename
**/
/*---------------------------------------------------------------------------*/
char * 
flames_get_basename(const char *filename)
{
  char *p ;
  p = strrchr (filename, '/');
  return p ? p + 1 : (char *) filename;
}


/*---------------------------------------------------------------------------*/
/**
  @brief get 'flat size' half width and its shift to and order table
  @param frm input master flat frame
  @param frm input table name

  This test exists just to verify that function works
**/
/*---------------------------------------------------------------------------*/
cpl_error_code
flames_get_mff_hw_and_yshift(cpl_frame* frm, 
			     const int save_flat_size,
                             const char* name_tbl)
{

  uves_propertylist* h=NULL;
  cpl_type type;
  cpl_image* ima=NULL;
  cpl_image* sim=NULL;
  cpl_image* ima_fit=NULL;
  cpl_image* ima_tmp=NULL;

  cpl_table* tbl=NULL;
  cpl_table* cor=NULL;
  cpl_table* fit=NULL;
  cpl_table* hwt=NULL;
  cpl_table* flat_size=NULL;

  float* psim=NULL;

  float* pint=NULL;
  float* psmo=NULL;
  float* prat=NULL;
  float* py=NULL;

  float* pima=NULL;
  float* pfit=NULL;
  float* pcor=NULL;
  float* ps=NULL;
  int* psize=NULL;

  int unit=0;
  float rms=0;
  const char* name_ima=NULL;
  int *coeffi = NULL;
  double *coeffd = NULL;
  float avg=0;
  float max=0;

  int ima_id=0;

  int ord_min=0;
  int ord_max=0;
  int ord_cen=0;
  int ord=0;
  int index=0;

  int xc_pix=0;
  int yc_pix=0;
  int yc=0;

  int defpol[2];

  int length=0;
  int i=0;
  int j=0;

  int sx=0;
  int sy=0;

  double fct=0;
  double yc_trace=0;
  double flux=0;
  int y_box_size=30; //updated on the fly to 1.5*halfwidth
  int lly=0;
  int ury=0;
  int s=5;
  int size=0;
  int sum=0;
  int count=0;
  int d=0;
  int xmin=0;
  int xmax=0;
  int found=0;
  int k=0;
  cpl_size row=0;
  int status=0;
  float yshift=0;
  float halfwidth=0;
  int ymin=0;
  int ymax=0;
  int rmin=0;


  check_nomsg(name_ima=cpl_frame_get_filename(frm));
  check_nomsg(ima=cpl_image_load(name_ima,CPL_TYPE_FLOAT,0,0));
  check_nomsg(tbl=cpl_table_load(name_tbl,1,0));
  check_nomsg(ord_min=cpl_table_get_column_min(tbl,"ORDER"));
  check_nomsg(ord_max=cpl_table_get_column_max(tbl,"ORDER"));
  //uves_msg_warning("norders=%d",ord_max-ord_min+1);

  check(h=uves_propertylist_load(name_tbl,0),"Loading %s header",name_tbl);
        
  check(coeffi=uves_read_midas_array(h,"COEFFI",&length,&type,NULL),
	"Error reading COEFFI from %s", name_tbl);
        
  assure( type == CPL_TYPE_INT, CPL_ERROR_TYPE_MISMATCH,
	  "Type of COEFFI is %s, int expected",
	  uves_tostring_cpl_type(type) );


  check(coeffd=uves_read_midas_array(h,"COEFFD",&length,&type,NULL),
	"Error reading COEFFD from %s", name_tbl);
        
  assure( type == CPL_TYPE_DOUBLE, CPL_ERROR_TYPE_MISMATCH,
	  "Type of COEFFD is %s, int expected",
	  uves_tostring_cpl_type(type) );
  
  defpol[0] = coeffi[5];
  defpol[1] = coeffi[6];
  //uves_msg_warning("defpol1 = %d, defpol2 = %d", defpol[0], defpol[1]);
  
  check_nomsg(sx=cpl_image_get_size_x(ima));
  check_nomsg(sy=cpl_image_get_size_y(ima));

  xc_pix=sx/2;
  ord_cen=(ord_min+ord_max)/2;

  check_nomsg(hwt=cpl_table_new(sy));
  check_nomsg(cpl_table_new_column(hwt,"INT",CPL_TYPE_FLOAT));
  check_nomsg(cpl_table_fill_column_window_float(hwt,"INT",0,sy,0));
  check_nomsg(pint=cpl_table_get_data_float(hwt,"INT"));
  check_nomsg(pima=cpl_image_get_data_float(ima));

  //Smooth a bit (radii=5 pix) the central image cross order trace 
  for(j=s;j<sy-s-1;j++){
    avg=0;
    for(i=-s;i<=s;i++) {
      avg +=pima[(j+i)*sx+xc_pix];
    }
    pint[j]=avg/(2*s+1);
    //uves_msg_warning("int=%f",pint[j]);
  }
  size=(int)(sy/(ord_max-ord_min+1)/2);
  uves_msg_warning("size=%d",size);

  //Get the approximate master flat intensty profile
  check_nomsg(cpl_table_duplicate_column(hwt,"SMO",hwt,"INT"));
  check_nomsg(psmo=cpl_table_get_data_float(hwt,"SMO"));
  for(j=size;j<sy-size-1;j++){
    max=-10;
    for(i=-size/2;i<size/2;i++) {
       if(pint[j+i]>max) max=pint[j+i];
    }
    psmo[j]=max;
  }

  //Normalize the master flat cross order profile
  check_nomsg(cpl_table_duplicate_column(hwt,"RAT",hwt,"INT"));
  check_nomsg(cpl_table_divide_columns(hwt,"RAT","SMO"));
  check_nomsg(prat=cpl_table_get_data_float(hwt,"RAT"));
  /*
  check_nomsg(cpl_table_save(hwt, NULL, NULL,
                             uves_sprintf("%s%s",name_ima,"_hwt.fits"), CPL_IO_DEFAULT));
  */
  //Remove coss profile intensity oscillations of the top 20%
  for(j=0;j<sy;j++){
    prat[j]=(prat[j]<0.6) ? 0:1;
  }

  //Get the average master flat 'flat size'
  sum=0;
  count=0;
  found=0;
  check_nomsg(flat_size=cpl_table_new(0));
  check_nomsg(cpl_table_new_column(flat_size,"size",CPL_TYPE_INT));
  check_nomsg(psize=cpl_table_get_data_int(flat_size,"size"));

  //uves_msg_warning("size=%d-%d",size,sy-size);
  for(j=size;j<sy-size;j++){
    if((found==0) && (prat[j]<prat[j+1])) {
      xmin=j;
      found=1;
    }
    if((found==1) && (prat[j]>prat[j+1])) {
      xmax=j;
      found=2;
    }
    if((found==2) && (xmax > xmin)) {
      d=xmax-xmin;

      //uves_msg_warning("d=%d",d);
      count++;
      cpl_table_set_size(flat_size,count);
      //psize[count-1];
      check_nomsg(cpl_table_set_int(flat_size,"size",count-1,d));      
      sum+=d;
      found=0;
    }
  }
  /*
  check_nomsg(cpl_table_save(flat_size,NULL,NULL,
  uves_sprintf("%s%s",name_ima,"size.fits"),CPL_IO_DEFAULT));
  */
  check_nomsg(d=(int)cpl_table_get_column_median(flat_size,"size"));
  uves_free_table(&flat_size);

  //d=sum/count;

  //Subtracts 1 to be on safe side
  d-=save_flat_size;
  halfwidth=d/2.;
  y_box_size=(int)(halfwidth+5);
  uves_msg_warning("halfwidth=%f",halfwidth);
  uves_msg_warning("y_box_size=%d",y_box_size);
  /*
  check_nomsg(cpl_table_save(hwt, NULL, NULL,
  uves_sprintf("%s%s",name_ima,"_hwt.fits"), CPL_IO_DEFAULT));
  */

  //uves_msg_warning("defpol[0]=%d,defpol[1]=%d",defpol[0],defpol[1]);

 
  //build a synthetic flat of 'flat size' equal to the input one,
  //centred on the order trace
  check_nomsg(sim=cpl_image_new(sx,sy,CPL_TYPE_FLOAT));
  check_nomsg(psim=cpl_image_get_data_float(sim));
  for (ord=ord_min;ord<ord_max; ord++){

    for(xc_pix=1;xc_pix<=sx;xc_pix++) {
      yc_trace=0;
      index=0;
      // get Y position of trace at central X pix 
      for(j=0;j<=defpol[1];j++){

	fct = pow(ord,j);
	for(i=0;i<=defpol[0];i++){
           
	  yc_trace += coeffd[index]*pow(xc_pix,i)*fct;
	  ++index;
             //uves_msg_warning("index=%d yc_trace[%d][%d]=%g",index,j,i,yc_trace); 
	}

      }
      yc_pix=(int)(yc_trace+0.5);
      lly=(yc_pix-y_box_size>1)  ? yc_pix-y_box_size: 1;
      lly=(lly<sy)  ? lly: sy;
      ury=(yc_pix+y_box_size<sy) ? yc_pix+y_box_size: sy;
      ury=(ury>1) ? ury: 1;
      //uves_msg_warning("xc_pix=%d lly=%d",xc_pix,lly);
      //uves_msg_warning("yc_pix=%d ury=%d",yc_pix,ury);
      check_nomsg(flux=cpl_image_get_max_window(ima,xc_pix,lly,xc_pix,ury));
      //uves_msg_warning("ok min=%d max=%d",yc_pix-d/2,yc_pix+d/2);
      ymin=((yc_pix-d/2)>0)  ? -d/2 : -yc_pix;
      ymax=((yc_pix+d/2)<sy) ? d/2 : sy-yc_pix;
      //uves_msg("min=%d max=%d",ymin,ymax);
      for(yc=ymin;yc<ymax;yc++) {
            psim[xc_pix+(yc_pix+yc)*sx]=flux;
      }
      //uves_msg_warning("ord=%d xc_pix=%d yc_trace=%f f=%f",
      //	     ord,xc_pix,yc_trace,flux);

    }
  }

  uves_msg_warning("xc_pix=%dm yc_pix=%d",xc_pix,yc_pix);
  check_nomsg(ima_tmp=uves_image_smooth_x(sim,50));
  uves_free_image(&sim);
  check_nomsg(sim=cpl_image_duplicate(ima_tmp));
  uves_free_image(&ima_tmp);
/*
  cpl_image_save(sim,uves_sprintf("%s%s",name_ima,"_sim.fits"), 
  CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);
*/
  //cross correlate the synthetic flat with the input flat
  check_nomsg(cor=cpl_table_new(4*size));
  cpl_table_new_column(cor,"Y",CPL_TYPE_FLOAT);
  cpl_table_new_column(cor,"COR",CPL_TYPE_FLOAT);

  check_nomsg(cpl_table_fill_column_window_float(cor,"COR",0,4*size,0));
  check_nomsg(cpl_table_fill_column_window_float(cor,"Y",0,4*size,0));
  check_nomsg(pcor=cpl_table_get_data_float(cor,"COR"));
  check_nomsg(ps=cpl_table_get_data_float(cor,"Y"));
  check_nomsg(psim=cpl_image_get_data_float(sim));
  check_nomsg(pima=cpl_image_get_data_float(ima));


  k=0;
  for(s=-2*size;s<2*size;s++){
    k++;
    flux=0;
    for(j=0;j<sy;j++) {
      for(i=0;i<sx;i++) {
	if((j-s>0) && (j-s)<sy) {
	  flux+=pima[i+j*sx]*psim[i+(j-s)*sx];
	}
      }
    }
    ps[k]=(float)s;
    pcor[k]=flux;
    //uves_msg_warning("s=%d,flux=%g",s,flux);

  }
/*
  check_nomsg(cpl_table_save(cor, NULL, NULL,
  uves_sprintf("%s%s",name_ima,"_cor.fits"), CPL_IO_DEFAULT));
*/

  //Find the maximum of the cross correlation function
  check_nomsg(cpl_table_get_column_maxpos(cor,"COR",&row));
  check_nomsg(yshift=(float)cpl_table_get(cor,"Y",row,&status));
  rmin=( (row-d/2) >0)? (row-d/2):0;

//uves_msg("row=%d row-d/4=%d d/2=%d",row,row-d/4,d/2);
  //check_nomsg(fit=cpl_table_extract(cor,row-d/4,d/2));
  check_nomsg(fit=cpl_table_extract(cor,rmin,d/2));
  check_nomsg(ima_fit=cpl_image_new(1,d/2,CPL_TYPE_FLOAT));
  check_nomsg(pcor=cpl_table_get_data_float(fit,"COR"));
  check_nomsg(py=cpl_table_get_data_float(fit,"Y"));
  check_nomsg(pfit=cpl_image_get_data_float(ima_fit));

  for(i=0;i<d/2;i++) {
    pfit[i]=pcor[i];
  }
  check_nomsg(yshift=(float)cpl_image_get_centroid_y_window(ima_fit,1,1,1,d/2));
  yshift+=py[0];
  //cpl_image_save(ima_fit,uves_sprintf("%s%s",name_ima,"_ima_fit.fits"),
  //CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT);
  //uves_msg_warning("name_ima=%s",name_ima);
  SCFOPN(name_ima,D_R4_FORMAT,0,F_IMA_TYPE,&ima_id); // open input frame 
  rms=0.5;
  SCDWRR(ima_id,"YSHIFT", &yshift,1,1,&unit);
  SCDWRR(ima_id,"ESO QC YSHIFT RMS", &rms,1,1,&unit); 
  SCDWRR(ima_id,"HALFWIDTH", &halfwidth,1,1,&unit); 
  SCDWRR(ima_id,"ESO QC HALFWIDTH RMS", &rms,1,1,&unit); 
  SCFCLO(ima_id);


 cleanup:


  //uves_free_table(&tbl);
  uves_free_image(&ima);
  uves_free_image(&sim);
  uves_free_image(&ima_fit);
  uves_free_table(&flat_size);
  uves_free_table(&cor);
  uves_free_table(&fit);

  return cpl_error_get_code();


}


/**
@brief    
@param             input master flat frame
@param             input order table file name
@param             UVES chip id of frame
*/

cpl_error_code
msffsz_flames(cpl_frame *in_ima,
	      const int save_flat_size,
              const char *in_tab,
              enum uves_chip chip)

{

  const char* name=NULL;
  uves_propertylist* header=NULL;
  cpl_image* img=NULL;

  check_nomsg(flames_get_mff_hw_and_yshift(in_ima, save_flat_size, in_tab) );
  check_nomsg(name=cpl_frame_get_filename(in_ima));

  check( header = uves_propertylist_load(name,0),
           "Could not load %s header", name);

  check_nomsg(flames_set_header_char_1(header,"CHIPCHOICE",
				       uves_chip_tochar(chip)));
  check_nomsg(img=cpl_image_load(name,CPL_TYPE_FLOAT,0,0));
  check_nomsg(uves_save_image(img,name,header,true,true));
  uves_free_image(&img);


 cleanup:
  uves_free_propertylist(&header);
  uves_free_image(&img);
  return cpl_error_get_code();


}


void
msffsz_flames2(cpl_frame *in_ima,
	       const int save_flat_size,
              const char *in_tab,
              enum uves_chip chip)
{

//crossref in_ima in_tab 
//define/parameter p1 ?            ima "enter name of input image:"
//define/parameter p2 {ORDTAB} tbl "enter order table:"

//config/instr {p1}
//define/local pid/c/1/20 "flames_get_flat_size_robust" ? +lower
//define/local in_ima/c/1/80 "{p1}"
//define/local in_tab/c/1/80 "{p2}"

//define/local pix_scale/r/1/1 0
//define/local slit_arcsec/r/1/1 0
//define/local slit_pix/r/1/1 0
    double slit_pix;
    int *coeffi = NULL;
    cpl_table *order_table = NULL;

    uves_propertylist *header = NULL;
    uves_propertylist *header_tab = NULL;
    int ord_min, ord_max;
    cpl_image* img=NULL;

//pix_scale = m$value({in_ima},{h_pixscl})   !Pixelscale ["/pixel]
//slit_arcsec = m$value({in_ima},{h_sllen({PATHID})})
//slit_pix = slit_arcsec/pix_scale/{CCDBIN(2)} - 2.0
        
    check( header = uves_propertylist_load(cpl_frame_get_filename(in_ima),
                                           0),
           "Could not load %s header", cpl_frame_get_filename(in_ima));
    
    check_nomsg( slit_pix = 
                 uves_pfits_get_slitlength_pixels(header, 
                                                  chip) - 2.0);

//!stat/tab {in_tab} :NEWORD     !if rmiddumr.tbl   
//stat/tab {in_tab} :ORDER >Null      !if o580REDL.tbl

    check( order_table = cpl_table_load(in_tab,
                                        1, 1),
           "Failed to load order table %s", in_tab);

//define/local ord_min/i/1/1 {outputr(1)}
//define/local ord_max/i/1/1 {outputr(2)}

    ord_min = cpl_table_get_column_min(order_table, "ORDER");
    ord_max = cpl_table_get_column_max(order_table, "ORDER");

//write/key in_a {in_ima}
//write/key in_b {in_tab}

    {
        const char *IN_A = cpl_frame_get_filename(in_ima);
        const char *IN_B = in_tab;

//write/key slit/r/1/1 {slit_pix}
        float slit = slit_pix;
//set/format i1

//write/key defpol/i/1/2 {defpol}
//!read/desc {in_tab} COEFFI
//!read/desc {in_tab} COEFFD
//defpol(1) = {{in_tab},COEFFI(6)}
//defpol(2) = {{in_tab},COEFFI(7)}

        int defpol[2];
        cpl_type coeffi_type;
        int coeffi_length;

        check( header_tab = uves_propertylist_load(in_tab,
                                                   0),
               "Could not load %s header", in_tab);
        
        check( coeffi = uves_read_midas_array(
                   header_tab, "COEFFI", &coeffi_length,
                   &coeffi_type, NULL),
               "Error reading COEFFI from %s", in_tab);
        
        assure( coeffi_type == CPL_TYPE_INT, CPL_ERROR_TYPE_MISMATCH,
                "Type of COEFFI is %s, int expected",
                uves_tostring_cpl_type(coeffi_type) );
  
        defpol[0] = coeffi[5];
        defpol[1] = coeffi[6];
//!w/o "defpol(1)={defpol(1)} defpol(2)={defpol(2)}"
        uves_msg_debug("defpol1 = %d, defpol2 = %d", defpol[0], defpol[1]);

        uves_msg_debug("image = %s; table = %s", IN_A, IN_B);

//set/format
//RUN PIPE_EXE:flames_get_flat_size.exe

        check_nomsg(flames_set_header_char_1(header,"CHIPCHOICE",
					     uves_chip_tochar(chip)));
        check_nomsg(img=cpl_image_load(IN_A,CPL_TYPE_FLOAT,0,0));
	check_nomsg(uves_save_image(img,IN_A,header,true,true));
        uves_free_image(&img);

        assure( 0 == flames_get_flat_size(IN_A,
                                          IN_B,
                                          &slit,
                                          &ord_min,
                                          &ord_max,
                                          defpol,
                                          save_flat_size,
                                          FLAMES_DRS_SFF_HW_MIN,
                                          FLAMES_X_WIND_SIZE,
                                          FLAMES_Y_WIND_SIZE,
                                          FLAMES_Y_SEARCH_WIND,
                                          FLAMES_ORD_TRESH,
                                          FLAMES_N_CLIP_MED,
                                          FLAMES_N_CLIP_AVG,
                                          FLAMES_INT_TRESH),
                CPL_ERROR_ILLEGAL_OUTPUT,
                "flames_get_flat_size failed");
 


   }
 
//config/instr {in_ima}
//WRITE/DES {in_ima} CHIPCHOICE/c/1/1 {chip({PATHID})}
//WRITE/DES {in_ima} MAXFIBRES/i/1/1 0
//return 0
        
  cleanup:
    uves_free_table(&order_table);
    uves_free_int(&coeffi);
    uves_free_propertylist(&header);
    uves_free_propertylist(&header_tab);
    uves_free_image(&img);
    return;
}




/*---------------------------------------------------------------------------*/
/**
   @brief    Get FLAMES library binary version number
   @return   Binary version number

   This functions gives knowledge of the compile time version number of
   the FLAMES library at runtime.
   UVES_BINARY_VERSION is the same for the entire package.
*/
/*---------------------------------------------------------------------------*/
int
flames_get_version_binary(void)
{
    float version;
    float version_returnvalue = fits_get_version(&version);
        
    uves_msg_debug("Linking against CFITSIO version %g (%g)", version, 
                   version_returnvalue);
    return UVES_BINARY_VERSION;
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Create frame from image buffer and header
   @param    filename          to save to
   @param    image             data to save
   @param    header            FITS header
   @return   newly allocated frame
*/
/*---------------------------------------------------------------------------*/
cpl_frame *
flames_new_table(const char *filename, 
         const cpl_table *table, 
         const uves_propertylist *header)
{
    cpl_frame *f = NULL;

    f = cpl_frame_new();
    cpl_frame_set_filename(f, filename);
    cpl_frame_set_type(f, CPL_FRAME_TYPE_TABLE);

    check( uves_table_save(table, header, NULL, filename, CPL_IO_DEFAULT),
       "Error creating file %s from image", filename);

  cleanup:
    return f;
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Create frame from image buffer and header
   @param    filename          to save to
   @param    image             data to save
   @param    header            FITS header
   @return   newly allocated frame
*/
/*---------------------------------------------------------------------------*/
cpl_frame *
flames_new_frame(const char *filename, 
         const cpl_image *image, 
         const uves_propertylist *header)
{
    cpl_frame *f = NULL;

    f = cpl_frame_new();
    cpl_frame_set_filename(f, filename);
    cpl_frame_set_type(f, CPL_FRAME_TYPE_IMAGE);

    check( uves_save_image(image, filename, header,true, true),
       "Error creating file %s from image", filename);

  cleanup:
    return f;
}


/*---------------------------------------------------------------------------*/
/**
   @brief    Create frame from image buffer and header
   @param    filename          to save to
   @param    imagelist             data to save
   @param    header            FITS header
   @return   newly allocated frame
*/
/*---------------------------------------------------------------------------*/
cpl_frame *
flames_new_framelist(const char *filename, 
         const cpl_imagelist *imagelist, 
         const uves_propertylist *header)
{
    cpl_frame *f = NULL;

    f = cpl_frame_new();
    cpl_frame_set_filename(f, filename);
    cpl_frame_set_type(f, CPL_FRAME_TYPE_IMAGE);

    check( uves_save_imagelist(imagelist, filename, header),
       "Error creating file %s from imagelist", filename);

  cleanup:
    return f;
}


/*---------------------------------------------------------------------------*/
/**
   @brief    Create frame from image and headers
   @param    filename          new filename or prefix
   @param    f                 frame to duplicate
   @param    prefix            whether the first parameter is the new filename
                               (false) or a prefix to attach (true)
   @param    reset_crval       if true, the frame is not just duplicated, but
                               CRVAL and CRPIX are set to one as in SPLIT/UVES
                               
   @return   newly allocated frame
*/
/*---------------------------------------------------------------------------*/
cpl_frame *
flames_image_duplicate(const char *filename_prefix,
                       const cpl_frame *frame,
                       bool prefix,
                       bool reset_crval)
{
    cpl_frame *f = NULL;
    cpl_image *image = NULL;
    uves_propertylist *header = NULL;

    const char *filename = cpl_frame_get_filename(frame);
    const char *new_filename = prefix ?
        uves_sprintf("%s%s", filename_prefix, filename) :
        uves_sprintf("%s", filename_prefix);

    int plane = 0;
    int extension = 0;

    check( image = uves_load_image(frame, plane, extension, &header),
       "Could not load image");

    if (reset_crval) {
        check_nomsg( flames_reset_crval_to_one(&header));
    }

    f = cpl_frame_new();
    cpl_frame_set_filename(f, new_filename);
    cpl_frame_set_type(f, CPL_FRAME_TYPE_IMAGE);
    cpl_frame_set_tag(f, cpl_frame_get_tag(frame));
    
    check( uves_save_image(image, new_filename, header,true, true),
       "Error creating file %s from image", new_filename);
    
  cleanup:
    uves_free_image(&image);
    uves_free_propertylist(&header);
    uves_free_string_const(&new_filename);
    return f;
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Get image median
   @param    f                 frame pointing to an image
   @return   CPL median
*/
/*---------------------------------------------------------------------------*/
double
flames_image_get_median(const cpl_frame *f)
{
    cpl_image *image = NULL;
    int plane = 0;
    int extension = 0;
    double result = 0;

    check( image = uves_load_image(f, plane, extension, NULL),
       "Could not load image");

    check( result = cpl_image_get_median(image),
       "Could not get median");

  cleanup:
    uves_free_image(&image);
    return result;
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Create frame from table and headers
   @param    filename_prefix   string to prefix to first filename
   @param    f1                frame to be subtracted
   @param    f2                frame to subtract
   @return   newly allocated frame
*/
/*---------------------------------------------------------------------------*/
cpl_frame *
flames_image_subtract_create(const char *filename_prefix,
                 const cpl_frame *f1,
                 const cpl_frame *f2)
{
    cpl_frame *f = NULL;
    cpl_image *image1 = NULL;
    cpl_image *image2 = NULL;
    uves_propertylist *header1 = NULL;
    const char *filename = cpl_frame_get_filename(f1);
    const char *new_filename = uves_sprintf("%s%s", filename_prefix,
                        filename);

    int plane = 0;
    int extension = 0;

    check( image1 = uves_load_image(f1, plane, extension, &header1),
       "Could not load image");

    check( image2 = uves_load_image(f2, plane, extension, NULL),
       "Could not load image");

    check( cpl_image_subtract(image1, image2),
       "Error subtracting images");

    f = cpl_frame_new();
    cpl_frame_set_filename(f, new_filename);
    cpl_frame_set_type(f, CPL_FRAME_TYPE_IMAGE);
    
    check( uves_save_image(image1, new_filename, header1,true, true),
       "Error creating file %s from image", new_filename);

  cleanup:
    uves_free_image(&image1);
    uves_free_image(&image2);
    uves_free_propertylist(&header1);
    uves_free_string_const(&new_filename);
    return f;
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Create frame from table and headers
   @param    filename_prefix   string to prefix to first filename
   @param    f1                frame to be subtracted
   @param    f2                frame to subtract
   @return   newly allocated frame
*/
/*---------------------------------------------------------------------------*/
cpl_frame *
flames_image_subtract_scalar_create(const char *filename_prefix,
                    const cpl_frame *f,
                    double value)
{
    cpl_frame *frame = NULL;
    cpl_image *image = NULL;
    uves_propertylist *header = NULL;
    const char *filename = cpl_frame_get_filename(f);
    const char *new_filename = uves_sprintf("%s%s", filename_prefix,
                        filename);

    int plane = 0;
    int extension = 0;

    check( image = uves_load_image(f, plane, extension, &header),
       "Could not load image");

    check( cpl_image_subtract_scalar(image, value),
       "Error subtracting images");

    frame = cpl_frame_new();
    cpl_frame_set_filename(frame, new_filename);
    cpl_frame_set_type(frame, CPL_FRAME_TYPE_IMAGE);
    
    check( uves_save_image(image, new_filename, header,true, true),
       "Error creating file %s from image", new_filename);

  cleanup:
    uves_free_image(&image);
    uves_free_propertylist(&header);
    uves_free_string_const(&new_filename);
    return frame;
}


/*---------------------------------------------------------------------------*/
/**
   @brief    Create frame from table and headers
   @param    filename          to save to
   @param    image             data to save
   @param    header            primary FITS header
   @param    theader            next FITS header
   @return   newly allocated frame
*/
/*---------------------------------------------------------------------------*/
cpl_frame *
flames_new_frame_table(const char *filename, 
               const cpl_table *table, 
               const uves_propertylist *header,
               const uves_propertylist *theader)
{
    cpl_frame *f = NULL;
    uves_propertylist *full_header;
    uves_propertylist *history;

    f = cpl_frame_new();
    cpl_frame_set_filename(f, filename);
    cpl_frame_set_type(f, CPL_FRAME_TYPE_TABLE);

    /* Copy all HISTORY keywords from extension header
       to primary header
    */
    check_nomsg( full_header = uves_propertylist_duplicate(header) );
    check_nomsg( history     = uves_propertylist_duplicate(theader) );
    check_nomsg( uves_propertylist_erase_regexp(history, "^HISTORY$", 1));
    check_nomsg( uves_propertylist_append(full_header, history));

    check( uves_table_save(table, full_header, full_header, filename, CPL_IO_DEFAULT),
       "Error creating file %s from table", filename);

  cleanup:
    uves_free_propertylist(&full_header);
    uves_free_propertylist(&history);
    return f;
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Concatenate tables
   @param    n1       first table filename
   @param    n2       second table filename

   The combined table is stored in the first filename using the header from
   the first table
*/
/*---------------------------------------------------------------------------*/
void
flames_merge_table(const char *n1, const char *n2)
{
    cpl_table *t1 = NULL;
    cpl_table *t2 = NULL;
    uves_propertylist *t1_header = NULL;

    check( t1 = cpl_table_load(n1, 1, 1),
           "Could not load table %s", n1);

    check( t1_header = uves_propertylist_load(n1, 0),
           "Could not load table %s header", n1);

    check( t2 = cpl_table_load(n2, 1, 1),
           "Could not load table %s", n2);
    
    check_nomsg(cpl_table_insert(t1, t2, 0));
    
    check( uves_table_save(t1, t1_header, NULL, n1, CPL_IO_DEFAULT),
            "Could not save table to %s", n1);

  cleanup:
    uves_free_table(&t1);
    uves_free_table(&t2);
    uves_free_propertylist(&t1_header);
    return;
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Rename table
   @param    n1       current name
   @param    n2       new name
*/
/*---------------------------------------------------------------------------*/
void
flames_rename_table(const char *n1, const char *n2)
{
    cpl_table *t = NULL;
    uves_propertylist *header = NULL;
    
    check( t = cpl_table_load(n1, 1, 1),
           "Could not load table %s", n1);
    check( header = uves_propertylist_load(n1, 0),
           "Could not load table %s header", n1);
    
    check( uves_table_save(t, header, NULL, n2, CPL_IO_DEFAULT),
           "Could not save table to %s", n2);
  cleanup:
    uves_free_table(&t);
    uves_free_propertylist(&header);
    return;           
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Sort table after 2 columns
   @param    filename table filename
   @param    c1       first column
   @param    c2       second column
*/
/*---------------------------------------------------------------------------*/
void
flames_sort_table_2(const char *filename, const char *c1, const char *c2)
{
    cpl_table *t = NULL;
    uves_propertylist *header = NULL;
    
    check( t = cpl_table_load(filename, 1, 1),
           "Could not load table %s", filename);
    check( header = uves_propertylist_load(filename, 0),
           "Could not load table %s header", filename);
    
    /* Sort ascending */
    check( uves_sort_table_2(t, c1, c2,
                             false, false),
           "Sorting table %s failed", filename);

    check( uves_table_save(t, header, NULL, filename, CPL_IO_DEFAULT),
           "Could not save table to %s", filename);
  cleanup:
    uves_free_table(&t);
    uves_free_propertylist(&header);
    return;           
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Select table rows
   @param    filename    table filename
   @param    column      a row is selected iff this column is non-NULL
*/
/*---------------------------------------------------------------------------*/
void
flames_select_non_null(const char *filename, const char *column)
{
    cpl_table *t = NULL;
    uves_propertylist *header = NULL;
    int i;
    
    check( t = cpl_table_load(filename, 1, 1),
           "Could not load table %s", filename);
    check( header = uves_propertylist_load(filename, 0),
           "Could not load table %s header", filename);
    
    assure_nomsg( cpl_table_has_column(t, "Select"), CPL_ERROR_DATA_NOT_FOUND );
    assure_nomsg( cpl_table_has_column(t, column), CPL_ERROR_DATA_NOT_FOUND);

    for (i = 0; i < cpl_table_get_nrow(t); i++)
        {
            if (cpl_table_is_valid(t, column, i)) {
                cpl_table_set_int(t, "Select", i, 1);
            }
            else {
                cpl_table_set_int(t, "Select", i, 0);
            }
        }

    check( uves_table_save(t, header, NULL, filename, CPL_IO_DEFAULT),
           "Could not save table to %s", filename);

  cleanup:
    uves_free_table(&t);
    uves_free_propertylist(&header);
    return;           
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Select all table rows
   @param    filename    table filename
*/
/*---------------------------------------------------------------------------*/
void
flames_select_all(const char *filename)
{
    cpl_table *t = NULL;
    uves_propertylist *header = NULL;
    int i;
    
    check( t = cpl_table_load(filename, 1, 1),
           "Could not load table %s", filename);
    check( header = uves_propertylist_load(filename, 0),
           "Could not load table %s header", filename);
    
    assure_nomsg( cpl_table_has_column(t, "Select"), CPL_ERROR_DATA_NOT_FOUND );

    for (i = 0; i < cpl_table_get_nrow(t); i++)
        {
            cpl_table_set_int(t, "Select", i, 1);
        }
    
    check( uves_table_save(t, header, NULL, filename, CPL_IO_DEFAULT),
           "Could not save table to %s", filename);
    
  cleanup:
    uves_free_table(&t);
    uves_free_propertylist(&header);
    return;           
}

/*---------------------------------------------------------------------------*/
/**
  @brief    Set crval keyword to 1
  @param    h    header to write to
**/
/*---------------------------------------------------------------------------*/
void
flames_reset_crval_to_one(uves_propertylist** h)
{
    /* FLAMES MIDAS C-code wants the START (CRVAL) to be 1 */
    {
        double crval_old, shift;
        check_nomsg( crval_old = uves_pfits_get_crval1(*h) );
        uves_msg_debug("Old crval1=%f",crval_old);
        
        shift = 1 - crval_old;
        
        check_nomsg( uves_pfits_set_crval1(*h,uves_pfits_get_crval1(*h) + shift) );
        check_nomsg( uves_pfits_set_crpix1(*h,uves_pfits_get_crpix1(*h) + shift) );
        
        check_nomsg( crval_old = uves_pfits_get_crval2(*h) );
        uves_msg_debug("Old crval2=%f",crval_old);
        shift = 1 - crval_old;
        check_nomsg( uves_pfits_set_crval2(*h,uves_pfits_get_crval2(*h) + shift) );
        check_nomsg( uves_pfits_set_crpix2(*h,uves_pfits_get_crpix2(*h) + shift) );
    }
    
    /* Also force REFNPIX (CRPIX) to one */
    check_nomsg( uves_pfits_set_crpix1(*h, 1) );
    check_nomsg( uves_pfits_set_crpix2(*h, 1) );
        
  cleanup:
    return;
}

/*---------------------------------------------------------------------------*/
/**
   @brief    Sort table after 3 columns
   @param    filename table filename
   @param    c1       first column
   @param    c2       second column
   @param    c3       third column
*/
/*---------------------------------------------------------------------------*/
void
flames_sort_table(const char *filename, const char *c1, const char *c2, const char *c3)
{
    cpl_table *t = NULL;
    uves_propertylist *header = NULL;
    
    check( t = cpl_table_load(filename, 1, 1),
           "Could not load table %s", filename);
    check( header = uves_propertylist_load(filename, 0),
           "Could not load table %s header", filename);
    
    /* Sort ascending */
    check( uves_sort_table_3(t, c1, c2, c3,
                             false, false, false),
           "Sorting table %s failed", filename);

    check( uves_table_save(t, header, NULL, filename, CPL_IO_DEFAULT),
           "Could not save table to %s", filename);
  cleanup:
    uves_free_table(&t);
    uves_free_propertylist(&header);
    return;           
}


/*---------------------------------------------------------------------------*/
/**
   @brief    Identify the instrument setting
   @param    frame             reads from primary FITS header
   @param    chip              CCD chip
   @param    wc                (output) central wavelength (nm)
   @return   instrument conf identifier as a string, dynamically allocated

!.OUTPUT        string identifying: wcen, SimCal/OzPoz setting, 
!               Plate No., ccd id, binning

*/
/*---------------------------------------------------------------------------*/
const char *
flames_get_frmid(const cpl_frame *frame, enum uves_chip chip,
         int *wc)
{
//define/parameter p1 ? ima "Enter frame name"
//define/local in_ima/c/1/80 {p1}
//define/local frmid/c/1/80 " " all
//define/local wc/c/1/3 " " all
    const char *fid = NULL;        /* Result */

//!define/local catal/i/1/1 0
//config/instr {in_ima}
//@p flames_get_frmid,set_fid {in_ima}
//frmid = q1
//wc = q2
//return {frmid} {wc}

//!==========================================================================
//entry set_fid
//define/parameter p1 ? ima "Enter frame name"
//define/local in_ima/c/1/80 {p1}

//define/local det/c/1/80 " " all
//define/local bin/c/1/80 " " all
//define/local mod/c/1/80 " " all
//define/local plt/c/1/1  " " all
//define/local modext/c/1/25 " " all
    const char *det;
    int bin1, bin2;
    const char *mod;
    const char *mod_dynamic = NULL;
    int plt;
//    const char *modext;

    int wcenter;
    int idx;
    const char *dprtype;
    uves_propertylist *header = NULL;

    check( header = uves_propertylist_load(cpl_frame_get_filename(frame), 0),
       "Could not load header from %s", cpl_frame_get_filename(frame));

//define/local wc/c/1/3 " " all
    assure_nomsg( wc != NULL, CPL_ERROR_NULL_INPUT );

//modext = q2
    /* Not used */

//det = "{q2(2:2)}{CCD}"
//det = m$lower(det(1:2))

/* Not sure what q2 is, but 'det' ends being re or rm */
    det = uves_chip_get_det(chip);


    check( wcenter = uves_pfits_get_gratwlen(header, chip),
       "Error reading grating central wavelength");

//outputi = wcenter / 10
/* Conversion -> nm */

//set/format I1
//wc = "{outputi}"
    *wc = uves_round_double(wcenter);
//set/format F1.0
    
//mod = "{{in_ima},{h_insmode}}"
    check( mod = uves_pfits_get_insmode(header),
       "Error reading instrument mode");


//if m$existd("{p1}","{h_plateid}") .eq. 1 plt = "{{in_ima},{h_plateid}}"
//if m$existd("{p1}","{h_newplateid}") .eq. 1 plt = "{{in_ima},{h_newplateid}}"
    check( plt = uves_flames_pfits_get_plateid(header),
       "Error reading plate ID");

//outputi(1) = m$strlen(mod)
//outputc = mod({outputi(1)}:{outputi(1)})
//if m$tstno(outputc) .eq. 1 then    !it's a number

    idx = strlen(mod) - 1;
    while(idx >= 0 && mod[idx] == ' ') idx--;   /* finds last non-blank character */

    assure( idx >= 0, CPL_ERROR_ILLEGAL_INPUT,
        "Illegal instrument mode string: '%s'", mod);
    
    if (isdigit(mod[idx]))
    {
        //mod = "d{outputc(1:1)}"
        mod_dynamic = uves_sprintf("d%c", mod[idx]);
    }
    else
    {
//        if mod .eq. "FIBRE" then
        if (strstr(mod, "FIBRE") != NULL)
        {
//  define/local type/c/1/80 " " all
//  type = "{{in_ima},{h_dprtype}}" 
            check( dprtype = uves_pfits_get_dpr_type(header),
               "Error reading DPR TYPE");
//                  outputi = m$index(type,"OzPoz")
//                  if m$index(type,"OzPoz") .gt. 0 then
            if (strstr(dprtype, "OzPoz") != NULL)
            {
//                      mod = "o{plt}"
                mod_dynamic = uves_sprintf("o%d", plt);                
            }
            else
            {
//            mod = "s{plt}"
                mod_dynamic = uves_sprintf("s%d", plt);
            }
        }
        else
        {
//            mod = "d0"
            mod_dynamic = uves_sprintf("d0");
        }
    }

//! The reversal of bin(2)xbin(1) serves for the 
//! naming convention before data rotation

//bin = "{CCDBIN(2)}x{CCDBIN(1)}"
//fid = "{wc}{mod}{det}{bin}"

    check( bin2 = uves_pfits_get_biny(header),
       "Error reading binning");
    check( bin1 = uves_pfits_get_binx(header),
       "Error reading binning");
    
    fid = uves_sprintf("%d%s%s%dx%d", *wc, mod_dynamic, det, bin2, bin1);
    
//return {fid} {wc}
  cleanup:
    uves_free_propertylist(&header);
    uves_free_string_const(&mod_dynamic);
    return fid;
}
/**@}*/
