/* $Id: fors_dark_impl.c,v 1.18 2013-09-10 19:12:03 cgarcia Exp $
 *
 * This file is part of the FORS Library
 * Copyright (C) 2002-2010 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

/*
 * $Author: cgarcia $
 * $Date: 2013-09-10 19:12:03 $
 * $Revision: 1.18 $
 * $Name: not supported by cvs2svn $
 */

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

#include <memory>
#include <fors_dark_impl.h>

#include <fors_stack.h>
#include <fors_stats.h>
#include <fors_dfs.h>
#include <fors_utils.h>
#include "fiera_config.h"
#include "fors_detmodel.h"
#include "fors_ccd_config.h"
#include "fors_overscan.h"
#include "fors_subtract_bias.h"
#include "fors_bpm.h"

#include <cpl.h>

/**
 * @addtogroup fors_dark
 */

/**@{*/

const char *const fors_dark_name = "fors_dark";
const char *const fors_dark_description_short = "Compute master dark frame";
const char *const fors_dark_author = "Jonas M. Larsen";
const char *const fors_dark_email = PACKAGE_BUGREPORT;
const char *const fors_dark_description =
"This recipe is used to combine input raw DARK frames into a master dark\n"
"frame by subtracing the master bias and using the given stacking method.\n"
"The overscan regions, if present, are removed from the result.\n"
"\n"
"Input files:\n"
"\n"
"  DO category:               Type:       Explanation:         Required:\n"
"  DARK                       Raw         Dark frame              Y\n"
"  MASTER_BIAS                FITS image  Master bias             Y\n"
"\n"
"Output files:\n"
"\n"
"  DO category:               Data type:  Explanation:\n"
"  MASTER_DARK                FITS image  Master dark frame\n"
"\n";

/**
 * @brief    Define recipe parameters
 * @param    parameters     parameter list to fill
 */
void fors_dark_define_parameters(cpl_parameterlist *parameters)
{
    char *context = cpl_sprintf("fors.%s", fors_dark_name);
    
    fors_stack_define_parameters(parameters, context, "median");

    cpl_free((void *)context);

    return;
}

#undef cleanup
#define cleanup \
do { \
    cpl_frameset_delete(dark_frames); \
    cpl_frameset_delete(master_bias_frame); \
    fors_image_delete(&master_bias); \
    fors_stack_method_delete(&sm); \
    fors_image_delete(&master_dark); \
    fors_image_list_delete(&darks, fors_image_delete); \
    cpl_free((void *)context); \
    cpl_propertylist_delete(master_dark_header);\
} while (0)
/**
 * @brief    Do the processing
 *
 * @param    frames         input frames
 * @param    parameters     parameters
 *
 * @return   0 if everything is ok
 */

