/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004,2005 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */
/*----------------------------------------------------------------------------
 
     File name    :       sinfo_new_lamp_flats.c
   Author       :    A. Modigliani
   Created on   :    Sep 29, 2003
   Description  : 

 * this step handles stacks of lamp flat fields, 
 *  o it takes a clean mean,
 *  o subtracts the off- from the on-frames, 
 *  o corrects for static bad pixels and normalizes for a master flat field. 
 *  o It distinguishes the spectrally dithered frames and 
 *  o treats them the same way. 
 *  o It can also generate a static bad pixel mask if wished.


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

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

/*----------------------------------------------------------------------------
                                Includes
 ---------------------------------------------------------------------------*/
#include "sinfo_new_lamp_flats.h"
#include "sinfo_flat_ini_by_cpl.h"
#include "sinfo_pro_save.h"
#include "sinfo_pro_types.h"
#include "sinfo_functions.h"
#include "sinfo_new_cube_ops.h"
#include "sinfo_error.h"
#include "sinfo_utils_wrappers.h"
#include "sinfo_image_ops.h"
#include "sinfo_utilities.h"
#include "sinfo_globals.h"
/*----------------------------------------------------------------------------
                                Defines
 ---------------------------------------------------------------------------*/

static int 
new_qc_get_cnt(cpl_frameset* on_set, cpl_frameset* of_set, flat_config* cfg);
static int 
new_lamp_flats_det_ncounts(cpl_frameset* raw, flat_config* config);

static struct {

  double avg_on; 
  double std_on;
  double avg_of; 
  double std_of;
  double avg_di; 
  double std_di;
  double nsat_on;
  double noise_on;
  double noise_of;
  double flux_on;
  double    nsat;

} qc_lampflat;

/*----------------------------------------------------------------------------
                             Function Definitions
 ---------------------------------------------------------------------------*/
/**@{*/
/**
 * @addtogroup sinfo_lamp_cfg Flat frame manipulation functions
 *
 * TBD
 */

