/*
 * This file is part of the KMOS Pipeline
 * Copyright (C) 2002,2003 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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

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

/*-----------------------------------------------------------------------------
 *                              Includes
 *----------------------------------------------------------------------------*/

#include <string.h>
#include <math.h>

#include <cpl.h>

#include "kmo_utils.h"
#include "kmos_pfits.h"
#include "kmo_dfs.h"
#include "kmo_error.h"
#include "kmo_constants.h"
#include "kmo_priv_dark.h"
#include "kmo_priv_combine.h"
#include "kmo_priv_functions.h"
#include "kmo_cpl_extensions.h"
#include "kmo_debug.h"
#include "kmos_oscan.h"

/*-----------------------------------------------------------------------------
 *                          Functions prototypes
 *----------------------------------------------------------------------------*/

static int kmos_dark_check_inputs(cpl_frameset *, int *, int *, int *, int *,
        double *);

static int kmos_dark_create(cpl_plugin *);
static int kmos_dark_exec(cpl_plugin *);
static int kmos_dark_destroy(cpl_plugin *);
static int kmos_dark(cpl_parameterlist *, cpl_frameset *);

/*-----------------------------------------------------------------------------
 *                          Static variables
 *----------------------------------------------------------------------------*/

static char kmos_dark_description[] =
"This recipe calculates the master dark frame.\n"
"\n"
"It is recommended to provide three or more dark exposures to produce a\n"
"reasonable master with associated noise.\n"
"\n"
"---------------------------------------------------------------------------\n"
"Input files:\n"
"   DO CATG          Type   Explanation                     Required #Frames\n"
"   -------          -----  -----------                     -------- -------\n"
"   DARK             RAW    Dark exposures                     Y       1-n  \n"
"                           (at least 3 frames recommended)                 \n"
"\n"
"Output files:\n"
"   DO CATG          Type   Explanation\n"
"   -------          -----  -----------\n"
"   MASTER_DARK      F2D    Calculated master dark frames\n"
"   BADPIXEL_DARK    B2D    Associated badpixel frames\n"
"---------------------------------------------------------------------------"
"\n";

/*----------------------------------------------------------------------------*/
/**
 * @defgroup kmos_dark   Create master dark frame & bad pixel mask
 */
/*----------------------------------------------------------------------------*/

/**@{*/

/*-----------------------------------------------------------------------------
 *                              Functions code
 *----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/**
  @brief    Build the list of available plugins, for this module. 
  @param    list    the plugin list
  @return   0 if everything is ok, -1 otherwise

  Create the recipe instance and make it available to the application using the 
  interface. This function is exported.
 */
