/*                                                                            *
 *   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-10-04 12:36:39 $
 * $Revision: 1.173 $
 * $Name: not supported by cvs2svn $
 */

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

/*----------------------------------------------------------------------------*/
/**
 * @defgroup create_master    Create Master Files (xsh_create_master_dark/bias/flat)
 * @ingroup drl_functions
 *
 * 
 *
 */
/*----------------------------------------------------------------------------*/
/**@{*/

/*-----------------------------------------------------------------------------
  Includes
  -----------------------------------------------------------------------------*/
#include <xsh_drl.h>
#include <xsh_dfs.h>
//#include <xsh_pfits.h>
#include <xsh_pfits_qc.h>
#include <xsh_utils_wrappers.h>
#include <xsh_utils_imagelist.h>
#include <xsh_pfits.h>
#include <xsh_pfits_qc.h>
#include <xsh_error.h>
#include <xsh_msg.h>
#include <xsh_badpixelmap.h>
#include <xsh_paf_save.h>
#include <xsh_utils_image.h>
#include <xsh_detmon.h>  
#include <irplib_utils.h>  //For skip_if
#include <xsh_utils_image.h> 
#include <xsh_irplib_mkmaster.h>
#include <xsh_data_order.h>
#include <math.h>

/*-----------------------------------------------------------------------------
  Typedefs
  ---------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
  Defines
  ---------------------------------------------------------------------------*/

#define RECIPE_ID "xsh_mbias"
#define HIST_FACT 2.354820045
/*-----------------------------------------------------------------------------
  Functions prototypes
  -----------------------------------------------------------------------------*/

static cpl_error_code
xsh_compute_ron_mbias(cpl_frameset* raws,
		      xsh_pre* master,
		      cpl_parameterlist* parameters);


static cpl_error_code
xsh_compute_fpn_mbias(cpl_frameset* raws,
		      xsh_pre* master,
		      xsh_instrument* instrument,
		      cpl_parameterlist* parameters);

static cpl_error_code
xsh_mbias_get_fpn(const cpl_image* ima,
                  cpl_parameterlist* parameters,
                  double* fpn);


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


static double
xsh_mdark_get_contam(xsh_pre * pre,cpl_parameterlist* params,
xsh_instrument* instr)
{

   double contam=0;

   int llx_on=0;
   int lly_on=0;
   int urx_on=0;
   int ury_on=0;

   int llx_off=0;
   int lly_off=0;
   int urx_off=0;
   int ury_off=0;
   if(xsh_parameters_find(params,"xsh_mdark","contam_on_llx")!=NULL){
      check(llx_on=xsh_parameters_get_int(params,"xsh_mdark","contam_on_llx"));
      check(lly_on=xsh_parameters_get_int(params,"xsh_mdark","contam_on_lly"));
      check(urx_on=xsh_parameters_get_int(params,"xsh_mdark","contam_on_urx"));
      check(ury_on=xsh_parameters_get_int(params,"xsh_mdark","contam_on_ury"));


      check(llx_off=xsh_parameters_get_int(params,"xsh_mdark","contam_off_llx"));
      check(lly_off=xsh_parameters_get_int(params,"xsh_mdark","contam_off_lly"));
      check(urx_off=xsh_parameters_get_int(params,"xsh_mdark","contam_off_urx"));
      check(ury_off=xsh_parameters_get_int(params,"xsh_mdark","contam_off_ury"));

   } else {
      if(xsh_instrument_get_arm(instr) == XSH_ARM_UVB) {
         llx_on =801; lly_on =1058;urx_on =810; ury_on =1067;
         llx_off=855; lly_off=1066;urx_off=864; ury_off=1075;
      } else if(xsh_instrument_get_arm(instr) == XSH_ARM_VIS) {
         llx_on =1672; lly_on =2672;urx_on =1681;ury_on =2681;
         llx_off=1926; lly_off=2941;urx_off=1935;ury_off=2950;
      } else if(xsh_instrument_get_arm(instr) == XSH_ARM_NIR) {
         llx_on =991; lly_on =1083;urx_on =1015;ury_on =1108;
         llx_off=1029;lly_off=1075;urx_off=1053;ury_off=1110;
      } else if(xsh_instrument_get_arm(instr) == XSH_ARM_AGC) {
         llx_on =25; lly_on =10;urx_on =550;ury_on =520;
         llx_off=25;lly_off=10;urx_off=550;ury_off=520;
      }
   }

   llx_on=llx_on/pre->binx;
   lly_on=lly_on/pre->biny;
   urx_on=urx_on/pre->binx;
   ury_on=ury_on/pre->biny;

   llx_off=llx_off/pre->binx;
   lly_off=lly_off/pre->biny;
   urx_off=urx_off/pre->binx;
   ury_off=ury_off/pre->biny;

   check(contam=
         cpl_image_get_median_window(pre->data,llx_on,lly_on,urx_on,ury_on)-
         cpl_image_get_median_window(pre->data,llx_off,lly_off,urx_off,ury_off));


cleanup:

 
  return contam;


} 


/** get master mean and stdev over a user defined region


 */
cpl_error_code
xsh_mdark_get_median_stdev(xsh_pre* preFrame,cpl_parameterlist* parameters,
			   cpl_frame* crh_frm, cpl_frame* bpm_frm)
{ 

  int sx=0;
  int sy=0;
  int ref_llx=-1;
  int ref_lly=-1;
  int ref_urx=-1;
  int ref_ury=-1;
  xsh_pre* dup_frm=NULL;
  const char* crh_name=NULL;
  const char* bpm_name=NULL;
  cpl_image* crh_ima=NULL;
  cpl_image* bpm_ima=NULL;
  int* pcrh=NULL;
  int* pbpm=NULL;
  int* pqua=NULL;
  int i=0;
  bool bpm_found=false;
  bool crh_found=false;
  double avg_value = 0.0;
  double median_value = 0.0;
  double stdev = 0.0;

  check(ref_llx=xsh_parameters_get_int(parameters,"xsh_mdark","ref1_llx"));
  check(ref_lly=xsh_parameters_get_int(parameters,"xsh_mdark","ref1_lly"));
  check(ref_urx=xsh_parameters_get_int(parameters,"xsh_mdark","ref1_urx"));
  check(ref_ury=xsh_parameters_get_int(parameters,"xsh_mdark","ref1_ury"));
  sx=cpl_image_get_size_x(preFrame->data);
  sy=cpl_image_get_size_y(preFrame->data);

  /* QC parameters */

  if(ref_llx == -1) ref_llx=1;
  if(ref_lly == -1) ref_lly=1;  
  if(ref_urx == -1) ref_urx=sx;
  if(ref_ury == -1) ref_ury=sy;

  ref_llx = (ref_llx>0) ? ref_llx : 1;
  ref_lly = (ref_lly>0) ? ref_lly : 1;
  ref_urx = (ref_urx<sx) ? ref_urx : sx;
  ref_ury = (ref_ury<sy) ? ref_ury : sy;

  /*
    Median and avg should ignore the bad pixels including the Cosmics.
    But at this point the preframe->qual DOES NOT include the COSMIC RAYS !
    (see xsh_remove_crh_multi function in xsh_remove_crh_multi.c)
    Thus we flag CRHs and BP.
  */

  dup_frm=xsh_pre_duplicate(preFrame);

  if(crh_frm!=NULL) {
    check(crh_name=cpl_frame_get_filename(crh_frm));
    check(crh_ima=cpl_image_load(crh_name,XSH_PRE_QUAL_TYPE,0,0));
    check(pcrh=cpl_image_get_data_int(crh_ima));
    crh_found=true;
  }

  if(bpm_frm != NULL) {
    check(bpm_name=cpl_frame_get_filename(bpm_frm));
    check(bpm_ima=cpl_image_load(bpm_name,XSH_PRE_QUAL_TYPE,0,0));
    check(pbpm=cpl_image_get_data_int(bpm_ima));
    bpm_found=true;
  }

  if (bpm_found || crh_found) {
    int sx_sy=0;
    check(pqua=cpl_image_get_data_int( preFrame->qual));
    sx = preFrame->nx;
    sy = preFrame->ny;
    sx_sy=sx*sy;
    if (bpm_found && crh_found) {
      for (i = 0; i < sx_sy; i++) {
        if (pcrh[i] != 0 || pbpm[i] != 0) {
          pqua[i] = XSH_GOOD_PIXEL_LEVEL;
        }
      }
    } else if (bpm_found) {
      for (i = 0; i < sx_sy; i++) {
         if (pbpm[i] != 0) {
          pqua[i] = XSH_GOOD_PIXEL_LEVEL;
         }
      }
    } else if (crh_found) {
      for (i = 0; i < sx_sy; i++) {
         if (pcrh[i] != 0) {
          pqua[i] = XSH_GOOD_PIXEL_LEVEL;
         }
      }
    }
  }
  xsh_free_image(&bpm_ima);
  xsh_free_image(&crh_ima);
  xsh_pre_free(&dup_frm);

  check( xsh_pre_median_mean_stdev_window( preFrame, 
                                           &avg_value, &median_value,&stdev,
                                           ref_llx,ref_lly,ref_urx,ref_ury) ) ;

  xsh_pfits_set_qc_mdarkavg( preFrame->data_header,avg_value) ;
  xsh_pfits_set_qc_mdarkmed( preFrame->data_header,median_value) ;
  xsh_pfits_set_qc_mdarkrms( preFrame->data_header,stdev) ;

 cleanup:
  xsh_free_image(&bpm_ima);
  xsh_free_image(&crh_ima);
  xsh_pre_free(&dup_frm);
  return cpl_error_get_code();
}