/*----------------------------------------------------------------------------
   @name  sinfo_new_lamp_flats()
   @param  plugin_id recipe id
   @param  config input parameterlist
   @param  sof    input set of frames
   @return integer (0 if it worked, -1 if it doesn't) 
   @doc 

 * this step handles stacks of lamp flat fields, 
 *  o it takes a clean mean,
 *  o subtracts the off- from the on-frames, 
 *  o corrects for static bad pixels and normalizes for a master flat field. 
 *  o It distinguishes the spectrally dithered frames and 
 *  o treats them the same way. 
 *  o It can also generate a static bad pixel mask if wished.


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




int 
sinfo_new_lamp_flats (const char* plugin_id, 
                      cpl_parameterlist* config, 
                      cpl_frameset* sof,
                      cpl_frameset* ref_set)
{
  flat_config * cfg =NULL;
  cpl_imagelist* list_object=NULL;
  cpl_imagelist* list_dither_object=NULL ;
  cpl_imagelist* list_sky=NULL ;
  cpl_imagelist* list_dither_sky=NULL;
  cpl_image ** im=NULL ;
  cpl_image * norm_dith =NULL;
  cpl_image * im_obj =NULL;
  cpl_image * int_im =NULL;
  cpl_image * int_im_dith =NULL;
  cpl_image * im_sky =NULL;
  cpl_image * im_dither =NULL;
  cpl_image * im_obj_sub =NULL;
  cpl_image * im_dither_sub =NULL;
  cpl_image * im_dither_sky =NULL;
  cpl_image ** imMed=NULL ;
  cpl_image * colImage =NULL;
  cpl_image * mask_im =NULL;
  cpl_image * norm =NULL;
  cpl_image * compImage =NULL;
  cpl_image * maskImage =NULL;
  cpl_image * threshIm =NULL;

  char name[MAX_NAME_SIZE];

  int typ;
  Stats * stats =NULL;
  int i = 0;
  int* n=NULL;
  int nob =0;
  int nsky = 0; 
  int nobjdith = 0;
  int nskydith = 0; 
  int n_badpixels =0;
  int pos =0;

  float** slit_edges=NULL;
  float local_clean_mean =0.;
  float clean_stdev =0.;
  float mean_factor =0.;
  float val_x=0;
  float val_y=0;

  char outNameDither[MAX_NAME_SIZE];
  char name_list[MAX_NAME_SIZE];
  char tbl_slitpos_name[MAX_NAME_SIZE];

  cpl_table* tbl_slitpos=NULL;
  int* status=NULL;

  int n_im_med=0;
  cpl_frameset* raw=NULL;

  cpl_table* qclog_tbl=NULL;
  int naxis1=0;
  int naxis2=0;
  double fpn_stdev1=0;
  double fpn_stdev2=0;

  int no=0;
  float lo_cut=0;
  float hi_cut=0;

  /*
       -----------------------------------------------------------------
       1) parse the file names and parameters to the tilt_config data 
          structure cfg 
       -----------------------------------------------------------------
  */

  cknull_nomsg(raw=cpl_frameset_new());

  cknull(cfg = sinfo_parse_cpl_input_flat(config,sof,&raw),
     "could not parse cpl input!");

  if (cfg->interpolInd == 1) {
    if(sinfo_is_fits_file(cfg->mask) != 1) {
      sinfo_msg_error("Input file %s is not FITS",cfg->mask);
      goto cleanup;
    }
    if (sinfo_is_fits_file(cfg->slitposList) != 1) {
      sinfo_msg_error("Input file %s is not FITS",cfg->slitposList);
      goto cleanup;
    }
  }

  /*
    #---------------------------------------------------------
    # Take a clean mean of several images
    # input is 1 or more similar images
    #---------------------------------------------------------
  */
  sinfo_msg("Takes clean mean of several images");
  /* #allocate memory for lists of object, sky and dithered frames--*/
  cknull(list_object = cpl_imagelist_new (),"could not allocate memory");

  if (cfg->contains_dither == 1) {
    cknull(list_dither_object=cpl_imagelist_new(),"could not allocate memory");
  }

  if (cfg->contains_sky == 1) {
    cknull(list_sky=cpl_imagelist_new(),"could not allocate memory");
  }

  if (cfg->contains_dither == 1 && cfg->nditheroff > 0) {
    cknull(list_dither_sky=cpl_imagelist_new(),"could not allocate memory");
  }

  if (cfg->contains_dither == 0 && cfg->nditheroff > 0){
    sinfo_msg_error("please use non-dithered off-frames, remove the 2!");
    goto cleanup;
  }
 

  /* problem with im as image holder: cleanup then does not work */
  im = (cpl_image**) cpl_calloc (cfg -> nframes, sizeof(cpl_image*)); 

  for (i=0; i< cfg->nframes; i++) {
    strcpy(name,cfg->framelist[i]);
    if(sinfo_is_fits_file(name) != 1) {
      sinfo_msg_error("PP Input file %s %d is not FITS",name,i);
      goto cleanup;
    }
    im[i]=cpl_image_load(name,CPL_TYPE_FLOAT,0,0);

  }

  for (i=0; i< cfg->nframes; i++) {
    typ = cfg->frametype[i];
    pos = cfg->frameposition[i];
    cknull(im[i],"could not load image %d",i);
    if (pos == 2) {
      if (typ == 1) {
        cpl_imagelist_set( list_object, cpl_image_duplicate(im[i]), nob );  
        nob = nob + 1;
      } else {
        cpl_imagelist_set( list_sky, cpl_image_duplicate(im[i]), nsky );
        nsky = nsky + 1 ;
      }
    } else {
      if (typ == 1) {
        cpl_imagelist_set(list_dither_object, 
                          cpl_image_duplicate(im[i]), nobjdith );  
        nobjdith = nobjdith + 1;
      } else {
        cpl_imagelist_set( list_dither_sky, 
                          cpl_image_duplicate(im[i]), nskydith );
        nskydith = nskydith + 1 ;
      }
    }
  }


  if (nob != cfg->nobj || cfg->noff != nsky || 
      nobjdith != cfg->nditherobj || nskydith != cfg->nditheroff) {
      sinfo_msg_error("something is wrong with the number of "
                      "the different types of frames");
      goto cleanup;
  }

  /* # create and fill cubes with the different image lists- */
  sinfo_msg("Creates and fills cubes with the different image lists");
  cknull(list_object,"could not create data cube!");

  if (cfg->contains_dither == 1) {
    cknull(list_dither_object,"could not create data cube!");
  }
  if (cfg->contains_sky == 1 && nsky > 0) {
    cknull(list_sky,"could not create data cube!");
  }

  if (cfg->contains_dither == 1 && nskydith > 0) {
    cknull(list_dither_sky,"could not create data cube!");
  }

 
  /*-take the average of the different cubes -*/
  sinfo_msg("Takes the average of the different cubes");
  if (cfg->loReject*cfg->nobj < 1. && cfg->hiReject *cfg->nobj < 1.) {
    cknull(im_obj = sinfo_new_average_cube_to_image(list_object ),
          "sinfo_averageCubeToImage failed" );
  } else {

    no=cpl_imagelist_get_size(list_object);
    lo_cut=(floor)(cfg->loReject*no+0.5);
    hi_cut=(floor)(cfg->hiReject*no+0.5);
    cknull(im_obj=cpl_imagelist_collapse_minmax_create(list_object,
                                                      lo_cut,
                                                      hi_cut),
                 "sinfo_average_with_rejection failed" );
  }
   sinfo_free_imagelist(&list_object);

  if (cfg->contains_sky == 1) {
    if (cfg->loReject * nsky < 1. && cfg->hiReject * nsky < 1.) {
      cknull(im_sky = sinfo_new_average_cube_to_image(list_sky ),
      "sinfo_new_average_cube_to_image failed" );
    } else {

    no=cpl_imagelist_get_size(list_sky);
    lo_cut=(floor)(cfg->loReject*no+0.5);
    hi_cut=(floor)(cfg->hiReject*no+0.5);
    cknull(im_sky=cpl_imagelist_collapse_minmax_create(list_sky,lo_cut,hi_cut),
           "sinfo_average_with_rejection failed" );
    }
    sinfo_free_imagelist(&list_sky);
  }

  if (cfg->contains_dither == 1) {
    if (cfg->loReject*nobjdith < 1. && cfg->hiReject * nobjdith < 1.) {
      cknull(im_dither = sinfo_new_average_cube_to_image(list_dither_object ),
             "sinfo_new_average_cube_to_image failed" );
   } else {


     no=cpl_imagelist_get_size(list_dither_object);
     lo_cut=(floor)(cfg->loReject*no+0.5);
     hi_cut=(floor)(cfg->hiReject*no+0.5);
     cknull(im_dither=cpl_imagelist_collapse_minmax_create(list_dither_object,
                                                           lo_cut,hi_cut),
                                      "sinfo_average_with_rejection failed" );
   }
   sinfo_free_imagelist(&list_dither_object);
  }

  if (cfg->contains_dither == 1 && nskydith > 0 ) {
    if (cfg->loReject*nskydith < 1. && cfg->hiReject*nskydith < 1.) {
      cknull(im_dither_sky = sinfo_new_average_cube_to_image(list_dither_sky ),
            "sinfo_new_average_cube_to_image failed" );
    } else {
      no=cpl_imagelist_get_size(list_dither_sky);
      lo_cut=(floor)(cfg->loReject*no+0.5);
      hi_cut=(floor)(cfg->hiReject*no+0.5);
      cknull(im_dither_sky=cpl_imagelist_collapse_minmax_create(list_dither_sky,
                                                                lo_cut,hi_cut),
                                          "new_average_with_rejection failed" );
    }
    sinfo_free_imagelist(&list_dither_sky);
  }   

  /*
  #---------------------------------------------------------
  # Subtract the resulting off-frame (sky) from the on-frame 
  #-------------------------------------------------------
  #finally, subtract off from on frames and store the result in the 
  # object cube----------------
  */

  sinfo_msg("Subtracts the resulting off-frame (sky) from the on-frame");
  if (cfg->contains_sky == 1) {
     cknull(im_obj_sub = cpl_image_subtract_create(im_obj, im_sky),
            "could not sinfo_sub_image");
     sinfo_free_image(&im_obj);
     if (((cfg->contains_dither == 1) && (nskydith > 0)) || 
         (cfg->contains_dither == 0)) {
       sinfo_free_image(&im_sky);
     }
     im_obj = im_obj_sub;
  }

  if (cfg->contains_dither == 1 && nskydith > 0) {
     cknull(im_dither_sub=cpl_image_subtract_create(im_dither, im_dither_sky),
            "could not sinfo_sub_image");
     sinfo_free_image(&im_dither);
     sinfo_free_image(&im_dither_sky);
     im_dither = im_dither_sub;
   } else if (cfg->contains_dither == 1 && 
                          nskydith == 0 && 
              cfg->contains_sky == 1) {
     cknull(im_dither_sub = cpl_image_subtract_create(im_dither, im_sky),
        "could not sinfo_sub_image");
     sinfo_free_image(&im_dither);
     sinfo_free_image(&im_sky);
     im_dither = im_dither_sub;
   }
   /*
   #---------------------------------------------------------
   # Generating a static bad pixel mask:
   # remove the intensity tilt from every column
   # and compute the standard deviation on a rectangular zone
   #---------------------------------------------------------
   */

   sinfo_msg("Generating a static bad pixel mask");
   n_im_med = cfg->iterations+1;

   imMed=(cpl_image**) cpl_calloc(n_im_med, sizeof(cpl_image*));

   if (cfg->badInd == 1) {
     sinfo_msg("removes the intensity tilt from every column and");
     sinfo_msg("computes the standard deviation on a rectangular zone");

     /* this call originates 36 bytes leaks */
     cknull(colImage  = sinfo_new_col_tilt( im_obj, cfg->sigmaFactor ),
        "sinfo_colTilt failed" );

     cknull(stats = sinfo_new_image_stats_on_rectangle(colImage, 
                                   cfg->badLoReject, 
                                   cfg->badHiReject, 
                                   cfg->llx, 
                                   cfg->lly, 
                                   cfg->urx, 
                               cfg->ury),
                                   "sinfo_get_image_stats_on_vig failed\n");
     
     local_clean_mean = stats->cleanmean;
     clean_stdev = stats->cleanstdev;


     /* indicate pixels with great deviations from the clean mean as bad */
     if (cfg->threshInd == 1) {
         cknull(threshIm = sinfo_new_thresh_image(colImage, 
            local_clean_mean-mean_factor*clean_stdev,
            local_clean_mean+mean_factor*clean_stdev),
        " sinfo_threshImage failed\n" );
     }
     if (cfg->threshInd == 0) {
        threshIm = colImage;
     }

     /*
     filter iteratively the images by a sinfo_median filter of the nearest 
     neighbors under the condition of a deviation greater than a factor 
     times the standard deviation
     */

     cknull(imMed[0]= sinfo_new_median_image(threshIm,-cfg->factor*clean_stdev),
                                            " sinfo_medianImage failed" );
     

       /* AMO check again if here the loop start and ending point are proper */

     for (i=1; i< cfg->iterations+1; i++) {
       cknull(imMed[i]=sinfo_new_median_image(imMed[i-1], 
                          -cfg->factor*clean_stdev),
                                          "sinfo_medianImage failed" );
     }

     /* compare the filtered image with the input image */
     cknull(compImage=sinfo_new_compare_images(threshIm, 
                                               imMed[cfg->iterations], 
                                               im_obj),
        "sinfo_compareImages failed" );

     /*---generate the bad pixel mask */
     n = (int*)cpl_calloc(1,sizeof(int));
     cknull(maskImage = sinfo_new_promote_image_to_mask( compImage, n ),
            "error in sinfo_promoteImageToMask" );


     n_badpixels = n[0];
     sinfo_msg("No of bad pixels: %d", n_badpixels);

     cknull_nomsg(qclog_tbl = sinfo_qclog_init());
     ck0_nomsg(sinfo_qclog_add_int(qclog_tbl,"QC BP-MAP NBADPIX",n_badpixels,
                                   "No of bad pixels","%d"));

     ck0(sinfo_pro_save_ima(maskImage,ref_set,sof,cfg->maskname,
                  PRO_BP_MAP,qclog_tbl,plugin_id,config),
               "cannot save ima %s", cfg->maskname);


     /* free memory */
     sinfo_free_table(&qclog_tbl);
 
     sinfo_new_del_Stats(stats);
     sinfo_free_int(&n);
     sinfo_free_image(&threshIm); /* */
     if (cfg->threshInd == 1) {
       sinfo_free_image(&colImage);
     }
     sinfo_free_image(&compImage);
     sinfo_free_image(&maskImage);

     for (i=0; i< cfg->iterations+1; i++) {
       sinfo_free_image(&imMed[i]);
     }

   }

   cpl_free(imMed);
   imMed=NULL;

   /*
   #---------------------------------------------------------
   # Master flat field: static bad pixel correction and normalizing
   #---------------------------------------------------------
   */

   sinfo_msg("Creates a Master flat field");
   if (cfg->interpolInd == 1) {
     cknull(mask_im = cpl_image_load(cfg->mask,CPL_TYPE_FLOAT,0,0),
        "could not load static bad pixel mask" );

      /* open the ASCII list of the slitlet positions */

      /* slit_edges = sinfo_new_2Dfloat_array(32, 2) ; */
      slit_edges = (float **) cpl_calloc( 32, sizeof (float*) ) ;
      for ( i = 0 ; i < 32 ; i++ )
        {
            slit_edges[i] = (float *) cpl_calloc( 2, sizeof (float)) ;
        }
      /*READ TFITS TABLE*/
      if(sinfo_is_fits_file(cfg->slitposList) !=1 ) {
          sinfo_msg_error("Input file %s is not FITS", cfg->slitposList);
          goto cleanup;
      }
      strcpy(tbl_slitpos_name,cfg->slitposList);
      check(tbl_slitpos = cpl_table_load(tbl_slitpos_name,1,0),
         "error loading tbl %s",tbl_slitpos_name);

      for (i =0 ; i< 32; i++){
            val_x=cpl_table_get_double(tbl_slitpos,"pos1",i,status);
            val_y=cpl_table_get_double(tbl_slitpos,"pos2",i,status);
            slit_edges[i][0]=val_x;
        slit_edges[i][1]=val_y;
      }
      sinfo_free_table(&tbl_slitpos);

      cknull(int_im = sinfo_interpol_source_image (im_obj, mask_im, 
                                                 cfg->maxRad, slit_edges),
                         "could not carry out sinfo_interpolSourceImage" );
      
      sinfo_free_image(&im_obj);
      cknull(norm = sinfo_new_normalize_to_central_pixel(int_im),
             "could not normalize flatfield" );
      sinfo_free_image(&int_im);
      im_obj = norm; 

      if (cfg->contains_dither == 1) {
        cknull(int_im_dith = sinfo_interpol_source_image(im_dither, 
                               mask_im,
                               cfg->maxRad,
                               slit_edges),
           "could not carry out sinfo_interpolSourceImage" );
      
        cpl_image_delete(im_dither);
        cknull(norm_dith = sinfo_new_normalize_to_central_pixel(int_im_dith),
           "could not normalize flatfield" );
        sinfo_free_image(&int_im_dith);
        im_dither = norm_dith;
      }
      /* sinfo_new_destroy_2Dfloatarray(slit_edges, 32); */    
      for ( i = 0 ; i < 32 ; i++ )
        {
            cpl_free( slit_edges[i] );
        }
      cpl_free( slit_edges ) ;
      sinfo_free_image(&mask_im);

   }

   if (cfg->interpolInd != 1) {
     cknull(norm = sinfo_new_normalize_to_central_pixel(im_obj),
            "could not normalize flatfield" );
      sinfo_free_image(&im_obj);
      im_obj = norm; 

      if (cfg->contains_dither == 1) {
        cknull(norm_dith = sinfo_new_normalize_to_central_pixel(im_dither),
           "could not normalize flatfield" );
        sinfo_free_image(&im_dither);
        im_dither = norm_dith;
      }
   }

   naxis1=cpl_image_get_size_x(im_obj);
   naxis2=cpl_image_get_size_y(im_obj);


   if(cfg->qc_fpn_xmin1 < 1) {
     sinfo_msg_error("qc_ron_xmin < 1");
     goto cleanup;
   }

   if(cfg->qc_fpn_xmax1 > naxis1) {
     sinfo_msg_error("qc_ron_xmax < %d",naxis1);
     goto cleanup;
   }

   if(cfg->qc_fpn_ymin1 < 1) {
     sinfo_msg_error("qc_ron_ymin < 1");
     goto cleanup;
   }

   if(cfg->qc_fpn_ymax1 > naxis2) {
     sinfo_msg_error("qc_ron_ymax < %d",naxis2);
     goto cleanup;
   }
   fpn_stdev1 = cpl_image_get_stdev_window(im_obj,
                       cfg->qc_fpn_xmin1,
                       cfg->qc_fpn_ymin1,
                       cfg->qc_fpn_xmax1,
                       cfg->qc_fpn_ymax1);


   if(cfg->qc_fpn_xmin2 < 1) {
     sinfo_msg_error("qc_ron_xmin < %d",1);
     goto cleanup;
   }


   if(cfg->qc_fpn_xmax2 > naxis1) {
     sinfo_msg_error("qc_ron_xmax < %d",naxis1);
     goto cleanup;
   }

   if(cfg->qc_fpn_ymin2 < 1) {
     sinfo_msg_error("qc_ron_ymin < 1");
     goto cleanup;
   }

   if(cfg->qc_fpn_ymax2 > naxis2) {
     sinfo_msg_error("qc_ron_ymax < %d",naxis2);
     goto cleanup;
   }
   fpn_stdev2 = cpl_image_get_stdev_window(im_obj,
                       cfg->qc_fpn_xmin2,
                       cfg->qc_fpn_ymin2,
                       cfg->qc_fpn_xmax2,
                       cfg->qc_fpn_ymax2);



   ck0(new_lamp_flats_det_ncounts(raw,cfg),"error computing number of counts");
   cknull_nomsg(qclog_tbl = sinfo_qclog_init());
   ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC SPECFLAT NCNTSAVG",
                                    qc_lampflat.avg_di,"Average counts","%g"));
   ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC SPECFLAT NCNTSSTD",
                                    qc_lampflat.std_di,"Stdev counts","%g"));
   ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC SPECFLAT OFFFLUX",
                                    qc_lampflat.avg_of,
                                    "Average flux off frames","%g"));

    ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,
                                     "QC LFLAT FPN1",
                                     fpn_stdev1,
                                     "Fixed Pattern Noise of combined frames",
                                     "%f"));

    ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,
                                     "QC LFLAT FPN2",
                                     fpn_stdev2,
                                     "Fixed Pattern Noise of combined frames",
                                     "%f"));

      ck0(sinfo_pro_save_ima(im_obj,ref_set,sof,cfg->outName,
			     PRO_MASTER_FLAT_LAMP,qclog_tbl,plugin_id,config),
	  "cannot save ima %s", cfg->outName);

    sinfo_free_table(&qclog_tbl);
    sinfo_free_image(&im_obj);

 
    if (cfg->contains_dither == 1) {

      if (strstr(cfg->outName, ".fits" ) != NULL ) {

    snprintf(name_list, MAX_NAME_SIZE-1,"%s%s", 
	     sinfo_new_get_rootname(cfg->outName),
                "_dither");
        strcpy(outNameDither,name_list);
        strcat(outNameDither,strstr(cfg->outName,".fits"));
       
      } else {
        strcpy(outNameDither,cfg->outName);
        strcat(outNameDither,"_dither");
      }


      naxis1=cpl_image_get_size_x(im_dither);
      naxis2=cpl_image_get_size_y(im_dither);


      if(cfg->qc_fpn_xmin1 < 1) {
     sinfo_msg_error("qc_ron_xmin1 < 1");
         goto cleanup;
      }

      if(cfg->qc_fpn_xmax1 > naxis1) {
     sinfo_msg_error("qc_ron_xmax1 < %d",naxis1);
         goto cleanup;
      }

      if(cfg->qc_fpn_ymin1 < 1) {
     sinfo_msg_error("qc_ron_ymin1 < 1");
         goto cleanup;
      }

      if(cfg->qc_fpn_ymax1 > naxis2) {
     sinfo_msg_error("qc_ron_ymax1 < %d",naxis2);
         goto cleanup;
      }

 
      fpn_stdev1 = cpl_image_get_stdev_window(im_dither,
                          cfg->qc_fpn_xmin1,
                          cfg->qc_fpn_ymin1,
                          cfg->qc_fpn_xmax1,
                          cfg->qc_fpn_ymax1);

      if(cfg->qc_fpn_xmin2 < 1) {
     sinfo_msg_error("qc_ron_xmin2 < 1");
         goto cleanup;
      }

      if(cfg->qc_fpn_xmax2 > naxis1) {
     sinfo_msg_error("qc_ron_xmax2 < %d",naxis1);
         goto cleanup;
      }

      if(cfg->qc_fpn_ymin2 < 1) {
     sinfo_msg_error("qc_ron_ymin2 < 1");
         goto cleanup;
      }

      if(cfg->qc_fpn_ymax2 > naxis2) {
     sinfo_msg_error("qc_ron_ymax2 < %d",naxis2);
         goto cleanup;
      }
 
      fpn_stdev2 = cpl_image_get_stdev_window(im_dither,
					      cfg->qc_fpn_xmin2,
					      cfg->qc_fpn_ymin2,
					      cfg->qc_fpn_xmax2,
					      cfg->qc_fpn_ymax2);


      ck0(new_lamp_flats_det_ncounts(raw,cfg),"error computing ncounts");
      cknull_nomsg(qclog_tbl = sinfo_qclog_init());
      ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC SPECFLAT NCNTSAVG",
				       qc_lampflat.avg_di,"Average counts","%g"));

      ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC SPECFLAT NCNTSSTD",
				       qc_lampflat.std_di,"Stdev counts","%g"));

      ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC SPECFLAT OFFFLUX",
				       qc_lampflat.avg_of,"Average flux off frames","%g"));

      ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC LFLAT FPN1",fpn_stdev1,
				       "Fixed Pattern Noise of combined frames","%f"));

      ck0_nomsg(sinfo_qclog_add_double(qclog_tbl,"QC LFLAT FPN2",fpn_stdev2,
				       "Fixed Pattern Noise of combined frames","%f"));
 

	ck0(sinfo_pro_save_ima(im_dither,ref_set,sof,outNameDither,
			       PRO_MASTER_FLAT_LAMP,qclog_tbl,plugin_id,config),
	    "cannot save ima %s", outNameDither);

      sinfo_free_table(&qclog_tbl);
      sinfo_free_image(&im_dither);

    }


    /* could be done earlier? */
    sinfo_free_image_array(&im,cfg->nframes);
    sinfo_free_frameset(&raw);
    sinfo_flat_free(&cfg);
    return 0;