/*----------------------------------------------------------------------------*/
int cpl_plugin_get_info(cpl_pluginlist *list)
{
    cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
    cpl_plugin *plugin = &recipe->interface;

    cpl_plugin_init(plugin,
            CPL_PLUGIN_API,
            KMOS_BINARY_VERSION,
            CPL_PLUGIN_TYPE_RECIPE,
            "kmos_dark",
            "Create master dark frame & bad pixel mask",
            kmos_dark_description,
            "Alex Agudo Berbel, Yves Jung",
            "usd-help@eso.org",
            kmos_get_license(),
            kmos_dark_create,
            kmos_dark_exec,
            kmos_dark_destroy);

    cpl_pluginlist_append(list, plugin);

    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options    
  @param    plugin  the plugin
  @return   0 if everything is ok

  Defining the command-line/configuration parameters for the recipe.
 */
/*----------------------------------------------------------------------------*/
static int kmos_dark_create(cpl_plugin *plugin)
{
    cpl_recipe *recipe;
    cpl_parameter *p;

    /* Check that the plugin is part of a valid recipe */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else
        return -1;

    /* Create the parameters list in the cpl_recipe object */
    recipe->parameters = cpl_parameterlist_new();

    /* Fill the parameters list */
    /* --oscan */
    p = cpl_parameter_new_value("kmos.kmos_dark.oscan",
            CPL_TYPE_BOOL, "Apply Overscan Correction",
            "kmos.kmos_dark", TRUE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "oscan");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --pos_bad_pix_rej */
    p = cpl_parameter_new_value("kmos.kmos_dark.pos_bad_pix_rej", 
            CPL_TYPE_DOUBLE, "The positive rejection threshold for bad pixels",
            "kmos.kmos_dark", 50.0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "pos_bad_pix_rej");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --neg_bad_pix_rej */
    p = cpl_parameter_new_value("kmos.kmos_dark.neg_bad_pix_rej",
            CPL_TYPE_DOUBLE, "The negative rejection threshold for bad pixels",
            "kmos.kmos_dark", 50.0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "neg_bad_pix_rej");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --file_extension */
    p = cpl_parameter_new_value("kmos.kmos_dark.file_extension", CPL_TYPE_BOOL,
            "Controls if EXPTIME should be appended to product filenames",
            "kmos.kmos_dark", FALSE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "file_extension");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    return kmos_combine_pars_create(recipe->parameters, "kmos.kmos_dark", 
            DEF_REJ_METHOD, FALSE);
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int kmos_dark_exec(cpl_plugin *plugin)
{
    cpl_recipe  *recipe;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else return -1;

    return kmos_dark(recipe->parameters, recipe->frames);
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Destroy what has been created by the 'create' function
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int kmos_dark_destroy(cpl_plugin *plugin)
{
    cpl_recipe *recipe;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else return -1 ;

    cpl_parameterlist_delete(recipe->parameters);
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Interpret the command line options and execute the data processing
  @param    parlist     the parameters list
  @param    frameset   the frames list
  @return   0 if everything is ok

  Possible _cpl_error_code_ set in this function:
  @li CPL_ERROR_ILLEGAL_INPUT   if frames of wrong KMOS format are provided
 */
/*----------------------------------------------------------------------------*/
static int kmos_dark(cpl_parameterlist * parlist, cpl_frameset * frameset)
{
    const cpl_parameter *   par ;
    int                     nx, ny, nz, next, ndit ;
    const char          *   cmethod ;
    const char          *   my_method ;
    cpl_frame           *   frame ;
    int                     file_extension, citer, cmin, cmax, oscan ;
    double                  pos_bad_pix_rej, neg_bad_pix_rej, cneg_rej,
                            cpos_rej, exptime, gain, qc_dark, qc_dark_median, 
                            qc_readnoise, qc_readnoise_median, qc_bad_pix_num ;
    char                *   filename ;
    char                *   filename_bad ;
    char                *   extname ;
    char                *   exptimeStr ;
    cpl_imagelist       *   detector_in_window ;
    cpl_image           *   img_in ;
    cpl_image           *   img_in_oscan ;
    cpl_image           *   img_in_window ;
    cpl_image           *   combined_data_window ;
    cpl_image           *   combined_data ;
    cpl_image           *   combined_noise_window ;
    cpl_image           *   combined_noise ;
    cpl_image           *   bad_pix_mask_window ;
    cpl_image           *   bad_pix_mask ;
    cpl_propertylist    *   sub_header ;
    int                     i ;
    
    /* Check entries */
    if (parlist == NULL || frameset == NULL) {
        cpl_msg_error(__func__, "Null Inputs") ;
        cpl_error_set(__func__, CPL_ERROR_NULL_INPUT) ;
        return -1 ;
    }

    /* Inistialise */
    nx = ny = next = -1 ;
    exptime = -1.0 ;

    /* Get Parameters */
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_dark.oscan");
    oscan = cpl_parameter_get_bool(par);
    par=cpl_parameterlist_find_const(parlist, "kmos.kmos_dark.pos_bad_pix_rej");
    pos_bad_pix_rej = cpl_parameter_get_double(par);
    par=cpl_parameterlist_find_const(parlist, "kmos.kmos_dark.neg_bad_pix_rej");
    neg_bad_pix_rej = cpl_parameter_get_double(par);
    par = cpl_parameterlist_find_const(parlist,"kmos.kmos_dark.file_extension");
    file_extension = cpl_parameter_get_bool(par);
    kmos_combine_pars_load(parlist, "kmos.kmos_dark", &cmethod, &cpos_rej,
       &cneg_rej, &citer, &cmin, &cmax, FALSE);

    /* Identify the RAW and CALIB frames in the input frameset */
    if (kmo_dfs_set_groups(frameset) != 1) {
        cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }

    /* Check the inputs consistency */
    if (kmos_dark_check_inputs(frameset, &nx, &ny, &ndit, &next,&exptime) != 1){
        cpl_msg_error(__func__, "Input frameset is not consistent") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }

    /* Use average method in case there is only 1 input */
    my_method = cmethod;
    if (cpl_frameset_count_tags(frameset, DARK) == 1) {
        cpl_msg_warning(cpl_func, 
                "cmethod is set to 'average' since there is only 1 input");
        my_method = "average";
    }

    /* Compute output file names */
    if (file_extension) {
        /* Delete trailing zeros  */
        /* If zero right after decimal point,delete point as well */
        exptimeStr = cpl_sprintf("%g", exptime);
        char *p = 0;
        for(p=exptimeStr; *p; ++p) {
            if('.' == *p) {
                while(*++p);
                while('0'==*--p) *p = '\0';
                if(*p == '.') *p = '\0';
                break;
            }
        }
        filename = cpl_sprintf("%s_%s", MASTER_DARK, exptimeStr);
        filename_bad = cpl_sprintf("%s_%s", BADPIXEL_DARK, exptimeStr);
        cpl_free(exptimeStr);
    } else {
        filename = cpl_sprintf("%s", MASTER_DARK);
        filename_bad = cpl_sprintf("%s", BADPIXEL_DARK);
    }

    /* Create primary header products */
    frame = kmo_dfs_get_frame(frameset, DARK);
    kmo_dfs_save_main_header(frameset, filename, "", frame, NULL, parlist, 
            cpl_func);
    kmo_dfs_save_main_header(frameset, filename_bad, "", frame, NULL, parlist, 
            cpl_func);

    /* Loop on detectors */
    for (i = 1; i <= next ; i++) {
        cpl_msg_info(cpl_func, "Processing detector No. %d", i);
        cpl_msg_indent_more() ;

        detector_in_window = cpl_imagelist_new();
        frame = kmo_dfs_get_frame(frameset, DARK);
        nz = 0;
        while (frame != NULL) {
            /* Load current detector DARK frames into an imagelist */
            img_in = cpl_image_load(cpl_frame_get_filename(frame),
                    CPL_TYPE_FLOAT, 0, i) ;
            if (img_in == NULL) {
                cpl_free(filename) ;
                cpl_free(filename_bad) ;
                cpl_imagelist_delete(detector_in_window) ;
                cpl_msg_error(__func__, "Cannot load frame %d", nz+1) ;
                cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
                cpl_msg_indent_less() ;
                return -1 ;
            }
           
            /* Overscan Correction */
            if (oscan) {
                img_in_oscan = kmos_oscan_correct(img_in) ;
                if (img_in_oscan == NULL) {
                    cpl_free(filename) ;
                    cpl_free(filename_bad) ;
                    cpl_imagelist_delete(detector_in_window) ;
                    cpl_image_delete(img_in);
                    cpl_msg_error(__func__, "Cannot correct Overscan") ;
                    cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
                    cpl_msg_indent_less() ;
                    return -1 ;
                }
                cpl_image_delete(img_in) ;
                img_in = img_in_oscan ;
            }

            /* Cut the borderѕ */
            img_in_window = cpl_image_extract(img_in, KMOS_BADPIX_BORDER+1,
                    KMOS_BADPIX_BORDER+1, nx-KMOS_BADPIX_BORDER,
                    ny-KMOS_BADPIX_BORDER) ;
            cpl_image_delete(img_in) ;
 
            cpl_imagelist_set(detector_in_window, img_in_window, nz);
            nz++;
                    
            /* Get next DARK frame */
            frame = kmo_dfs_get_frame(frameset, NULL);
        }

        /* Combine imagelist and create noise */
        kmos_combine_frames(detector_in_window, my_method, 
                cpos_rej, cneg_rej, citer, cmax, cmin, &combined_data_window, 
                &combined_noise_window, -1.0);
        cpl_imagelist_delete(detector_in_window) ;

        if (kmclipm_omit_warning_one_slice > 10) 
            kmclipm_omit_warning_one_slice = FALSE;
    
        /* Calculate preliminary mean and stdev to create the BPM */
        qc_dark = cpl_image_get_mean(combined_data_window);

        /* Check the noise frame (NULL or ALL pixels are bad) */
        if (combined_noise_window == NULL ||
                cpl_image_count_rejected(combined_noise_window) ==
                cpl_image_get_size_x(combined_noise_window)*
                cpl_image_get_size_y(combined_noise_window)) {
            qc_readnoise = cpl_image_get_stdev(combined_data_window);
        } else {
            if (nz > 2)         
                qc_readnoise = cpl_image_get_mean(combined_noise_window);
            else if (nz == 2)  
                qc_readnoise = cpl_image_get_stdev(combined_noise_window);
            else if (nz == 1)
                qc_readnoise = cpl_image_get_stdev(combined_data_window);
            else {
                cpl_msg_error(__func__, "Not enough frames: %d", nz) ;
                cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
                cpl_free(filename) ;
                cpl_free(filename_bad) ;
                cpl_image_delete(combined_data_window);
                cpl_image_delete(combined_noise_window);
                cpl_msg_indent_less() ;
                return -1 ;
            }
        }

        /* Create bad-pixel-mask */
        qc_bad_pix_num = kmo_create_bad_pix_dark(combined_data_window, 
                qc_dark, qc_readnoise, pos_bad_pix_rej, neg_bad_pix_rej, 
                &bad_pix_mask_window);

        sub_header = kmo_dfs_load_sub_header(frameset, DARK, i, FALSE);
        kmclipm_update_property_int(sub_header, QC_NR_BAD_PIX, qc_bad_pix_num, 
                "[] nr. of bad pixels");

        /* Calculate QC.DARK, QC.READNOISE, QC.DARK.MEDIAN, */
        /* QC.READNOISE.MEDIAN, QC.DARKCUR */

        /* Badpixels from combined_data_window are already rejected */
        /* by kmo_create_bad_pix_dark() */
        kmo_image_reject_from_mask(combined_noise_window, bad_pix_mask_window);
        qc_dark = cpl_image_get_mean(combined_data_window);
        qc_dark_median = cpl_image_get_median(combined_data_window);

        /* Check the noise frame (NULL or ALL pixels are bad) */
        /* Calculate mean and stddev of combined frames (with rejection) */
        if (combined_noise_window == NULL ||
                cpl_image_count_rejected(combined_noise_window) ==
                cpl_image_get_size_x(combined_noise_window)*
                cpl_image_get_size_y(combined_noise_window)) {
            qc_readnoise = cpl_image_get_stdev(combined_data_window);
            qc_readnoise_median = 
                kmo_image_get_stdev_median(combined_data_window);
        } else {
            if (nz > 2) {         
                qc_readnoise = 
                    cpl_image_get_mean(combined_noise_window) * sqrt(nz) ;
                qc_readnoise_median =
                    cpl_image_get_median(combined_noise_window) * sqrt(nz);
            } else if (nz == 2) {   
                qc_readnoise = 
                    cpl_image_get_stdev(combined_noise_window) * sqrt(nz) ;
                qc_readnoise_median = sqrt(nz) *
                    kmo_image_get_stdev_median(combined_noise_window) ;
            } else if (nz == 1) {
                qc_readnoise = cpl_image_get_stdev(combined_data_window);
                qc_readnoise_median =
                    kmo_image_get_stdev_median(combined_data_window);
            } else {
                cpl_msg_error(__func__, "Not enough frames: %d", nz) ;
                cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
                cpl_free(filename) ;
                cpl_free(filename_bad) ;
                cpl_image_delete(combined_data_window);
                cpl_image_delete(combined_noise_window);
                cpl_image_delete(bad_pix_mask_window);
                cpl_propertylist_delete(sub_header);
                cpl_msg_indent_less() ;
                return -1 ;
            }
        }

        kmclipm_update_property_double(sub_header, QC_DARK, qc_dark, 
                "[adu] mean of master dark");
        kmclipm_update_property_double(sub_header, QC_READNOISE, qc_readnoise, 
                "[adu] mean noise of master dark");
        kmclipm_update_property_double(sub_header, QC_DARK_MEDIAN, 
                qc_dark_median, "[adu] median of master dark");
        kmclipm_update_property_double(sub_header, QC_READNOISE_MEDIAN, 
                qc_readnoise_median, "[adu] median noise of master dark");

        /* Load gain */
        gain = kmo_dfs_get_property_double(sub_header, GAIN);
        if (cpl_error_get_code() != CPL_ERROR_NONE) {
            cpl_msg_error(__func__, "GAIN is missing in header") ;
            cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
            cpl_free(filename) ;
            cpl_free(filename_bad) ;
            cpl_image_delete(combined_data_window);
            cpl_image_delete(combined_noise_window);
            cpl_image_delete(bad_pix_mask_window);
            cpl_propertylist_delete(sub_header);
            cpl_msg_indent_less() ;
            return -1 ;
        }

        kmclipm_update_property_double(sub_header, QC_DARK_CURRENT, 
                qc_dark / exptime / gain, "[e-/s] dark current");

        /* Save dark frame */
        extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
        kmclipm_update_property_string(sub_header, EXTNAME, extname, 
                "FITS extension name");
        cpl_free(extname);

        combined_data = kmo_add_bad_pix_border(combined_data_window, TRUE);
        cpl_image_delete(combined_data_window);

        kmo_dfs_save_image(combined_data, filename, "", sub_header, 0./0.);
        cpl_image_delete(combined_data);

        /* Save noise frame */
        extname = kmo_extname_creator(detector_frame, i, EXT_NOISE);
        kmclipm_update_property_string(sub_header, EXTNAME, extname, 
                "FITS extension name");
        cpl_free(extname);

        combined_noise = kmo_add_bad_pix_border(combined_noise_window, TRUE);
        cpl_image_delete(combined_noise_window);

        kmo_dfs_save_image(combined_noise, filename, "", sub_header, 0./0.);
        cpl_image_delete(combined_noise);

        /* Save bad_pix frame */
        extname = kmo_extname_creator(detector_frame, i, EXT_BADPIX);
        kmclipm_update_property_string(sub_header, EXTNAME, extname, 
                "FITS extension name");
        cpl_free(extname);

        bad_pix_mask = kmo_add_bad_pix_border(bad_pix_mask_window, FALSE);
        cpl_image_delete(bad_pix_mask_window);

        kmo_dfs_save_image(bad_pix_mask, filename_bad, "", sub_header, 0.);
        cpl_image_delete(bad_pix_mask);
        
        cpl_propertylist_delete(sub_header);

        cpl_msg_indent_less() ;
    } 

    /* Free and Return */
    cpl_free(filename);
    cpl_free(filename_bad);
    return CPL_ERROR_NONE;
}

/**@}*/

/*----------------------------------------------------------------------------*/
/**
  @brief    Make consistency checks
  @param    frameset        Set of frames
  @param    nx          [out] images x size
  @param    ny          [out] images x size
  @param    ndit        [out] NDIT
  @param    next        [out] Nb extensions
  @param    exptime_on  [out] exptime
  @return   1 if consistent, 0 if not, -1 in error case
 */
/*----------------------------------------------------------------------------*/
static int kmos_dark_check_inputs(
        cpl_frameset        *   frameset,
        int                 *   nx,
        int                 *   ny,
        int                 *   ndit,
        int                 *   next,
        double              *   exptime)
{
    cpl_frame           *   frame ;
    cpl_propertylist    *   eh ;
    cpl_propertylist    *   main_header ;
    int                     nx_cur, ny_cur, ndit_cur, ne_cur ;
    double                  exptime_cur ;
    int                     i, j ;

    /* Check Entries */
    if (nx == NULL || ny == NULL || frameset == NULL || exptime == NULL ||
            ndit == NULL || next == NULL) {
        return -1;
    }

    /* More than 3 frames is recommended */
    if (cpl_frameset_count_tags(frameset, DARK) < 3) {
        cpl_msg_warning(cpl_func, "3 DARK frames or more are recommended");
    }

    /* Loop on the frames - Check Main Headers consistency */
    i = 0;
    frame = kmo_dfs_get_frame(frameset, DARK);
    while (frame != NULL) {
        /* Get Frame nb of extensions */
        ne_cur = cpl_frame_get_nextensions(frame);

        /* Read Frame header */
        main_header = cpl_propertylist_load(cpl_frame_get_filename(frame),0);
        ndit_cur = kmos_pfits_get_ndit(main_header) ;
        exptime_cur = kmos_pfits_get_exptime(main_header) ;
        cpl_propertylist_delete(main_header) ;

        if (cpl_error_get_code() != CPL_ERROR_NONE) {
            cpl_msg_error(__func__, "Cannot retrieve keywords from header") ;
            return -1 ;
        }

        if (i == 0) {
            *exptime = exptime_cur ;
            *ndit = ndit_cur ;
            *next = ne_cur ;
        } else {
            if (ndit_cur != *ndit || ne_cur != *next || 
                    fabs(exptime_cur-(*exptime)) >1e-3) {
                cpl_msg_error(__func__, "Header keywords are inconsistent") ;
                return -1 ;
            }
        }

        /* Get next DARK frame */
        frame = kmo_dfs_get_frame(frameset, NULL);
        i++;
    }

    /* Loop on the frames - Check Extension Headers consistency */
    i = 0;
    frame = kmo_dfs_get_frame(frameset, DARK);
    while (frame != NULL) {
        /* Loop on extensions */
        for (j=1 ; j<=*next ; j++) {
            /* Read extension header */
            eh = cpl_propertylist_load(cpl_frame_get_filename(frame), j);
            nx_cur = kmos_pfits_get_naxis1(eh) ;
            ny_cur = kmos_pfits_get_naxis2(eh) ;
            cpl_propertylist_delete(eh) ;
            if (cpl_error_get_code() != CPL_ERROR_NONE) {
                cpl_msg_error(__func__, "Cannot retrieve keywords from header");
                return -1 ;
            }

            if (i == 0 && j == 1) {
                *nx = nx_cur ;
                *ny = ny_cur ;
            } else {
                if (nx_cur != *nx || ny_cur != *ny) {
                    cpl_msg_error(__func__, "Header keywords are inconsistent");
                    return -1 ;
                }
            }
        }

        /* Get next DARK frame */
        frame = kmo_dfs_get_frame(frameset, NULL);
        i++;
    }

    /* Check Sizeѕ */
    if (*nx <= 2*KMOS_BADPIX_BORDER || *ny <= 2*KMOS_BADPIX_BORDER) {
        cpl_msg_error(__func__, "Input frames x/y size must be > 9 pixels");
        return -1 ;
    }

    /* Return */
    return 1 ;
}