static cpl_error_code
xsh_mdark_measure_fpn(xsh_pre* preFrame,cpl_parameterlist* parameters,xsh_instrument* instrument)
{ 

  /* fpn computation (DRS mode) */
  cpl_size zone_fpn[4];
  int fpn_llx=-1;
  int fpn_lly=-1;
  int fpn_urx=-1;
  int fpn_ury=-1;
  int fpn_nsamp=100;
  int fpn_hsize=4;
  double exptime=preFrame->exptime;
  double qc_fpn_val=0;
  double qc_fpn_err=0;
  int sx=0;
  int sy=0;
  double median=0;

  check(fpn_llx=xsh_parameters_get_int(parameters,"xsh_mdark","fpn_llx"));
  check(fpn_lly=xsh_parameters_get_int(parameters,"xsh_mdark","fpn_lly"));
  check(fpn_urx=xsh_parameters_get_int(parameters,"xsh_mdark","fpn_urx"));
  check(fpn_ury=xsh_parameters_get_int(parameters,"xsh_mdark","fpn_ury"));
  check(fpn_nsamp=xsh_parameters_get_int(parameters,"xsh_mdark","fpn_nsamples"));
  check(fpn_hsize=xsh_parameters_get_int(parameters,"xsh_mdark","fpn_hsize"));
  //xsh_msg("fpn=%d %d %d %d",fpn_llx,fpn_lly,fpn_urx,fpn_ury);
  sx=cpl_image_get_size_x(preFrame->data);
  sy=cpl_image_get_size_y(preFrame->data);

  /* QC parameters */

  if(fpn_llx == -1) fpn_llx=1;
  if(fpn_lly == -1) fpn_lly=1;  
  if(fpn_urx == -1) fpn_urx=sx;
  if(fpn_ury == -1) fpn_ury=sy;

  fpn_llx = (fpn_llx>0) ? fpn_llx : 1;
  fpn_lly = (fpn_lly>0) ? fpn_lly : 1;
  fpn_urx = (fpn_urx<sx) ? fpn_urx : sx;
  fpn_ury = (fpn_ury<sy) ? fpn_ury : sy;

  assure(fpn_urx>fpn_llx,CPL_ERROR_ILLEGAL_INPUT,
         "Must be fpn_urx (%d) >  fpn_llx (%d)",
         fpn_urx,fpn_llx);

  assure(fpn_ury>fpn_lly,CPL_ERROR_ILLEGAL_INPUT,
         "Must be fpn_ury (%d) >  fpn_lly (%d)",
         fpn_ury,fpn_lly);

  zone_fpn[0]=fpn_llx;
  zone_fpn[1]=fpn_urx;
  zone_fpn[2]=fpn_lly;
  zone_fpn[3]=fpn_ury;
  xsh_image_flag_bp(preFrame->data,preFrame->qual,instrument);

  //xsh_msg("fpn=%d %d %d %d hsize=%d nsamp=%d",fpn_llx,fpn_urx,fpn_lly,fpn_ury,
  //	  fpn_hsize,fpn_nsamp);
  check_msg(cpl_flux_get_noise_window(preFrame->data, zone_fpn, fpn_hsize,
              fpn_nsamp, &qc_fpn_val, &qc_fpn_err),
      "Error computing noise in a window");
   
  if ( instrument->arm != XSH_ARM_NIR ) {
        median=cpl_image_get_median_window(preFrame->data,fpn_llx,fpn_lly,fpn_urx,fpn_ury);
        qc_fpn_val /= median;
        qc_fpn_err /= median;
        /*
        xsh_msg("DRS norm fpn=%g",qc_fpn_val); 
        xsh_msg("DRS norm err fpn=%g",qc_fpn_err); 
	*/
        xsh_pfits_set_qc_norm_fpn(preFrame->data_header,qc_fpn_val);
        xsh_pfits_set_qc_norm_fpn_err(preFrame->data_header,qc_fpn_err);
        /* xsh_msg("DRS norm fpn=%g",qc_fpn_val); */

        qc_fpn_val *= exptime;
        qc_fpn_err *= exptime;

        xsh_pfits_set_qc_fpn( preFrame->data_header,qc_fpn_val);
        xsh_pfits_set_qc_fpn_err(preFrame->data_header,qc_fpn_err);

      } else {
         median=cpl_image_get_median_window(preFrame->data,fpn_llx,fpn_lly,fpn_urx,fpn_ury);

        xsh_pfits_set_qc_fpn( preFrame->data_header,qc_fpn_val);
        xsh_pfits_set_qc_fpn_err(preFrame->data_header,qc_fpn_err);
	/*
        xsh_msg("DRS norm fpn=%g",qc_fpn_val); 
        xsh_msg("DRS norm err fpn=%g",qc_fpn_err); 
	*/
        qc_fpn_val /= median;
        qc_fpn_err /= median;

        xsh_pfits_set_qc_norm_fpn(preFrame->data_header,qc_fpn_val);
        xsh_pfits_set_qc_norm_fpn_err(preFrame->data_header,qc_fpn_err);
      }
  //abort();
  /* xsh_msg("DRS fpn=%g",qc_fpn_val); */

 cleanup:
  return cpl_error_get_code();
}


/* ron region (not used) */
cpl_error_code
xsh_mdark_measure_ron(xsh_pre* preFrame,cpl_parameterlist* parameters)
{ 

  int ron_llx=-1;
  int ron_lly=-1;
  int ron_urx=-1;
  int ron_ury=-1;
  //int ron_nsamp=100;
  //int ron_hsize=4;
  int sx=0;
  int sy=0;

  check(ron_llx=xsh_parameters_get_int(parameters,"xsh_mdark","ron_llx"));
  check(ron_lly=xsh_parameters_get_int(parameters,"xsh_mdark","ron_lly"));
  check(ron_urx=xsh_parameters_get_int(parameters,"xsh_mdark","ron_urx"));
  check(ron_ury=xsh_parameters_get_int(parameters,"xsh_mdark","ron_ury"));
  //check(ron_hsize=xsh_parameters_get_int(parameters,"xsh_mdark","ron_hsize"));
  //check(ron_nsamp=xsh_parameters_get_int(parameters,"xsh_mdark","ron_nsamples"));
  sx=cpl_image_get_size_x(preFrame->data);
  sy=cpl_image_get_size_y(preFrame->data);

  /* QC parameters */
  if(ron_llx == -1) ron_llx=1;
  if(ron_lly == -1) ron_lly=1;  
  if(ron_urx == -1) ron_urx=sx;
  if(ron_ury == -1) ron_ury=sy;

  ron_llx = (ron_llx>0) ? ron_llx : 1;
  ron_lly = (ron_lly>0) ? ron_lly : 1;
  ron_urx = (ron_urx<sx) ? ron_urx : sx;
  ron_ury = (ron_ury<sy) ? ron_ury : sy;

  cleanup:
  return cpl_error_get_code();
}

/** 
 * Calculates and sets QC parameters: Median, Average, Stdev and Slope, for
 * primary image AND qual.
 * 
 * @param preFrame Master Dark Frame (PRE format)
 * @param instrument Pointer to the instrument description structure
 * @param parameters parameters of recipe
 * @param crh_frm CRH frame
 * @param bpm_frm BPM frame 
 */
static void
set_masterdark_qc (xsh_pre * preFrame, 
                   xsh_instrument * instrument,
                   cpl_parameterlist* parameters,
                   cpl_frame* crh_frm, cpl_frame* bpm_frm)
{
  int nx =0, ny = 0;

  /* xsh_msg("set_masterdark_qc"); */
  nx = xsh_pre_get_nx (preFrame);
  assure (nx != 0, cpl_error_get_code (), "Cant get X size");
  ny = xsh_pre_get_ny (preFrame);
  assure (ny != 0, cpl_error_get_code (), "Cant get Y size");

  check(xsh_mdark_get_median_stdev(preFrame,parameters,crh_frm,bpm_frm));

  if ( instrument->arm != XSH_ARM_AGC ) {
    check(xsh_mdark_measure_fpn(preFrame,parameters,instrument));
  }

  { /* get contamination */
    double contam=0;
    check(contam=xsh_mdark_get_contam(preFrame,parameters,instrument));
    check(xsh_pfits_set_qc_contamination( preFrame->data_header,contam)) ;
  }
  /* not used
  check(xsh_mdark_measure_ron(preFrame,parameters));
  */

  /*
    Should calculate the "slope", but how ?

    double slope = 0.0 ;
    slope = xsh_calculate_slope( preFrame->data ) ;

    xsh_pfits_set_qc( preFrame->data_header, (void *)&slope,
                      XSH_QC_MDARKSLOPE, instrument ) ;
  */

 cleanup:

  return;
}


static cpl_error_code
xsh_mdark_get_ron(xsh_pre* pre,cpl_parameterlist* params,cpl_propertylist* qc_log,xsh_instrument* instr)
{

   int fpn_llx=0;
   int fpn_lly=0;
   int fpn_urx=0;
   int fpn_ury=0;
   cpl_size fpn_hsize=0;
   cpl_size fpn_nsamp=0;
   int sx=0;
   int sy=0;
   cpl_size zone[4];
   double qc_ron_val=0;
   double qc_ron_err=0;
   //double median=0;

   /* xsh_msg("xsh_mdark_get_fpn"); */

   XSH_ASSURE_NOT_NULL_MSG(pre, "Null input pre");
   XSH_ASSURE_NOT_NULL_MSG(params, "Null input parameters");
   XSH_ASSURE_NOT_NULL_MSG(qc_log, "Null input QC propertylist");
  
   sx=xsh_pre_get_nx(pre);
   sy=xsh_pre_get_ny(pre);


   check(fpn_llx=xsh_parameters_get_int(params,"xsh_mdark","fpn_llx"));
   check(fpn_lly=xsh_parameters_get_int(params,"xsh_mdark","fpn_lly"));
   check(fpn_urx=xsh_parameters_get_int(params,"xsh_mdark","fpn_urx"));
   check(fpn_ury=xsh_parameters_get_int(params,"xsh_mdark","fpn_ury"));
   check(fpn_hsize=xsh_parameters_get_int(params,"xsh_mdark","fpn_hsize"));
   check(fpn_nsamp=xsh_parameters_get_int(params,"xsh_mdark","fpn_nsamples"));


   /* QC parameters */

   if(fpn_llx == -1) fpn_llx=1;
   if(fpn_lly == -1) fpn_lly=1;  
   if(fpn_urx == -1) fpn_urx=sx;
   if(fpn_ury == -1) fpn_ury=sy;

   fpn_llx = (fpn_llx>0) ? fpn_llx : 1;
   fpn_lly = (fpn_lly>0) ? fpn_lly : 1;
   fpn_urx = (fpn_urx<sx) ? fpn_urx : sx;
   fpn_ury = (fpn_ury<sy) ? fpn_ury : sy;

   assure(fpn_urx>fpn_llx,CPL_ERROR_ILLEGAL_INPUT,
          "Must be fpn_urx (%d) >  fpn_llx (%d)",
          fpn_urx,fpn_llx);

   assure(fpn_ury>fpn_lly,CPL_ERROR_ILLEGAL_INPUT,
          "Must be fpn_ury (%d) >  fpn_lly (%d)",
          fpn_ury,fpn_lly);


   zone[0]=fpn_llx;
   zone[1]=fpn_urx;
   zone[2]=fpn_lly;
   zone[3]=fpn_ury;
  
   check(xsh_image_flag_bp(pre->data,pre->qual,instr));
   check_msg(cpl_flux_get_noise_window(pre->data, zone, fpn_hsize,fpn_nsamp, 
                                       &qc_ron_val, &qc_ron_err),
             "Error computing noise in a window");
   /*
   median=cpl_image_get_median_window(ima,fpn_llx,fpn_lly,fpn_urx,fpn_ury);
   median=xsh_pfits_get_qc_mdarkmed(qc_log);
   */
   /* xsh_msg("fpn=%g,%g",qc_fpn_val,qc_fpn_err); */
   double inv_sqrt2=1./sqrt(2);
   xsh_pfits_set_qc_ron(qc_log,qc_ron_val*inv_sqrt2);
   xsh_pfits_set_qc_ron_err(qc_log,qc_ron_err*inv_sqrt2) ;

   /* as we are using input raw frames difference to get FPN, the noise
      on the time normalized frame (master dark) has to be divided by 
      exptime */
/*
   qc_fpn_val /= exptime;
   qc_fpn_err /= exptime;
   qc_fpn_val /= median;
   qc_fpn_err /= median;
*/
   //xsh_pfits_set_qc_norm_fpn(qc_log,qc_fpn_val) ;
   //xsh_pfits_set_qc_norm_fpn_err(qc_log,qc_fpn_err) ;
   /* xsh_msg("normalized fpn=%g,%g",qc_fpn_val,qc_fpn_err); */

  cleanup:

   return cpl_error_get_code();

}


