/* $Id: fors_flatfield.c,v 1.6 2013-04-24 14:14:13 cgarcia Exp $
 *
 * This file is part of the FORS Data Reduction Pipeline
 * 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-04-24 14:14:13 $
 * $Revision: 1.6 $
 * $Name: not supported by cvs2svn $
 */

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

#include <math.h>
#include <cpl.h>
#include <moses.h>
#include <fors_dfs.h>

static int fors_flatfield_create(cpl_plugin *);
static int fors_flatfield_exec(cpl_plugin *);
static int fors_flatfield_destroy(cpl_plugin *);
static int fors_flatfield(cpl_parameterlist *, cpl_frameset *);

static char fors_flatfield_description[] =
"This recipe is used to divide the input frame by the normalised flat\n"
"field frame produced by recipe fors_normalise_flat. The input frame must\n"
"be already bias subtracted (e.g., by recipe fors_remove_bias).\n"
"In the table below the MXU acronym can be alternatively read as MOS and\n"
"LSS.\n\n"
"Input files:\n\n"
"  DO category:               Type:       Explanation:         Required:\n"
"  SCIENCE_UNBIAS_MXU\n"
"  or STANDARD_UNBIAS_MXU     Raw         Bias subtracted frame   Y\n"
"  MASTER_NORM_FLAT_MXU       Calib       Normalised flat frame   Y\n\n"
"Output files:\n\n"
"  DO category:               Data type:  Explanation:\n"
"  SCIENCE_UNFLAT_MXU\n"
"  or STANDARD_UNFLAT_MXU     FITS image  Flat field corrected frame\n\n";

#define fors_flatfield_exit(message)          \
{                                             \
if ((const char *)message != NULL) cpl_msg_error(recipe, message);  \
cpl_image_delete(raw_image);                  \
cpl_image_delete(norm_flat);                  \
cpl_propertylist_delete(header);              \
cpl_msg_indent_less();                        \
return -1;                                    \
}

#define fors_flatfield_exit_memcheck(message) \
{                                               \
if ((const char *)message != NULL) cpl_msg_info(recipe, message);     \
printf("free raw_image (%p)\n", raw_image);     \
cpl_image_delete(raw_image);                    \
printf("free norm_flat (%p)\n", norm_flat);     \
cpl_image_delete(norm_flat);                    \
printf("free header (%p)\n", header);           \
cpl_propertylist_delete(header);                \
cpl_msg_indent_less();                          \
return 0;                                       \
}


/**
 * @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,
                    FORS_BINARY_VERSION,
                    CPL_PLUGIN_TYPE_RECIPE,
                    "fors_flatfield",
                    "Flat field correction of input frame",
                    fors_flatfield_description,
                    "Carlo Izzo",
                    PACKAGE_BUGREPORT,
    "This file is currently part of the FORS Instrument Pipeline\n"
    "Copyright (C) 2002-2010 European Southern Observatory\n\n"
    "This program is free software; you can redistribute it and/or modify\n"
    "it under the terms of the GNU General Public License as published by\n"
    "the Free Software Foundation; either version 2 of the License, or\n"
    "(at your option) any later version.\n\n"
    "This program is distributed in the hope that it will be useful,\n"
    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
    "GNU General Public License for more details.\n\n"
    "You should have received a copy of the GNU General Public License\n"
    "along with this program; if not, write to the Free Software Foundation,\n"
    "Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n",
                    fors_flatfield_create,
                    fors_flatfield_exec,
                    fors_flatfield_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 fors_flatfield_create(cpl_plugin *plugin)
{
    cpl_recipe    *recipe;
/* Uncomment in case parameters are defined
    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 (empty) parameters list in the cpl_recipe object 
     */

    recipe->parameters = cpl_parameterlist_new(); 

    return 0;
}


/**
 * @brief    Execute the plugin instance given by the interface
 *
 * @param    plugin  the plugin
 *
 * @return   0 if everything is ok
 */

