/*
 * 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_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++) {
        cpl_frame* 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 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;


    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++) {
        cpl_frame* on_frm = cpl_frameset_get_frame(on_set,i);
        strcpy(name,cpl_frame_get_filename(on_frm));
        cpl_image* on_ima = cpl_image_load(name,CPL_TYPE_FLOAT,0,0);
        double med= cpl_image_get_median(on_ima);
        cpl_vector_set(vec_on,i,med);

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

        /* Are you sure to have same frames off as on ? */
        cpl_frame* of_frm = cpl_frameset_get_frame(of_set,i);
        strcpy(name,cpl_frame_get_filename(of_frm));
        cpl_image* 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);
        cpl_image* 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;
}

/**@}*/