static cpl_error_code
xsh_mdark_compute_fpn(cpl_frameset* raws,cpl_parameterlist* params,
                      cpl_propertylist* qclog,xsh_instrument* instrument)
{

   int nraws=0;
   cpl_frame* frm=NULL;
   xsh_pre* pre1=NULL;
   xsh_pre* pre2=NULL;
   //double exptime=0;

   XSH_ASSURE_NOT_NULL_MSG(raws, "Null input raw frames set");
   XSH_ASSURE_NOT_NULL_MSG(params, "Null input parameter list");
   XSH_ASSURE_NOT_NULL_MSG(qclog, "Null input qc property list");

   nraws=cpl_frameset_get_size(raws);

   XSH_ASSURE_NOT_ILLEGAL_MSG(nraws >1,"Min 2 raw darks needed to compute FPN");

   frm=cpl_frameset_get_frame(raws,0);
   pre1=xsh_pre_load(frm,instrument);

   frm=cpl_frameset_get_frame(raws,1);
   pre2=xsh_pre_load(frm,instrument);

   xsh_pre_subtract(pre1,pre2);
   //exptime=xsh_pfits_get_exptime(pre1->data_header);
   check(xsh_mdark_get_ron(pre1,params,qclog,instrument));

  cleanup:
   xsh_pre_free(&pre1);
   xsh_pre_free(&pre2);

   return cpl_error_get_code();

}

/** 
 * Calculates master dark frame.
 * 
 * @param raws raw input frames (PRE format)
 * @param stack_param parameters to do frame stacking
 * @param instr Pointer to the instrument description structure
 * @param output data imagelist (to later compute QC).
 */
cpl_frame *
xsh_create_master_dark2(cpl_frameset * raws, 
                        xsh_stack_param* stack_param, 
                        cpl_parameterlist* params,
                        cpl_propertylist* qc_log,
                        xsh_instrument* instr)
{

  double mean_exptime=0;
  cpl_imagelist* raw_data=NULL;
  cpl_imagelist* raw_errs=NULL;
  cpl_imagelist* iml_errs=NULL;
  cpl_imagelist* iml_data=NULL;
  cpl_image* qual_comb=NULL;

  cpl_propertylist** raw_headers=NULL;
  cpl_frame* mdark=NULL;
  cpl_image* mbias=NULL; /* for xsh the bias has been already subtracted */
  xsh_pre* pre=NULL;

  cpl_frame* frm=NULL;
  char mdark_name[256];
  char mdark_tag[256];
  const double kappa=5.;
  const int nclip=5;
  const double tolerance=1.e-5;

  int sz=0;
  int i=0;
  int mode_and=0;

  /* prepare image list and list of headers */
  sz=cpl_frameset_get_size(raws);
  raw_data=cpl_imagelist_new();
  raw_errs=cpl_imagelist_new();
  raw_headers=cpl_calloc(sz,sizeof(cpl_propertylist*));

  for(i=0;i<sz;i++){
    check(frm=cpl_frameset_get_frame(raws,i));
    pre=xsh_pre_load(frm,instr);
    /* inject bad pixels to change statistics */
    check(xsh_image_flag_bp(pre->data,pre->qual,instr));
    check(xsh_image_flag_bp(pre->errs,pre->qual,instr));
    cpl_imagelist_set(raw_data,cpl_image_duplicate(pre->data),i);
    cpl_imagelist_set(raw_errs,cpl_image_duplicate(pre->errs),i);
    raw_headers[i]=cpl_propertylist_duplicate(pre->data_header);
    if(i==0) {
       qual_comb=cpl_image_duplicate(pre->qual);
    } else {
       xsh_badpixelmap_image_coadd(&qual_comb,pre->qual,mode_and);
    }
    /* All the input frames have the same bad pixel map, thus use the
       last one as the "final" one */
    if (i < (sz-1)) {
      xsh_pre_free(&pre);
    } 
  }

  iml_data=xsh_irplib_mkmaster_dark_fill_imagelist(raw_data,raw_headers,mbias,&mean_exptime);

  iml_errs=xsh_irplib_mkmaster_dark_fill_imagelist(raw_errs,raw_headers,mbias,&mean_exptime);

  /*free memory on product extensions that will be re-allocated later on 
    (errs is only modified) */
    xsh_free_image(&pre->data);
    xsh_free_image(&pre->qual);

  /* store combined bp map in qual extension of result */
  pre->qual=cpl_image_duplicate(qual_comb);

  /* Get median stack of input darks */
  if (strcmp(stack_param->stack_method, "median") == 0) {
    xsh_msg("Calculating stack median");

    pre->data=xsh_irplib_mkmaster_median(iml_data,kappa,nclip,tolerance);
    check( xsh_collapse_errs(pre->errs,iml_errs,0));

  } else {
    xsh_msg("Calculating stack mean");
    pre->data=xsh_irplib_mkmaster_mean(iml_data,kappa,nclip,tolerance,stack_param->klow,stack_param->khigh);

    check( xsh_collapse_errs(pre->errs,iml_errs,1));

  }


  /* If not NIR, Divide master data and errs by exposure time ansd rescale header EXPTIME,
   * else just correct header exposure time */
  if ( instr->arm != XSH_ARM_NIR ) {

    check_msg (cpl_image_divide_scalar (pre->data, mean_exptime),
          "Cant divide median image by exptime %lf", mean_exptime);
     check_msg (cpl_image_divide_scalar (pre->errs, mean_exptime),
          "Cant divide median image by exptime %lf", mean_exptime);

     /* Set the EXPTIME at 1. */
     xsh_msg_dbg_high ("Set EXPTIME = 1.");
     check(xsh_pfits_set_exptime (pre->data_header, (double) 1.));
     check(xsh_pfits_set_exptime (pre->errs_header, (double) 1.));

  } else {

     check(xsh_pfits_set_exptime(pre->data_header, mean_exptime ));
     check(xsh_pfits_set_exptime(pre->errs_header, mean_exptime ));

  }


  if(qc_log != NULL ) {
      if ( instr->arm != XSH_ARM_AGC ) {
          if(sz>1) {
              xsh_mdark_compute_fpn(raws,params,qc_log,instr);

          }
      }
  }  
  if(qc_log != NULL ) {
      cpl_propertylist_append(pre->data_header,qc_log);
  }


  sprintf(mdark_tag,"%s_%s",XSH_MASTER_DARK,xsh_instrument_arm_tostring(instr));
  sprintf(mdark_name,"%s.fits",mdark_tag);
  xsh_count_satpix(pre,instr,sz);
  check(mdark = xsh_pre_save( pre,mdark_name,mdark_tag,0));
  check(cpl_frame_set_tag(mdark,mdark_tag));

cleanup:
xsh_free_imagelist(&raw_data);
xsh_free_imagelist(&raw_errs);
xsh_free_imagelist(&iml_data);
xsh_free_imagelist(&iml_errs);
xsh_free_image(&qual_comb);
xsh_pre_free(&pre);

for(i=0;i<sz;i++){
  xsh_free_propertylist(&raw_headers[i]);
}
cpl_free(raw_headers);
return mdark;
}
/** 
 * PELIMINARY:
 * Creates a master dark frame from input frame. Input frame is in PRE
 * format: medianFrame, errsFrame, bpmapFrame.
 * Divide median frame by exposure time and calculate QC params.
 * 
 * @param medFrame Frame in PRE format
 * @param instr instrument containing the arm in use
 * @param parameters of the recipe
 * @param crh_frm CRH frame
 * @param bpm_frm BPM frame 
*/
cpl_frame *
xsh_create_master_dark (cpl_frame * medFrame, xsh_instrument* instr,
			cpl_parameterlist* parameters,
                        cpl_frame* crh_frm,cpl_frame* bpm_frm)
{
  /*
    Load frame into PRE 
    Modify KW, etc
    Save PRE and returns pointer to frame
  */
  xsh_pre *medPre = NULL;
  double exptime = 0.0;
  cpl_frame* resFrame = NULL;
  const  char* tag = XSH_GET_TAG_FROM_ARM (XSH_MASTER_DARK, instr);
  char* finalName = NULL;

  XSH_ASSURE_NOT_NULL(medFrame);
  XSH_ASSURE_NOT_NULL(instr);

  check(medPre = xsh_pre_load (medFrame,instr));
  XSH_ASSURE_NOT_NULL(medPre);

  exptime = medPre->exptime;

  /* If not NIR, Divide medFrameand medErrs by exposure time */
  if ( instr->arm != XSH_ARM_NIR ) {
    check_msg (cpl_image_divide_scalar (medPre->data, exptime),
	       "Cant divide median image by exptime %lf", exptime);
    check_msg (cpl_image_divide_scalar (medPre->errs, exptime),
	       "Cant divide median image by exptime %lf", exptime);

    /* Set the EXPTIME at 1. */
    xsh_msg_dbg_high ("Set EXPTIME = 1.");
    check(xsh_pfits_set_exptime (medPre->data_header, (double) 1.));
    check(xsh_pfits_set_exptime (medPre->errs_header, (double) 1.));
  }


 if ( xsh_instrument_get_arm(instr) == XSH_ARM_NIR ) {
    check(finalName =cpl_sprintf("%s_%s.fits",XSH_MASTER_DARK, 
				xsh_instrument_arm_tostring(instr)));
 } else {
   /* Sabines doesn't like to have the binning in the file-name
   check(binx=xsh_pfits_get_binx(medPre->data_header));
   check(biny=xsh_pfits_get_biny(medPre->data_header));
   check(finalName =cpl_sprintf("%s_%s_%dx%d.fits",XSH_MASTER_DARK, 
				xsh_instrument_arm_tostring(instr),
				binx,biny));
   */
    check(finalName =cpl_sprintf("%s_%s.fits",XSH_MASTER_DARK, 
				xsh_instrument_arm_tostring(instr)));
 }
  /* Calculate and Save QC Keywords */
  /* QC.MDARKMED
     QC.MDARKRMS
     QC.MDARKSLOGE (?)
  */
  check(set_masterdark_qc (medPre, instr,parameters,crh_frm,bpm_frm));

  /* Save PRE */
  check ( resFrame = xsh_pre_save (medPre, finalName, tag,0 ));
  
 assure (resFrame != NULL, cpl_error_get_code (), "Cant save PRE");
  xsh_msg_dbg_high ("PRE frame saved as file %s", finalName);

  check(cpl_frame_set_level (resFrame, CPL_FRAME_LEVEL_FINAL));
  check(cpl_frame_set_tag (resFrame, tag)); 

 cleanup:
  if (cpl_error_get_code() != CPL_ERROR_NONE){
    xsh_free_frame(&resFrame);
  }
  cpl_free(finalName);
  xsh_pre_free(&medPre);
  return resFrame;
}