cleanup:


    /* free memory */
    if(slit_edges != NULL) {
      for ( i = 0 ; i < 32 ; i++ )
        {
      if(slit_edges[i] != NULL) {
            cpl_free( slit_edges[i] );
      }
          slit_edges[i]=NULL;
        }
      cpl_free( slit_edges ) ;
    }
    sinfo_free_image(&mask_im);
    sinfo_free_table(&qclog_tbl);
    if(stats) {
      sinfo_new_del_Stats(stats);
    }
    sinfo_free_int(&n);
    sinfo_free_image(&threshIm);
    sinfo_free_image(&maskImage);
    if(imMed != NULL) sinfo_free_image_array(&imMed,cfg->iterations);
    if(stats!= NULL) {
        sinfo_new_del_Stats(stats);
        stats=NULL;
    }
    sinfo_free_image(&compImage);
    sinfo_free_image(&colImage);
    sinfo_free_imagelist(&list_dither_object);
    sinfo_free_imagelist(&list_dither_sky);
    if(list_sky != NULL) {
       sinfo_free_imagelist(&list_sky);
    }
    sinfo_free_imagelist(&list_object);
    sinfo_free_image(&im_dither);
    sinfo_free_image(&im_dither_sky);
    sinfo_free_image(&im_obj);
    sinfo_free_image(&im_sky);
    if(im != NULL) sinfo_free_image_array(&im,cfg->nframes);
    sinfo_free_frameset(&raw);
    if(cfg != NULL) {
      sinfo_flat_free(&cfg);
    }
    return -1;

}