void fors_dark(cpl_frameset *frames, const cpl_parameterlist *parameters)
{
    /* Raw */
    cpl_frameset *dark_frames      = NULL;
    fors_image_list *darks   = NULL;

    /* Calibration */
    cpl_frameset *master_bias_frame = NULL;
    fors_image *master_bias   = NULL; 

    /* Product */
    fors_image *master_dark = NULL;

    /* Parameters */
    stack_method *sm    = NULL;

    cpl_propertylist * master_dark_header = NULL;

    /* Other */
    char *context = cpl_sprintf("fors.%s", fors_dark_name);

    /* Get parameters */
    sm = fors_stack_method_new(parameters, context);
    assure( !cpl_error_get_code(), return, "Could not get stacking method");
    
    /* Find raw */
    dark_frames = fors_frameset_extract(frames, DARK);
    std::auto_ptr<fors::fiera_config> dark_ccd_config = 
            fors::ccd_settings_equal(dark_frames);

    assure( cpl_frameset_get_size(dark_frames) > 0, return, 
            "No %s provided", DARK);

    /* Find calibration */
    master_bias_frame = fors_frameset_extract(frames, MASTER_BIAS);
    assure( cpl_frameset_get_size(master_bias_frame) == 1, return, 
            "One %s required. %"CPL_SIZE_FORMAT" found", 
            MASTER_BIAS, cpl_frameset_get_size(master_bias_frame));

    /* Update RON estimation from bias */
    cpl_propertylist * master_bias_header =
       cpl_propertylist_load(cpl_frame_get_filename(
               cpl_frameset_get_position(master_bias_frame, 0)), 0);
    fors::update_ccd_ron(*dark_ccd_config, master_bias_header);
    assure( !cpl_error_get_code(), return, "Could not get RON from master bias"
            " (missing QC DET OUT? RON keywords)");

    /* Load bias */
    master_bias = 
      fors_image_load(cpl_frameset_get_position(master_bias_frame, 0));
    
    assure( !cpl_error_get_code(), return, 
            "Could not load master bias");
    
    /* Load raw darks */
    fors_image_list * darks_raw = 
            fors_image_load_list(dark_frames);
    
    /* Check that the overscan configuration is consistent */
    bool perform_preoverscan = !fors_is_preoverscan_empty(*dark_ccd_config);

    assure(perform_preoverscan == 
           fors_is_master_bias_preoverscan_corrected(master_bias_header),
           return, "Master bias overscan configuration doesn't match science");
    cpl_propertylist_delete(master_bias_header);

    /* Create variances map */
    std::vector<double> overscan_levels; 
    if(perform_preoverscan)
        overscan_levels = 
            fors_get_bias_levels_from_overscan(fors_image_list_first(darks_raw), 
                                               *dark_ccd_config);
    else
        overscan_levels = fors_get_bias_levels_from_mbias(master_bias, 
                                                          *dark_ccd_config);
    fors_image_variance_from_detmodel(darks_raw, *dark_ccd_config, 
                                      overscan_levels);
    
    /* Subtract overscan */
    if(perform_preoverscan)
        darks = fors_subtract_prescan(darks_raw, *dark_ccd_config);
    else 
    {
        darks = fors_image_list_duplicate(darks_raw, fors_image_duplicate); 
        //The rest of the recipe assumes that the images carry a bpm.
        fors_bpm_image_list_make_explicit(darks); 
    }
    
    /* Trim pre/overscan */
    if(perform_preoverscan)
        fors_trimm_preoverscan(darks, *dark_ccd_config);

    master_dark_header= cpl_propertylist_new();

    /* QC Parameters - Dark Raws*/

    fors_write_images_mean_mean_in_propertylist(darks_raw, master_dark_header,
                                   "ESO QC DARK RAW MEAN MEAN");
    fors_write_images_median_mean_in_propertylist(darks_raw, master_dark_header,
                                    "ESO QC DARK RAW MEAN MEDIAN");
    fors_write_images_mean_stddev_in_propertylist(darks_raw, master_dark_header,
                                    "ESO QC DARK RAW STDEV MEAN");

    fors_image_list_delete(&darks_raw, fors_image_delete);

    /* Subtract master bias */
    fors_subtract_bias_imglist(darks, master_bias);
    assure( !cpl_error_get_code(), return, "Could not load dark images");

    /* Stack */
    master_dark = fors_stack_const(darks, sm);
    assure( !cpl_error_get_code(), return, "Dark stacking failed");
    
   /* QC Parameters - Master Dark */
   fors_write_max_in_propertylist(master_dark, master_dark_header,
                                  "ESO QC DARK MAX");
   fors_write_min_in_propertylist(master_dark, master_dark_header,
                                  "ESO QC DARK MIN");
   fors_write_mean_in_propertylist(master_dark, master_dark_header,
                                  "ESO QC DARK MEAN");
   fors_write_median_in_propertylist(master_dark, master_dark_header,
                                   "ESO QC DARK MEDIAN");
   fors_write_stdev_in_propertylist(master_dark, master_dark_header,
                                   "ESO QC DARK STDEV");
   fors_write_num_bad_pixels_propertylist(master_dark, master_dark_header,
                                   "ESO QC DARK NBADPX");

   /* Save product */
   fors_dfs_save_image_err(frames, master_dark, MASTER_DARK,
                       master_dark_header, NULL, parameters, fors_dark_name,
                       cpl_frameset_get_position(dark_frames, 0));
   assure( !cpl_error_get_code(), return, "Saving %s failed",
           MASTER_DARK);

    cleanup;
    return;
}

/**@}*/