/*---------------------------------------------------------------------------*/
/**
  @brief    Reject outlier pixels
  @param    image       image with pixels
  @param    min         reject pixels below this value
  @param    max         reject pixels abouve this value
 */
/*---------------------------------------------------------------------------*/
static void
reject_lo_hi(cpl_image *image, double min, double max)
{
  cpl_mask *mask_lo = NULL;
  cpl_mask *mask_hi = NULL;

  mask_lo = cpl_mask_threshold_image_create(image, -DBL_MAX, min);
  mask_hi = cpl_mask_threshold_image_create(image, max, DBL_MAX);
  assure_mem( mask_lo );
  assure_mem( mask_hi );

  cpl_mask_or(mask_lo, mask_hi);

  cpl_image_reject_from_mask(image, mask_lo);
  
  cleanup:
  xsh_free_mask(&mask_lo);
  xsh_free_mask(&mask_hi);
  return;
}

static int
count_good(const cpl_image *image)
{
    return 
        cpl_image_get_size_x(image) * cpl_image_get_size_y(image) - 
        cpl_image_count_rejected(image);
}

static double
get_masterbias_qc_structure_row_region(cpl_image * tima)
{

   cpl_image* avg_row=NULL;
   double min=0;
   double max=0;
   double struct_row=0;



  check(avg_row = cpl_image_collapse_create(tima,0));
  check(cpl_image_divide_scalar(avg_row,cpl_image_get_size_y(tima)));

  /* restricts statistics to +/- 2 ADU around mean */
  min = cpl_image_get_mean(avg_row) - 2;
  max = cpl_image_get_mean(avg_row) + 2;
 
  /* replace with MIDAS
  stat/ima avg_row + exc={min},{max};
  */
  check( reject_lo_hi(avg_row, min, max) );
  if (count_good(avg_row) >= 2)
      {
          check(struct_row = cpl_image_get_stdev(avg_row));
      }
  else
      {
          struct_row = -1;
          xsh_msg_warning("Only %d good pixels in image. Setting QC parameter to -1",
                           count_good(avg_row));
      }

  cleanup:
   xsh_free_image(&avg_row);
 

   return struct_row;

}




static double
get_masterbias_qc_structure_col_region(cpl_image * tima)
{


   cpl_image* avg_col=NULL;
   double min=0;
   double max=0;
   double struct_col=0;

   check(avg_col = cpl_image_collapse_create(tima,1));
   check(cpl_image_divide_scalar(avg_col,cpl_image_get_size_x(tima)));

   /* restricts statistics to +/- 2 ADU around mean */
   min = cpl_image_get_mean(avg_col) - 2;
   max = cpl_image_get_mean(avg_col) + 2; 

   /* replace with MIDAS
      stat/ima avg_col + exc={min},{max};
   */
   check( reject_lo_hi(avg_col, min, max) );
   if (count_good(avg_col) >= 2)
   {
      check(struct_col = cpl_image_get_stdev(avg_col));
   }
   else
   {
      struct_col = -1;
      xsh_msg_warning("Only %d good pixels in image. Setting QC parameter to -1",
                      count_good(avg_col));
   }

  cleanup:
   xsh_free_image(&avg_col);
 

   return struct_col;

}


/** 
 * Calculates and sets QC parameters: Median, Average, Stdev and Slope, for
 * primary image AND qual.
 * 
 * @param master Master Bias Frame (PRE format)
 * @param llx lower left X
 * @param lly lower left Y
 * @param urx upper right X
 * @param ury upper right Y
 * @param ref_x reference X
 * @param ref_y reference Y
 * @param reg_id region id
 * @param dlevel debug level
 * @return void
 */
static void
set_masterbias_qc_xsh_structure_region(xsh_pre * master,
                                   const int llx,
                                   const int lly,
                                   const int urx,
                                   const int ury,
                                   const int ref_x,
                                   const int ref_y,
                                   const int reg_id)
{
   /* UVES algorithm */
   cpl_image* xima=NULL;
   cpl_image* yima=NULL;
   double struct_row=0;
   double struct_col=0;

   double mean = 0.0;
   double median = 0.0;
   double stdev = 0.0;

   double structx = 0.0;
   double structy = 0.0;
   //int nb_images = 0;

   check( xsh_pre_median_mean_stdev_window( master, &mean, &median,&stdev,
                                            llx,lly,urx,ury) ) ;

   check(xsh_pfits_set_qc_mbiasavg (master->data_header, mean));
   check(xsh_pfits_set_qc_mbiasmed (master->data_header, median));
   check(xsh_pfits_set_qc_mbiasrms (master->data_header, stdev));



   if(reg_id==1){
      check(xima=cpl_image_extract(master->data,llx,lly,urx,ref_y));
   } else {
      check(xima=cpl_image_extract(master->data,llx,ref_y,urx,ury));
   }
   if (0) {
      /*
        replace/ima {mbia} {tmpfrm} 300,>=300.;
      */
      check(cpl_image_threshold(xima,
                                -DBL_MAX,median+3*stdev,
                                -DBL_MAX,median+3*stdev));
   }

   if(reg_id==1){
      check(yima=cpl_image_extract(master->data,llx,lly,ref_x,ury));
   } else {
      check(yima=cpl_image_extract(master->data,ref_x,lly,urx,ury));
   }
   if (0) {
      /*
        replace/ima {mbia} {tmpfrm} 300,>=300.;
      */
      check(cpl_image_threshold(yima,
                                -DBL_MAX,median+3*stdev,
                                -DBL_MAX,median+3*stdev));
   }

   check(struct_row=get_masterbias_qc_structure_row_region(xima));
   structx=struct_row;

   check(struct_col=get_masterbias_qc_structure_col_region(yima));
   structy=struct_col;

   /* end UVES based algorithm */
   if(reg_id==1) {
      check(xsh_pfits_set_qc_reg1_structx(master->data_header,structx));
      check(xsh_pfits_set_qc_reg1_structy(master->data_header,structy));
   } else {
      check(xsh_pfits_set_qc_reg2_structx(master->data_header,structx));
      check(xsh_pfits_set_qc_reg2_structy(master->data_header,structy));
   }

   //check( nb_images = xsh_pfits_get_datancom( master->data_header));
   //AMo: this formula is inaccurate. RON is computed anyway elsewhere
   //check( xsh_pfits_set_qc_ron( master->data_header, 
   //  stdev*master->conad*sqrt(nb_images)));


  cleanup:
   xsh_free_image(&xima);
   xsh_free_image(&yima);
   return;

}

/** 
 * Calculates and sets QC parameters: Median, Average, Stdev and Slope, for
 * primary image AND qual.
 * 
 * @param master Master Bias Frame (PRE format)
 * @param llx lower left X
 * @param lly lower left Y
 * @param urx upper right X
 * @param ury upper right Y
 * @param ref_x reference X
 * @param ref_y reference Y
 * @param reg_id region id
 * @return void
 */
static void
set_masterbias_qc_structure_region(xsh_pre * master,
                                   const int llx,
                                   const int lly,
                                   const int urx,
                                   const int ury,
                                   const int ref_x,
                                   const int ref_y,
                                   const int reg_id)
{


  check(set_masterbias_qc_xsh_structure_region(master,llx,lly,urx,ury,
					       ref_x,ref_y,reg_id));

  cleanup:
   return;

}


/** 
 * Calculates and sets QC parameters: Median, Average, Stdev and Slope, for
 * primary image AND qual.
 * 
 * @param master Master Bias Frame (PRE format)
 * @param parameters Recipe input parameters
 */