static int
new_lamp_flats_det_ncounts(cpl_frameset* raw, flat_config* cfg)
{
 int i=0;
 int j=0;

 int nraw=0;
 int non=0;
 int noff=0;

 double mjd_on=0;
 double mjd_of=0;
 double mjd_of_frm=0;

 char filename[MAX_NAME_SIZE];

 cpl_frame* frm=NULL;
 cpl_frame* frm_dup=NULL;
 cpl_frame* on_frm=NULL;
 cpl_frame* of_frm=NULL;
 cpl_frame* tmp_of_frm=NULL;


 cpl_frameset* on_set=NULL;
 cpl_frameset* of_set=NULL;
 cpl_frameset* wrk_set=NULL;

 on_set=cpl_frameset_new();
 of_set=cpl_frameset_new();

 nraw = cpl_frameset_get_size(raw);

 for (i=0; i< nraw; i++) {
   frm = cpl_frameset_get_frame(raw,i);
   frm_dup = cpl_frame_duplicate(frm);
   if(sinfo_frame_is_on(frm) == 1) {
     cpl_frameset_insert(on_set,frm_dup);
     non++;
   } else {
     cpl_frameset_insert(of_set,frm_dup);
     noff++;
   }
 }


 if (non == noff) {
   new_qc_get_cnt(on_set,of_set,cfg);

 } else if (non == 0) {
   sinfo_msg("non == 0");
   sinfo_msg_warning("QC SPECFLAT NCNTAVG/NCTNTSTD/OFFFLUX=0 ");
 
 } else if ( noff == 0 ) {
   sinfo_msg("noff == 0");
   sinfo_msg_warning("QC SPECFLAT NCNTAVG/NCTNTSTD/OFFFLUX=0 ");

 } else {

   sinfo_msg_warning("non != noff, => QC SPECFLAT NCNTAVG/NCTNTSTD/OFFFLUX=0 ");

    for (i=0;i<non;i++) {
       wrk_set=cpl_frameset_new();
       on_frm=cpl_frameset_get_frame(on_set,i);
       mjd_on=sinfo_get_mjd_obs(on_frm);
       of_frm=cpl_frameset_get_frame(of_set,0);
       mjd_of=sinfo_get_mjd_obs(of_frm);
       strcpy(filename,cpl_frame_get_filename(of_frm));
       for (j=1;j<noff;j++) {
          tmp_of_frm = cpl_frameset_get_frame(of_set,j);
          mjd_of_frm = sinfo_get_mjd_obs(tmp_of_frm);

          if(1000.*(mjd_of_frm-mjd_on)*(mjd_of_frm-mjd_on) <
             1000.*(mjd_of-    mjd_on)*(mjd_of-    mjd_on) ) {
            mjd_of=mjd_of_frm;
             of_frm=cpl_frame_duplicate(tmp_of_frm);
       }
       }
       strcpy(filename,cpl_frame_get_filename(of_frm));
       frm_dup=cpl_frame_duplicate(of_frm);
       cpl_frameset_insert(wrk_set,frm_dup);
       strcpy(filename,cpl_frame_get_filename(of_frm));
    }
    /* Commented out as algorithm non robust if non != noff */
    new_qc_get_cnt(on_set,wrk_set,cfg); 

 }

 cpl_frameset_delete(wrk_set);
 cpl_frameset_delete(on_set);
 cpl_frameset_delete(of_set);
 return 0;


}