static int fors_flatfield_exec(cpl_plugin *plugin)
{
    cpl_recipe *recipe;
    
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else 
        return -1;

    return fors_flatfield(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 fors_flatfield_destroy(cpl_plugin *plugin)
{
    cpl_recipe *recipe;
    
    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 set-of-frames
 *
 * @return   0 if everything is ok
 */

static int fors_flatfield(cpl_parameterlist *parlist, cpl_frameset *frameset)
{

    const char *recipe = "fors_flatfield";


    /*
     * CPL objects
     */

    cpl_image        *raw_image   = NULL;
    cpl_image        *norm_flat   = NULL;
    cpl_propertylist *header      = NULL;

    /*
     * Auxiliary variables
     */

    char        version[80];
    const char *norm_flat_tag;
    const char *raw_image_tag;
    const char *pro_image_tag;
    char       *instrume = NULL;
    int         science_mxu;
    int         science_mos;
    int         science_lss;
    int         standard_mxu;
    int         standard_mos;
    int         standard_lss;
    int         nflat, nframe;


    cpl_msg_set_indentation(2);


    cpl_msg_info(recipe, "Check input set-of-frames:");
    cpl_msg_indent_more();

    nframe  = science_mxu  = cpl_frameset_count_tags(frameset, 
                                                     "SCIENCE_UNBIAS_MXU");
    nframe += science_mos  = cpl_frameset_count_tags(frameset, 
                                                     "SCIENCE_UNBIAS_MOS");
    nframe += science_lss  = cpl_frameset_count_tags(frameset, 
                                                     "SCIENCE_UNBIAS_LSS");
    nframe += standard_mxu = cpl_frameset_count_tags(frameset, 
                                                     "STANDARD_UNBIAS_MXU");
    nframe += standard_mos = cpl_frameset_count_tags(frameset, 
                                                     "STANDARD_UNBIAS_MOS");
    nframe += standard_lss = cpl_frameset_count_tags(frameset, 
                                                     "STANDARD_UNBIAS_LSS");

    if (nframe == 0) {
        fors_flatfield_exit("Missing required input scientific frame");
    }
    if (nframe > 1) {
        cpl_msg_error(recipe, "Too many input scientific frames (%d > 1)", 
                      nframe);
        fors_flatfield_exit(NULL);
    }

    if (science_mxu) {
        norm_flat_tag = "MASTER_NORM_FLAT_MXU";
        pro_image_tag = "SCIENCE_UNFLAT_MXU";
        raw_image_tag = "SCIENCE_UNBIAS_MXU";
    }
    else if (science_mos) {
        norm_flat_tag = "MASTER_NORM_FLAT_MOS";
        pro_image_tag = "SCIENCE_UNFLAT_MOS";
        raw_image_tag = "SCIENCE_UNBIAS_MOS";
    }
    else if (science_lss) {
        norm_flat_tag = "MASTER_NORM_FLAT_LSS";
        pro_image_tag = "SCIENCE_UNFLAT_LSS";
        raw_image_tag = "SCIENCE_UNBIAS_LSS";
    }
    else if (standard_mxu) {
        norm_flat_tag = "MASTER_NORM_FLAT_MXU";
        pro_image_tag = "STANDARD_UNFLAT_MXU";
        raw_image_tag = "STANDARD_UNBIAS_MXU";
    }
    else if (standard_mos) {
        norm_flat_tag = "MASTER_NORM_FLAT_MOS";
        pro_image_tag = "STANDARD_UNFLAT_MOS";
        raw_image_tag = "STANDARD_UNBIAS_MOS";
    }
    else if (standard_lss) {
        norm_flat_tag = "MASTER_NORM_FLAT_LSS";
        pro_image_tag = "STANDARD_UNFLAT_LSS";
        raw_image_tag = "STANDARD_UNBIAS_LSS";
    }

    nflat = cpl_frameset_count_tags(frameset, norm_flat_tag);
    if (nflat == 0) {
        cpl_msg_error(recipe, "Missing required input: %s", norm_flat_tag);
        fors_flatfield_exit(NULL);
    }
    if (nflat > 1) {
        cpl_msg_error(recipe, "Too many in input (%d > 1): %s",
                      nflat, norm_flat_tag);
        fors_flatfield_exit(NULL);
    }

    if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
        cpl_msg_warning(cpl_func,"Input frames are not from the same grism");

    if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
        cpl_msg_warning(cpl_func,"Input frames are not from the same filter");

    if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID")) 
        cpl_msg_warning(cpl_func,"Input frames are not from the same chip");

    header = dfs_load_header(frameset, raw_image_tag, 0);

    if (header == NULL) {
        cpl_msg_error(recipe, "Cannot load header of %s frame", raw_image_tag);
        fors_flatfield_exit(NULL);
    }

    instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
    if (instrume == NULL) {
        cpl_msg_error(recipe, "Missing keyword INSTRUME in %s header", 
                      raw_image_tag);
        fors_flatfield_exit(NULL);
    }

    if (instrume[4] == '1')
        snprintf(version, 80, "%s/%s", "fors1", VERSION);
    if (instrume[4] == '2')
        snprintf(version, 80, "%s/%s", "fors2", VERSION);

    cpl_msg_indent_less();
    cpl_msg_info(recipe, "Load input frames:");
    cpl_msg_indent_more();

    norm_flat = dfs_load_image(frameset, norm_flat_tag, CPL_TYPE_FLOAT, 0, 1);
    if (norm_flat == NULL)
        fors_flatfield_exit("Cannot load normalised flat field");

    raw_image = dfs_load_image(frameset, raw_image_tag, CPL_TYPE_FLOAT, 0, 0);
    if (raw_image == NULL) {
        cpl_msg_error(recipe, "Cannot load %s frame", raw_image_tag);
        fors_flatfield_exit(NULL);
    }

    cpl_msg_indent_less();
    cpl_msg_info(recipe, "Divide input %s by flat field...", raw_image_tag);
    cpl_msg_indent_more();

    if (cpl_image_divide(raw_image, norm_flat) != CPL_ERROR_NONE) {
        cpl_msg_error(recipe, "Failure of flat field correction: %s",
                      cpl_error_get_message());
        fors_flatfield_exit(NULL);
    }
    cpl_image_delete(norm_flat); norm_flat = NULL;

    cpl_msg_indent_less();

    if (dfs_save_image(frameset, raw_image, pro_image_tag,
                       header, parlist, recipe, version))
        fors_flatfield_exit(NULL);

    cpl_propertylist_delete(header); header = NULL;
    cpl_image_delete(raw_image); raw_image = NULL;

    return 0;
}