static void
set_masterbias_qc_structure(xsh_pre * master, cpl_parameterlist* parameters)
{

  cpl_parameter* p=NULL;
  int ref_x=-1;
  int ref_y=-1;

  int ref_llx=0;
  int ref_lly=0;
  int ref_urx=0;
  int ref_ury=0;

  int sx=0;
  int sy=0;
  //int dlevel=0;

  sx=cpl_image_get_size_x(master->data);
  sy=cpl_image_get_size_y(master->data);
  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.struct_refx"));
  check(ref_x = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.struct_refy"));
  check(ref_y = cpl_parameter_get_int(p));

  //check(dlevel=xsh_parameters_debug_level_get("xsh_mbias",parameters));

  ref_llx=1;ref_urx=sx;
  ref_lly=1;ref_ury=sy;

  if(ref_x == -1) {
     ref_x=sx/2;
  }

  if(ref_y == -1) {
     ref_y=sy/2;
  }

  ref_x = (ref_x>0) ? ref_x : 1;
  ref_y = (ref_y>0) ? ref_y : 1;
  ref_x = (ref_x<sx) ? ref_x : sx;
  ref_y = (ref_y<sy) ? ref_y : sy;

  set_masterbias_qc_structure_region(master,ref_llx,ref_lly,ref_urx,ref_ury,
				     ref_x,ref_y,1);

 
  set_masterbias_qc_structure_region(master,ref_llx,ref_lly,ref_urx,ref_ury,
                                     ref_x,ref_y,2);
  

  cleanup:
     return;

}

/*----------------------------------------------------------------------------*/


/*----------------------------------------------------------------------------*/

/**
 * @brief Creates master bias
 * @param rawFrames Pointer to the frameset
 * @param stack_par Pointer to stack param structure
 * @param instr instrument containing the arm , mode and lamp in use
 * @param result_tag product catg
 * @return the clean mean frame newly allocated
 */

cpl_frame*
xsh_create_master_bias2(cpl_frameset* rawFrames,xsh_stack_param* stack_par,
                        xsh_instrument* instr,const char* result_tag,
			const int method_code)
{
  cpl_frame* medFrame = NULL;
  cpl_imagelist *dataList = NULL;
  cpl_imagelist *errsList = NULL;
  cpl_image* qual_comb=NULL;
  int i=0;
  int size=0;
  xsh_pre* pre = NULL;
  cpl_frame *current = NULL;
  char result_name[256];
  const int mode_and=0;

  XSH_ASSURE_NOT_NULL( instr);
  XSH_ASSURE_NOT_NULL( rawFrames);
  check( size = cpl_frameset_get_size( rawFrames));

  XSH_ASSURE_NOT_ILLEGAL( size > 0);

  check( dataList = cpl_imagelist_new());
  check( errsList = cpl_imagelist_new());
  /* Populate image lists with images in frame set */
  for (i=0;i < size;i++){
    check(current = cpl_frameset_get_frame(rawFrames,i));
    /* Load pre file */
    check(pre = xsh_pre_load(current,instr));

    /* inject bad pixels to change statistics */
    xsh_image_flag_bp(pre->data,pre->qual,instr);
    xsh_image_flag_bp(pre->errs,pre->qual,instr);
    check_msg( cpl_imagelist_set(dataList,cpl_image_duplicate(pre->data),
      i),"Cant add Data Image %d to imagelist", i) ;
    check_msg( cpl_imagelist_set(errsList,cpl_image_duplicate(pre->errs),
      i),"Cant add Data Image %d to imagelist", i) ;

    /* combining bad pixels */
    if(i==0) {
       qual_comb=cpl_image_duplicate(pre->qual);
    } else {
       xsh_badpixelmap_image_coadd(&qual_comb, pre->qual,mode_and);
    }
    /* Free memory */
    if (i < (size-1)) {
      xsh_pre_free(&pre);
    }
  }

  /*free memory on product extensions that will be re-allocated later on 
    (errs is only modified) */
  xsh_free_image(&pre->data);
  xsh_free_image(&pre->qual);

  /* store combined bp map in qual extension of result */
  pre->qual=cpl_image_duplicate(qual_comb);

  xsh_free_image(&qual_comb);

  /* Verify uniformity of imagelists */
  assure (cpl_imagelist_is_uniform (dataList) == 0, CPL_ERROR_ILLEGAL_INPUT,
    "Data images are not uniform");
  assure (cpl_imagelist_is_uniform (errsList) == 0, CPL_ERROR_ILLEGAL_INPUT,
    "Errs images are not uniform");

  /* computes the median of data and errs */
  if(method_code==0) {

    pre->data=xsh_irplib_mkmaster_median(dataList,5.,5,1.e-5);
    check( xsh_collapse_errs(pre->errs,errsList,0));

  } else if(method_code==1) {
    check(pre->data=xsh_irplib_mkmaster_mean(dataList,5.,5,1.e-5,
				       stack_par->klow,
				       stack_par->khigh));
    //Making a clean mean instead of a median
    check( xsh_collapse_errs(pre->errs,errsList,1));

  }

  /* Add PRO KW */
  check( xsh_pfits_set_datancom( pre->data_header, size));

  /* Now save the relevant frame of PRE
     With: median image, errs image and bad pixel map
  */
  sprintf(result_name,"%s.fits",result_tag);
  check(medFrame = xsh_pre_save( pre, result_name, result_tag,0));

  check(cpl_frame_set_tag(medFrame, result_tag));


  cleanup:

    if (cpl_error_get_code () != CPL_ERROR_NONE) {
      xsh_free_frame(&medFrame);
    }
    xsh_free_imagelist(&dataList);

    xsh_pre_free(&pre);
    xsh_free_imagelist(&errsList) ;
    xsh_free_image(&qual_comb);


   return medFrame;

}

/** 
  @brief 
    Computes QC on a master bias frame
  @param raws 
    The raw frames
  @param frame 
    The master bias frame
  @param instr 
    The instrument containing the arm in use
  @param parameters
    The instrument recipe parameters (To be removed)
  @param params
    Additional params to compute QC

  @return
    The master bias frame with QC parameters and PROCATG

 */

cpl_frame *
xsh_compute_qc_on_master_bias (cpl_frameset* raws, 
                        cpl_frame * frame, 
                        xsh_instrument* instr,
                        cpl_parameterlist* params)
{
  xsh_pre *master = NULL;
  cpl_frame *masterFrame = NULL;
  const char* tag = XSH_GET_TAG_FROM_ARM( XSH_MASTER_BIAS, instr);
  char mastername[256];
  //int binx=0;
  //int biny=0;
  int nraws =0;

  XSH_ASSURE_NOT_NULL(frame);
  check(master = xsh_pre_load (frame,instr));
  /* set the PROCATG KW */
  check(xsh_pfits_set_pcatg (master->data_header, tag));
  
  //check(binx=xsh_pfits_get_binx(master->data_header));
  //check(biny=xsh_pfits_get_biny(master->data_header));

  if ( params != NULL ) {
     check( set_masterbias_qc_structure(master,params));
     if(cpl_frameset_get_size(raws) > 1 ) {
        check(xsh_compute_ron_mbias(raws,master,params));
        check(xsh_compute_fpn_mbias(raws,master,instr,params));
     }
  }

  sprintf(mastername,"MASTER_BIAS_%s.fits", 
			  xsh_instrument_arm_tostring (instr));

  nraws = cpl_frameset_get_size(raws);
  xsh_msg("nraws=%d",nraws);
  xsh_count_satpix(master, instr, nraws);

  xsh_msg ("Create master bias");

  check(masterFrame = xsh_pre_save (master, mastername,tag,0 ));
  cpl_frame_set_tag (masterFrame, tag);
  cpl_frame_set_group(masterFrame,CPL_FRAME_GROUP_CALIB);

  cleanup:
    xsh_pre_free(&master);
    return masterFrame;
}

cpl_frame *
xsh_create_master_flat_with_mask (cpl_frame *frame, cpl_frame* edges, xsh_instrument *instr)
{
  xsh_pre *master = NULL;
  cpl_frame *masterFrame = NULL;
  const char *tag = NULL;
  char *name = NULL;
  xsh_order_list *orderlist = NULL;
  int nx = 0;
  int ny = 0;
  int ord=0;
  int y=0;
  int x=0;
  int x1=0;
  int x2=0;
  int y1=0;
  int y2=0;
  int endy=0;
  int starty=0;
  //int* qual=NULL;
  //int decode_bp=0;
  cpl_image* ima=NULL;
  //float* pima=NULL;
  //float* pdata=NULL;
  //cpl_mask* mask=NULL;
  double mean=0;
  int hbox=20;

  // Check parameters 
  XSH_ASSURE_NOT_NULL( frame);
  XSH_ASSURE_NOT_NULL( edges);
  XSH_ASSURE_NOT_NULL( instr);
  if(xsh_instrument_get_lamp(instr) == XSH_LAMP_D2) {
    ord=22;
    ord=2;
  } else if(xsh_instrument_get_lamp(instr) == XSH_LAMP_QTH) {
    ord=16;
    ord=4;
  } else if (xsh_instrument_get_arm(instr) == XSH_ARM_VIS ) {
    ord=24;
    ord=6;
  } else if (xsh_instrument_get_arm(instr) == XSH_ARM_NIR ) {
    ord=18;
    ord=8;
  }
  //decode_bp=instr->decode_bp;
  check( master = xsh_pre_load( frame, instr));
  check( orderlist = xsh_order_list_load( edges, instr));

  nx = master->nx;
  ny = master->ny;
  //qual=cpl_image_get_data_int(xsh_pre_get_qual(master));
  ima=cpl_image_new(nx,ny,CPL_TYPE_FLOAT);
  xsh_image_flag_bp(ima,master->qual,instr);
  //mask=cpl_image_get_bpm( ima);
  //check(pima=cpl_image_get_data_float(ima));
  //check(pdata=cpl_image_get_data_float(master->data));
  xsh_msg("prepare mask");

  starty = orderlist->list[ord-1].starty;
  endy = orderlist->list[ord-1].endy;

  y=0.5*(starty+endy)/master->biny;
  check(x1 = xsh_order_list_eval(orderlist, orderlist->list[ord].edglopoly,y));
  check(x2 = xsh_order_list_eval(orderlist, orderlist->list[ord].edguppoly, y));
  x=0.5*(x1+x2);
  x1=x-hbox;
  x2=x+hbox;
  y1=y-hbox;
  y2=y+hbox;
  //xsh_msg("x1=%d x2=%d y1=%d y2=%d",x1,x2,y1,y2);

  check(mean=cpl_image_get_median_window(master->data,x1,y1,x2,y2));
  xsh_msg("scaling factor mean flat=%g",mean);
  XSH_NAME_LAMP_MODE_ARM( name, "MASK_FLAT",".fits",instr);
  //cpl_image_save(ima,name, XSH_PRE_DATA_BPP, NULL, CPL_IO_DEFAULT);
  xsh_free_image(&ima);

  // set the PROCATG KW 
  tag = XSH_GET_TAG_FROM_LAMP( XSH_MASTER_FLAT, instr);
  check( xsh_pfits_set_pcatg( master->data_header, tag));
  
  //check( xsh_pre_normalize( master));
  cpl_image_divide_scalar( master->data, mean);
  cpl_image_divide_scalar( master->errs, mean);

  XSH_FREE( name);
  XSH_NAME_LAMP_MODE_ARM( name, "MASTER_FLAT",".fits",instr);
  xsh_msg ("Create master flat %s tag %s", name, tag);
 
  check( masterFrame = xsh_pre_save( master, name, tag,0));
  check( cpl_frame_set_tag( masterFrame, tag));
  check( cpl_frame_set_group( masterFrame, CPL_FRAME_GROUP_CALIB));

  cleanup:
  xsh_order_list_free(&orderlist);
    XSH_FREE( name);
    xsh_pre_free( &master);
    return masterFrame;
}


cpl_frame *
xsh_create_master_flat (cpl_frame *frame, xsh_instrument *instr)
{
  xsh_pre *master = NULL;
  cpl_frame *masterFrame = NULL;
  const char *tag = NULL;
  char *name = NULL;

  /* Check parameters */
  XSH_ASSURE_NOT_NULL( frame);
  XSH_ASSURE_NOT_NULL( instr);

  check( master = xsh_pre_load( frame, instr));

  /* set the PROCATG KW */
  tag = XSH_GET_TAG_FROM_LAMP( XSH_MASTER_FLAT, instr);
  check( xsh_pfits_set_pcatg( master->data_header, tag));
  
  check( xsh_pre_normalize( master));

  XSH_NAME_LAMP_MODE_ARM( name, "MASTER_FLAT",".fits",instr);
  xsh_msg ("Create master flat %s tag %s", name, tag);

  check( masterFrame = xsh_pre_save( master, name, tag,0));
  check( cpl_frame_set_tag( masterFrame, tag));
  check( cpl_frame_set_group( masterFrame, CPL_FRAME_GROUP_CALIB));

  cleanup:
    XSH_FREE( name);
    xsh_pre_free( &master);
    return masterFrame;
}


cpl_frame *
xsh_create_master_dark_bpmap (cpl_frame * mdarkFrame, xsh_instrument* instr)
{
  /*
     Load frame into PRE 
     Create Frame with only the bpmap (qual from xsh_pre)
     Save and returns pointer to frame
  */
  xsh_pre * mdark_pre = NULL ;
  cpl_frame* resFrame = NULL;
  const  char* tag = XSH_GET_TAG_FROM_ARM( XSH_MASTER_BP_MAP_DARK, instr);
  char* finalName = NULL;
  int i=0, j=0;
  int* qual_data = NULL;
  int nx,ny;
  int nb_flag_pixels = 0;
  int error_pixels[32];

  /* check parameters input */
  XSH_ASSURE_NOT_NULL( mdarkFrame);
  XSH_ASSURE_NOT_NULL( instr);

  /* load data */
  check( mdark_pre = xsh_pre_load( mdarkFrame, instr));

  if ( xsh_instrument_get_arm(instr) == XSH_ARM_NIR ) {
     check(finalName=cpl_sprintf("%s_%s.fits",XSH_MASTER_BP_MAP_DARK,
			       xsh_instrument_arm_tostring(instr)));

  } else {
    /* Sabine doesn't like the binning in the filename
    check(binx=xsh_pfits_get_binx(mdark_pre->data_header));
    check(biny=xsh_pfits_get_biny(mdark_pre->data_header));
    check(finalName=cpl_sprintf("MASTER_BP_MAP_%s_%dx%d.fits", 
				xsh_instrument_arm_tostring(instr),binx,biny));
    */
     check(finalName=cpl_sprintf("%s_%s.fits",XSH_MASTER_BP_MAP_DARK,
			       xsh_instrument_arm_tostring(instr)));

  }

   check(resFrame=xsh_frame_product(finalName,tag,
                                   CPL_FRAME_TYPE_IMAGE,
                                   CPL_FRAME_GROUP_PRODUCT,
                                   CPL_FRAME_LEVEL_FINAL));

  /* Compute some QC KW */
  check( qual_data = cpl_image_get_data_int( mdark_pre->qual));
  check( nx = cpl_image_get_size_x( mdark_pre->qual));
  check( ny = cpl_image_get_size_y( mdark_pre->qual));

  /* count error in BP_MAP */
  for(j=0; j<32; j++){
    error_pixels[j] = 0;
  }

  for(i=0; i<nx*ny; i++){
    if( qual_data[i] > 0){
      nb_flag_pixels++;
      for( j=0;j<32;j++){
        int cmp = pow(2,j);
        if ( qual_data[i] & cmp){
          error_pixels[j]++;
        }
      }
    }
  }


  /* Write QC */
  check( xsh_pfits_set_qc_bp_map_ntotal(mdark_pre->qual_header,nb_flag_pixels));

  for(i=1; i< 33; i++){
    check( xsh_pfits_set_qc_multi( mdark_pre->qual_header, 
      (void *)&(error_pixels[i-1]), XSH_QC_BP_MAP_NFLAGi, instr, i));
  }
  /* Save qual image by itself */
  
  check_msg (cpl_image_save (mdark_pre->qual, finalName, XSH_PRE_DATA_BPP,
			     mdark_pre->qual_header, CPL_IO_DEFAULT),
	     "Could not save bpmap to %s extension", finalName);


  cleanup:
    if (cpl_error_get_code() != CPL_ERROR_NONE){
      xsh_free_frame(&resFrame);
    }
    XSH_FREE(finalName);
    xsh_pre_free( &mdark_pre);
    return resFrame;
}

static cpl_error_code
xsh_compute_ron_mbias(cpl_frameset* raws,
		      xsh_pre* master,
		      cpl_parameterlist* parameters)
{

   //const char* ron_method="ALL";
  int random_sizex=-1;
  int random_nsamples=-1;
  double ron=0;
  double ron_err=0;


  int ref_llx=-1;
  int ref_lly=-1;
  int ref_urx=-1;
  int ref_ury=-1;


  int ref2_llx=-1;
  int ref2_lly=-1;
  int ref2_urx=-1;
  int ref2_ury=-1;


  cpl_parameter* p=NULL;

  const cpl_frame  * fr = NULL;

  cpl_propertylist * plist = NULL;

  int naxis1 = 0;
  int naxis2 = 0;
/*
  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.ron_method"));
  check(ron_method = cpl_parameter_get_string(p));
*/

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.random_sizex"));
  check(random_sizex = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.random_nsamples"));
  check(random_nsamples = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.ref1_llx"));
  check(ref_llx = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.ref1_lly"));
  check(ref_lly = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.ref1_urx"));
  check(ref_urx = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.ref1_urx"));
  check(ref_ury = cpl_parameter_get_int(p));

  check(fr=cpl_frameset_get_frame_const(raws,0));

  check(plist=cpl_propertylist_load(cpl_frame_get_filename(fr),0));
  check(naxis1 = cpl_propertylist_get_int(plist, "NAXIS1"));
  check(naxis2 = cpl_propertylist_get_int(plist, "NAXIS2"));


  cpl_propertylist_delete(plist);

  //checking for defaults
  if(ref_llx == -1) ref_llx = naxis1 / 8;
  if(ref_lly == -1) ref_lly = naxis2 / 8;
  if(ref_urx == -1) ref_urx = naxis1 * 7 / 8;
  if(ref_ury == -1) ref_ury = naxis2 * 7 / 8;

  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_llx>0,"Must be ref_llx  > 0");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_lly>0,"Must be ref_lly  > 0");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_urx>0,"Must be ref_urx  > 0");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_ury>0,"Must be ref_ury  > 0");

  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_llx<naxis1,"Must be ref_llx  < frame X size");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_lly<naxis2,"Must be ref_lly  < frame Y size");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_urx<=naxis1,"Must be ref_urx  > frame X size");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_ury<=naxis2,"Must be ref_ury  > frame Y size");

  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_urx>ref_llx,"Must be ref_urx  > ref_llx");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref_ury>ref_lly,"Must be ref_ury  > ref_lly");

  XSH_ASSURE_NOT_ILLEGAL_MSG(random_nsamples>0,"Must be random_nsamples  > 0");
  XSH_ASSURE_NOT_ILLEGAL_MSG(random_nsamples<100000000,"Must be random_nsamples  < 100000000");
  XSH_ASSURE_NOT_ILLEGAL_MSG(random_sizex>0,"Must be random_sizex  > 0");
  XSH_ASSURE_NOT_ILLEGAL_MSG(random_sizex<naxis1,"Must be random_sizex  < frame X size");
  XSH_ASSURE_NOT_ILLEGAL_MSG(random_sizex<naxis2,"Must be random_sizex  < frame Y size");

  check(xsh_compute_ron(raws,ref_llx,ref_lly,ref_urx,ref_ury,random_nsamples,random_sizex,1,&ron,&ron_err));
  xsh_pfits_set_qc_ron1(master->data_header,ron);
  xsh_pfits_set_qc_ron1_err(master->data_header,ron_err);

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.ref2_llx"));
  check(ref2_llx = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.ref2_lly"));
  check(ref2_lly = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.ref2_urx"));
  check(ref2_urx = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.ref2_urx"));
  check(ref2_ury = cpl_parameter_get_int(p));


  //checking for defaults
  if(ref2_llx == -1) ref2_llx = naxis1 / 8;
  if(ref2_lly == -1) ref2_lly = naxis2 / 8;
  if(ref2_urx == -1) ref2_urx = naxis1 * 7 / 8;
  if(ref2_ury == -1) ref2_ury = naxis2 * 7 / 8;


  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_llx>0,"Must be ref2_llx  > 0");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_lly>0,"Must be ref2_lly  > 0");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_urx>0,"Must be ref2_urx  > 0");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_ury>0,"Must be ref2_ury  > 0");

  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_llx<naxis1,"Must be ref2_llx  < frame X size");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_lly<naxis2,"Must be ref2_lly  < frame Y size");


  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_urx<=naxis1,"Must be ref2_urx  <= frame X size");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_ury<=naxis2,"Must be ref2_ury  <= frame Y size");

  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_urx>ref2_llx,"Must be ref2_urx  > ref2_llx");
  XSH_ASSURE_NOT_ILLEGAL_MSG(ref2_ury>ref2_lly,"Must be ref2_ury  > ref2_lly");

  if( 
     (ref2_llx != ref_llx) ||
     (ref2_lly != ref_lly) ||
     (ref2_urx != ref_urx) ||
     (ref2_ury != ref_ury) 
     ) {

  check(xsh_compute_ron(raws,ref2_llx,ref2_lly,ref2_urx,ref2_ury,random_nsamples,random_sizex,2,&ron,&ron_err));

  xsh_pfits_set_qc_ron2(master->data_header,ron);
  xsh_pfits_set_qc_ron2_err(master->data_header,ron_err);

  }


 cleanup:

  return cpl_error_get_code();

}