static int
new_qc_get_cnt(cpl_frameset* on_set, cpl_frameset* of_set, flat_config* cfg)
{

  int i=0;
  int nsat=0;
  int non=0;
  int nof=0;
  int nfr=0;

  char name[MAX_NAME_SIZE];
  cpl_vector* vec_on=NULL;
  cpl_vector* vec_of=NULL;
  cpl_vector* vec_di=NULL;
  cpl_vector* vec_nsat=NULL;
  cpl_frame* on_frm=NULL;
  cpl_frame* of_frm=NULL;

  cpl_image* dif_ima=NULL;
  cpl_image* on_ima=NULL;
  cpl_image* of_ima=NULL;
  cpl_image* tmp_ima=NULL;

  double med=0;
    non = cpl_frameset_get_size(on_set);
    nof = cpl_frameset_get_size(of_set);
    nfr = (non <= nof) ? non : nof;
    vec_on = cpl_vector_new(nfr);
    vec_of = cpl_vector_new(nfr);
    vec_di = cpl_vector_new(nfr);
    vec_nsat = cpl_vector_new(nfr);


    for (i=0; i< nfr; i++) {
      on_frm = cpl_frameset_get_frame(on_set,i);
      strcpy(name,cpl_frame_get_filename(on_frm));
      on_ima = cpl_image_load(name,CPL_TYPE_FLOAT,0,0);
      med= cpl_image_get_median(on_ima);
      cpl_vector_set(vec_on,i,med);

      tmp_ima = cpl_image_duplicate(on_ima);
      cpl_image_threshold(tmp_ima,SINFO_DBL_MIN,
                          cfg->qc_thresh_max,0,1);
      nsat=cpl_image_get_flux(tmp_ima);
      cpl_vector_set(vec_nsat,i,nsat);

   /* Are you sure to have same frames off as on ? */
      of_frm = cpl_frameset_get_frame(of_set,i);
      strcpy(name,cpl_frame_get_filename(of_frm));
      of_ima = cpl_image_load(name,CPL_TYPE_FLOAT,0,0);
      med= cpl_image_get_median(of_ima);
      cpl_vector_set(vec_of,i,med);
      dif_ima = cpl_image_subtract_create(on_ima,of_ima);
      med= cpl_image_get_median(dif_ima);
      cpl_vector_set(vec_di,i,med);

    cpl_image_delete(on_ima);
    cpl_image_delete(of_ima);
    cpl_image_delete(dif_ima);
    cpl_image_delete(tmp_ima);
    }
    qc_lampflat.avg_on=cpl_vector_get_mean(vec_on);
    qc_lampflat.avg_of=cpl_vector_get_mean(vec_of);
    qc_lampflat.avg_di=cpl_vector_get_mean(vec_di);
    if(nfr > 1 ) {
       qc_lampflat.std_on=cpl_vector_get_stdev(vec_on);
       qc_lampflat.std_of=cpl_vector_get_stdev(vec_of);
       qc_lampflat.std_di=cpl_vector_get_stdev(vec_di);
    }
    qc_lampflat.nsat=cpl_vector_get_mean(vec_nsat);
    cpl_vector_delete(vec_on);
    cpl_vector_delete(vec_of);
    cpl_vector_delete(vec_di);
    cpl_vector_delete(vec_nsat);
    /*
    sinfo_msg( "sinfo_qc_get_cnt","avg_on=%g std_on=%g ",
                      qc_lampflat.avg_on,qc_lampflat.std_on);
    sinfo_msg( "sinfo_qc_get_cnt","avg_of=%g std_of=%g ",
                      qc_lampflat.avg_of,qc_lampflat.std_of);
    sinfo_msg( "sinfo_qc_get_cnt","avg_di=%g std_di=%g ",
                      qc_lampflat.avg_di,qc_lampflat.std_di);
    sinfo_msg( "sinfo_qc_get_cnt","nsat=%g ",qc_lampflat.nsat);
    */
    return 0;
}

/**@}*/