static cpl_error_code
xsh_compute_fpn_mbias(cpl_frameset* raws,
		      xsh_pre* master,
		      xsh_instrument* instrument,
		      cpl_parameterlist* parameters)
{

    int nraws=0;
    double ron=0;
    double fpn=0;
    cpl_frame* frm1=NULL;
    cpl_frame* frm2=NULL;
    cpl_image* ima1=NULL;
    cpl_image* ima2=NULL;
    cpl_image* ima=NULL;


 if ( xsh_instrument_get_arm(instrument) != XSH_ARM_NIR){
    nraws=cpl_frameset_get_size(raws);
    if(nraws > 1) {
       frm1=cpl_frameset_get_frame(raws,0);
       frm2=cpl_frameset_get_frame(raws,1);
       ima1=cpl_image_load(cpl_frame_get_filename(frm1),CPL_TYPE_FLOAT,0,0);
       ima2=cpl_image_load(cpl_frame_get_filename(frm2),CPL_TYPE_FLOAT,0,0);

       ima = cpl_image_duplicate(ima1);
       check(ron = xsh_image_get_stdev_clean(ima, NULL) / sqrt(2.0));

       check(fpn = xsh_fixed_pattern_noise_bias(ima1,ima2,ron));

       xsh_pfits_set_qc_ron_master(master->data_header,ron);
       xsh_pfits_set_qc_fpn_master(master->data_header,fpn);

       xsh_free_image(&ima);
       xsh_free_image(&ima1);
       xsh_free_image(&ima2);
    }
 } else {
    check(xsh_mbias_get_fpn(master->data,parameters,&fpn));
    /* xsh_msg("Fixed Pattern Noise=%f",fpn); */

 }

 cleanup:
 xsh_free_image(&ima);
 xsh_free_image(&ima1);
 xsh_free_image(&ima2);
 return cpl_error_get_code();

}



static cpl_error_code
xsh_mbias_get_fpn(const cpl_image* ima,
                  cpl_parameterlist* parameters,
                  double* fpn)
{

  int fpn_llx=0;
  int fpn_lly=0;
  int fpn_urx=0;
  int fpn_ury=0;
  int fpn_hsize=0;
  int fpn_nsamp=0;
  cpl_parameter* p=NULL;
 
  cpl_size zone[4];
  int sx=0;
  int sy=0;

   check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.fpn_llx"));
  check(fpn_llx = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.fpn_lly"));
  check(fpn_lly = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.fpn_urx"));
  check(fpn_urx = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.fpn_ury"));
  check(fpn_ury = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.fpn_hsize"));
  check(fpn_hsize = cpl_parameter_get_int(p));

  check(p = cpl_parameterlist_find(parameters,"xsh.xsh_mbias.fpn_nsamples"));
  check(fpn_nsamp = cpl_parameter_get_int(p));

  sx=cpl_image_get_size_x(ima);
  sy=cpl_image_get_size_y(ima);

  fpn_llx=(fpn_llx>0) ? fpn_llx: 0;
  fpn_lly=(fpn_lly>0) ? fpn_lly: 0;

  fpn_urx=(fpn_urx<sx) ? fpn_urx: sx;
  fpn_ury=(fpn_ury<sy) ? fpn_ury: sy;

  
  zone[0]=fpn_llx;
  zone[1]=fpn_urx;
  zone[2]=fpn_lly;
  zone[3]=fpn_ury;

  
  check_msg(cpl_flux_get_noise_window(ima,zone,fpn_hsize,fpn_nsamp,fpn,NULL),
         "Error computing noise in a window");

 cleanup:
  return cpl_error_get_code();

}





/**
 * @brief
 *   Stack images using k-sigma clipping
 *
 * @param flats             List of flats to stack
 * @param ordertable        Input order table
 * @param order_locations   polynomial description of order locations
 *
 * @return Stacked image.
 *
 * The input list of flats is analized to compute
 * for each flat
 *     for each order
 *         The median flux on a number of windows of given X * Y size.
 *         The mean flux  of the values computed on each order is computed.
 *     endfor
 *     Finally the mean flux of all means is computed.
 *     The flat is normalized by the computed mean
 *  endfor
 */

static cpl_image *
xsh_flat_create_normalized_master(cpl_imagelist * flats,
                                   const cpl_table *ordertable,
                                   xsh_order_list* order_locations,
                                   xsh_stack_param* stack_param)
{
   int         ni;
   cpl_image  *image=NULL;
   cpl_image* master_flat=NULL;
   cpl_imagelist* flats_norm=NULL;
   int   k=0;
   int ord_min=0;
   int ord_max=0;
   int nord=0;
   double flux_mean=0;
   int nsam=10;
   int y_space=10;
   int hbox_sx=10;
   int hbox_sy=0;
   int is=0;
   int pos_x=0;
   int pos_y=0;
   int llx=0;
   int lly=0;
   int urx=0;
   int ury=0;

   double x=0;
   double y=0;
   int sx=0;
   int sy=0;
   cpl_vector* vec_flux_ord=NULL;
   cpl_vector* vec_flux_sam=NULL;
   double* pvec_flux_ord=NULL;
   double* pvec_flux_sam=NULL;
   double tolerance=1.e-5;
   //int absord=0;
   int ord=0;

   XSH_ASSURE_NOT_NULL_MSG(order_locations, "Null input order locations polinomial!");
   XSH_ASSURE_NOT_NULL_MSG(flats, "Null input flats imagelist!");

   ni         = cpl_imagelist_get_size(flats);

   image      = cpl_image_duplicate(cpl_imagelist_get(flats, 0));
   sx         = cpl_image_get_size_x(image);
   sy         = cpl_image_get_size_y(image);

   xsh_free_image(&image);
   ord_min=cpl_table_get_column_min(ordertable,"ORDER");
   ord_max=cpl_table_get_column_max(ordertable,"ORDER");
   nord=ord_max-ord_min+1;
   vec_flux_ord=cpl_vector_new(nord);
   vec_flux_sam=cpl_vector_new(nsam);
   pvec_flux_ord=cpl_vector_get_data(vec_flux_ord);
   pvec_flux_sam=cpl_vector_get_data(vec_flux_sam);
   hbox_sy=(int)((sy-2*y_space)/(2*nsam)+0.5);
   flats_norm=cpl_imagelist_new();
   int starty=0;
   int endy=0;
   for(k=0;k<ni;k++) {        /* loop over input images */
      xsh_free_image(&image);
      image = cpl_image_duplicate(cpl_imagelist_get(flats, k));
      for(ord=0;ord<nord;ord++) {
	//absord=ord+ord_min;
         endy=order_locations->list[ord].endy;
         starty=order_locations->list[ord].starty;
         pos_y=starty-hbox_sy;
         hbox_sy=(int)((endy-starty+1-2*y_space)/(2*nsam)+0.5);
         for(is=0;is<nsam;is++) {
           /* sample flux on nsam rectangular boxes distributed uniformly over the order center traces
            * we use a median to be robust to bad pixels */
            pos_y+=(2*hbox_sy+y_space);
            y=(int)(pos_y+0.5);


            check( x=cpl_polynomial_eval_1d(order_locations->list[ord].cenpoly,
                                                    (double)y,NULL));

            pos_x=(int)(x+0.5);

            llx=xsh_max_int(pos_x-hbox_sx,1);
            lly=xsh_max_int(pos_y-hbox_sy,1);
            llx=xsh_min_int(llx,sx);
            lly=xsh_min_int(lly,sy);

            urx=xsh_min_int(pos_x+hbox_sx,sx);
            ury=xsh_min_int(pos_y+hbox_sy,sy);
            urx=xsh_max_int(urx,1);
            ury=xsh_max_int(ury,1);

            llx=xsh_min_int(llx,urx);
            lly=xsh_min_int(lly,ury);
            //xsh_msg("ord=%d llx=%d lly=%d urx=%d ury=%d",ord,llx,lly,urx,ury);
            check(pvec_flux_sam[is]=cpl_image_get_median_window(image,llx,lly,urx,ury));

         }
         /* compute mean sampled values for each order */
         pvec_flux_ord[ord]=cpl_vector_get_mean(vec_flux_sam);
      }
      /* compute mean of mean values measured on all orders. Normalizes each flat image to it */
      flux_mean=cpl_vector_get_mean(vec_flux_ord);
      xsh_msg("Flat %d normalize factor inter1: %g",k,flux_mean);
      cpl_image_divide_scalar(image,flux_mean);
      cpl_imagelist_set(flats_norm,cpl_image_duplicate(image),k);
   }

   /* the first master flat is the median of all means */
   if (strcmp(stack_param->stack_method, "median") == 0) {
     master_flat=xsh_imagelist_collapse_median_create(flats_norm);
   } else {
     master_flat=cpl_imagelist_collapse_sigclip_create(flats_norm,stack_param->klow,stack_param->khigh,tolerance,CPL_COLLAPSE_MEAN,NULL);
   }


  cleanup:

   xsh_free_vector(&vec_flux_ord);
   xsh_free_vector(&vec_flux_sam);
   xsh_free_image(&image);
   xsh_free_imagelist(&flats_norm);


   return master_flat;

}



/**
 * @brief
 *   Stack images using k-sigma clipping
 *
 * @param flats             List of flats to stack
 * @param ordertable        Input order table
 * @param order_locations   polynomial description of order locations
 *
 * @return Stacked image.
 *
 * The input list of flats is analyzed to compute
 * for each flat
 *     for each order
 *         The median flux on a number of windows of given X * Y size.
 *         The mean flux  of the values computed on each order is computed.
 *     endfor
 *     Finally the mean flux of all means is computed.
 *     The flat is normalized by the computed mean
 *  endfor
 */
static cpl_image *
xsh_flat_create_normalized_master2(cpl_imagelist * flats,
    const cpl_table *ordertable, xsh_order_list* order_locations,
    const cpl_image* mflat,xsh_stack_param* stack_param,cpl_vector** vec_flux_stack) {

  cpl_imagelist* flats_norm = NULL;
  cpl_imagelist* flats_norm2 = NULL;
  cpl_image* master_flat = NULL;

  cpl_image* flat = NULL;
  cpl_image* flat_mflat = NULL;


  cpl_vector* vec_flux = NULL;
  double* pvec_flux = NULL;

  double* pvec_flux_stack = NULL;

  int ni = 0;
  int i = 0;
  int sx = 0;
  int sy = 0;
  int ord_min = 0;
  int ord_max = 0;
  int nord = 0;
  int nsam = 10;
  int y_space = 10;
  int llx = 0;
  int lly = 0;
  int urx = 0;
  int ury = 0;
  int hbox_sx = 0;
  int hbox_sy = 0;
  int ord = 0;
  //int absord = 0;
  int pos_x = 0;
  int pos_y = 0;
  double x = 0;
  double y = 0;
  double flux_median = 0;
  double mean_explevel=0;
  /* double exptime=0; */
  int is = 0;
  int k = 0;

  int starty=0;
  int endy=0;
  double tolerance=1.e-5;

  cpl_mask* mask;
  int nflagged=0;

  ni = cpl_imagelist_get_size(flats);

  /* evaluate median on many windows distributed all over orders of flats */
  sx = cpl_image_get_size_x(mflat);
  sy = cpl_image_get_size_y(mflat);

  ord_min = cpl_table_get_column_min(ordertable, "ORDER");
  ord_max = cpl_table_get_column_max(ordertable, "ORDER");
  nord = ord_max - ord_min + 1;

  hbox_sy = (int) ((sy - 2 * y_space) / (2 * nsam) + 0.5);
  flats_norm = cpl_imagelist_new();
  *vec_flux_stack = cpl_vector_new(ni);
  pvec_flux_stack=cpl_vector_get_data(*vec_flux_stack);

  for (i = 0; i < ni; i++) {
    xsh_free_vector(&vec_flux);
    vec_flux = cpl_vector_new(nord * nsam);
    pvec_flux = cpl_vector_get_data(vec_flux);





    check(flat = cpl_image_duplicate(cpl_imagelist_get(flats, i)));
    /* normalize flats by master flat previously computed */
    flat_mflat = cpl_image_duplicate(flat);
    cpl_image_divide(flat_mflat, mflat);
    mask = cpl_image_get_bpm(flat_mflat);
    k = 0;
    for (ord = 0; ord < nord; ord++) {
      //absord = ord + ord_min;

      endy=order_locations->list[ord].endy;
      starty=order_locations->list[ord].starty;
      pos_y=starty-hbox_sy;
      hbox_sy=(int)((endy-starty+1-2*y_space)/(2*nsam)+0.5);

      for (is = 0; is < nsam; is++) {
        /* sample flux on nsam rectangular boxes distributed uniformly over the order center traces
         * we use a median to be robust to bad pixels */

        pos_y += (2 * hbox_sy + y_space);
        y = (int) (pos_y + 0.5);

        check(
            x=cpl_polynomial_eval_1d(order_locations->list[ord].cenpoly, (double)y,NULL));

        pos_x = (int) (x + 0.5);

        check(llx=xsh_max_int(pos_x-hbox_sx,1));
        check(lly=xsh_max_int(pos_y-hbox_sy,1));
        check(llx=xsh_min_int(llx,sx));
        check(lly=xsh_min_int(lly,sy));

        check(urx=xsh_min_int(pos_x+hbox_sx,sx));
        check(ury=xsh_min_int(pos_y+hbox_sy,sy));
        check(urx=xsh_max_int(urx,1));
        check(ury=xsh_max_int(ury,1));

        check(llx=xsh_min_int(llx,urx));
        check(lly=xsh_min_int(lly,ury));

        check(pvec_flux[k]=0);
        nflagged=cpl_mask_count_window(mask,llx,lly,urx,ury);
        if(nflagged < (urx-llx+1)*(ury-lly+1) ) {
           check(pvec_flux[k]=cpl_image_get_median_window(flat_mflat,llx,lly,urx,ury));
        }
        k++;
      }

    }
    /* compute median of median values measured on all orders. Normalizes each flat image to it */
    flux_median = cpl_vector_get_median(vec_flux);
    pvec_flux_stack[i]= flux_median;
    xsh_msg("Flat %d level iter2: %g", i, flux_median);
    //xsh_msg("Flat %d level ingested: %g", i, cpl_vector_get(*vec_flux_stack,i));
    //cpl_image_divide_scalar(flat, flux_median);
    cpl_imagelist_set(flats_norm, cpl_image_duplicate(flat), i);
    //xsh_msg("mean flat1=%g",cpl_image_get_median(flat));
    mean_explevel+=flux_median;
  

    xsh_free_image(&flat_mflat);
    xsh_free_image(&flat);

  }

  mean_explevel /= ni;
  //xsh_msg("mean_exp level: %g",mean_explevel);
 
  cpl_vector_divide_scalar(*vec_flux_stack, mean_explevel);
  pvec_flux_stack = cpl_vector_get_data(*vec_flux_stack);
  flats_norm2 = cpl_imagelist_new();
  for (i = 0; i < ni; i++) {
    flat = cpl_imagelist_get(flats_norm, i);
    cpl_image_multiply_scalar(flat, 1./pvec_flux_stack[i]);
    //xsh_msg("inv factor=%g",pvec_flux_stack[i]);
    //xsh_msg("factor=%g",1./pvec_flux_stack[i]);
    //xsh_msg("mean flat2=%g",cpl_image_get_median(flat));
    cpl_imagelist_set(flats_norm2, cpl_image_duplicate(flat), i);
  }

  if (strcmp(stack_param->stack_method, "median") == 0) {
    master_flat=xsh_imagelist_collapse_median_create(flats_norm2);
  } else {
    master_flat=cpl_imagelist_collapse_sigclip_create(flats_norm2,stack_param->klow,stack_param->khigh,tolerance,CPL_COLLAPSE_MEAN,NULL);
  }

  cleanup:

  xsh_free_imagelist(&flats_norm);
  xsh_free_imagelist(&flats_norm2);
  xsh_free_vector(&vec_flux);
  xsh_free_image(&flat_mflat);
  //xsh_free_image(&flat);

  return master_flat;

}

/*----------------------------------------------------------------------------*/
/**
   @brief    Combine master frames
   @param    raw_images          The input images
   @param    raw_headers         An array containing the input image headers. The ordering
                                 must be the same as the ordering of images in the
                 input image list
   @param    master_flat_header  This header is updated with the normalized exposure time
   @param    master_bias         The master bias image for this chip, or NULL
   @param    master_dark         If non-null this master dark frame is subtracted
   @param    mdark_header        The header of the master dark frame (used only if
                                 @em master_dark is non-null).
   @param    ordertable          The order table for this chip. Used for background
                                 subtraction.
   @param    order_locations     Polynomial defining the order locations. Used for
                                 background subtraction.
   @param    flames              FLAMES reduction? In this case the background image
                                 is not computed/subtracted
   @param    parameters          The recipe parameter list
   @param    chip                CCD chip
   @param    recipe_id           name of calling recipe
   @param    DEBUG               Save intermediate results to disk?
   @param    background          (output) The background image which was subtracted

   @return   The master flat image

   This function
   - subtracts from each input image the provided @em master bias,
   - rescales each input image to unit exposure time by dividing by the exposure
   time read from the image header.
   - computes the master flat image by taking the pixel-by-pixel median of all input frames,
   - optionally subtracts the normalized master dark frame, and
   - subtracts the background (see @c xsh_backsub_spline()) .

   masterflat = median( (flat_i - masterbias)/exptime_i ) - masterdark/exptime_mdark - background

   Dark subtraction is optional and is done only if the parameter @em master_dark is non-NULL.
*/
/*----------------------------------------------------------------------------*/
cpl_frame *
xsh_create_master_flat2(cpl_frameset *set,
                        cpl_frame * order_tab_cen,
                        xsh_stack_param* stack_par,
                        xsh_instrument* inst)

{
  cpl_imagelist *dataList = NULL;
  cpl_imagelist *errsList = NULL;
  cpl_imagelist *errsList2 = NULL;
  //cpl_imagelist *raw_images_local = NULL;

  cpl_image* qual_comb = NULL;
  cpl_image* ima=NULL;

  cpl_image *master_flat = NULL;
  cpl_image *master_flat_tmp = NULL;
  cpl_image *current_flat = NULL;

  cpl_table* ordertable = NULL;

  cpl_vector* vec_flux=NULL;

  cpl_frame *current = NULL;

  cpl_frame* mflat = NULL;

  xsh_pre* pre = NULL;
  xsh_order_list *order_centres_list = NULL;

  double* pvec_flux=NULL;
  const char* fname = NULL;

  int i = 0;
  int size = 0;

  const int mode_and = 0;
  char mflat_name[256];
  char mflat_tag[256];
  
  /* is input valid? */
  XSH_ASSURE_NOT_NULL( inst);
  XSH_ASSURE_NOT_NULL( set);
  check( size = cpl_frameset_get_size( set));
  XSH_ASSURE_NOT_ILLEGAL( size > 0);

  fname = cpl_frame_get_filename(order_tab_cen);
  ordertable = cpl_table_load(fname, 1, 0);
  order_centres_list = xsh_order_list_load(order_tab_cen, inst);

  check( dataList = cpl_imagelist_new());
  check( errsList = cpl_imagelist_new());

  /* Populate image lists with images in frame set */
  for (i = 0; i < size; i++) {
    check(current = cpl_frameset_get_frame(set,i));
    /* Load pre file */
    check(pre = xsh_pre_load(current,inst));

    /* inject bad pixels to change statistics */
    xsh_image_flag_bp(pre->data, pre->qual, inst);
    xsh_image_flag_bp(pre->errs, pre->qual, inst);
    check_msg( cpl_imagelist_set(dataList,cpl_image_duplicate(pre->data),
            i),"Cant add Data Image %d to imagelist", i);
    check_msg( cpl_imagelist_set(errsList,cpl_image_duplicate(pre->errs),
            i),"Cant add Data Image %d to imagelist", i);

    /* combining bad pixels */
    if (i == 0) {
      qual_comb = cpl_image_duplicate(pre->qual);
    } else {
      xsh_badpixelmap_image_coadd(&qual_comb, pre->qual, mode_and);
    }
    /* Free memory */
    if (i < (size - 1)) {
      xsh_pre_free(&pre);
    }
  }

 /*free memory on product extensions that will be re-allocated later on (errs is only modified) */
  xsh_free_image(&pre->data);
  xsh_free_image(&pre->qual);

  /* store combined bp map in qual extension of result */
  pre->qual = cpl_image_duplicate(qual_comb);
  xsh_free_image(&qual_comb);

  /* Create initial master flat */
  xsh_msg("Calculating stack normalized master");
  check_msg(
      master_flat_tmp = xsh_flat_create_normalized_master(dataList, ordertable, order_centres_list,stack_par),
      "Error computing master flat with normalization");
  //check(cpl_image_save(master_flat_tmp,"master_flat_tmp.fits",CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT));


  /* Create final master flat */
 
  //xsh_free_image(&(pre->data));
  check_msg(
      pre->data = xsh_flat_create_normalized_master2(dataList, ordertable, order_centres_list, master_flat_tmp,
          stack_par,&vec_flux),"Error computing master flat with normalization");
  //check(cpl_image_save(pre->data,"master_flat_final.fits",CPL_BPP_IEEE_FLOAT,NULL,CPL_IO_DEFAULT));

  xsh_free_image(&master_flat_tmp);
  check(pvec_flux=cpl_vector_get_data(vec_flux));
  check( errsList2 = cpl_imagelist_new());

  for (i = 0; i < size; i++) {
     check(ima=cpl_imagelist_get(errsList,i));
     check(cpl_image_multiply_scalar(ima,pvec_flux[i]));
     check(cpl_imagelist_set(errsList2,cpl_image_duplicate(ima),i));
  }

  /* Create initial master flat err */
  if (strcmp(stack_par->stack_method, "median") == 0) {
     check( xsh_collapse_errs(pre->errs,errsList2,0));
  } else {
     check( xsh_collapse_errs(pre->errs,errsList2,1));
  }

  /*clean memory and reset local pointers to null */
  //raw_images_local = NULL;
  if (xsh_instrument_get_arm(inst) == XSH_ARM_UVB) {
    sprintf(mflat_tag, "%s_%s_%s", XSH_MASTER_FLAT,
        xsh_instrument_lamp_tostring(inst), xsh_instrument_arm_tostring(inst));
  } else {
    sprintf(mflat_tag, "%s_%s", XSH_MASTER_FLAT,xsh_instrument_arm_tostring(inst));
  }
  sprintf(mflat_name,"%s.fits",mflat_tag);
  xsh_count_satpix(pre, inst,size);
  check(mflat = xsh_pre_save( pre,mflat_name,mflat_tag,1));
  check(cpl_frame_set_tag(mflat,mflat_tag));

  cleanup:
  xsh_free_imagelist(&dataList);
  xsh_free_imagelist(&errsList);
  xsh_free_imagelist(&errsList2);
  xsh_free_table(&ordertable);
  xsh_free_image(&current_flat);
  xsh_order_list_free(&order_centres_list);
  xsh_pre_free(&pre);
  xsh_free_vector(&vec_flux);

  if (cpl_error_get_code() != CPL_ERROR_NONE) {
    xsh_free_image(&master_flat);
  }

  return mflat;
}


















/**@}*/
