/*
 * 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

#include <string.h>

#include <cpl.h>

#include "kmo_utils.h"
#include "kmo_dfs.h"
#include "kmo_error.h"
#include "kmo_priv_noise_map.h"
#include "kmo_constants.h"
#include "kmo_debug.h"

static int kmo_noise_map_create(cpl_plugin *);
static int kmo_noise_map_exec(cpl_plugin *);
static int kmo_noise_map_destroy(cpl_plugin *);
static int kmo_noise_map(cpl_parameterlist *, cpl_frameset *);

static char kmo_noise_map_description[] =
"The noise in each pixel of the input data is estimated using gain and readnoise.\n"
"The readnoise is expected to be in the primary header (ESO DET CHIP RON), the\n"
"gain (ESO DET CHIP GAIN) has to be in each of the subsequent headers of each \n"
"detector frame. The output is the initial noise map of the data frame.\n"
"\n"
"-------------------------------------------------------------------------------\n"
"  Input files:\n"
"\n"
"   DO                    KMOS                                                  \n"
"   category              Type   Explanation                    Required #Frames\n"
"   --------              -----  -----------                    -------- -------\n"
"   <none or any>         RAW    raw data frame                     Y       1   \n"
"\n"
"  Output files:\n"
"\n"
"   DO                    KMOS\n"
"   category              Type   Explanation\n"
"   --------              -----  -----------\n"
"   NOISE_MAP             F2D    Initial noise map\n"
"                                (6 Extensions, 3 data and 3 noise)\n"
"-------------------------------------------------------------------------------\n"
"\n";

/**
 * @defgroup kmo_noise_map kmo_noise_map Create noise map.
 *
 * See recipe description for details.
 */

/**@{*/

/**
  @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,
                        "kmo_noise_map",
                        "Generate a noise map from a raw frame",
                        kmo_noise_map_description,
                        "Alex Agudo Berbel",
                        "usd-help@eso.org",
                        kmos_get_license(),
                        kmo_noise_map_create,
                        kmo_noise_map_exec,
                        kmo_noise_map_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 kmo_noise_map_create(cpl_plugin *plugin)
{
    cpl_recipe *recipe;

    /* 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();

    return 0;
}

/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
*/
static int kmo_noise_map_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 kmo_noise_map(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 kmo_noise_map_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_NULL_INPUT         if no input file is defined,
                                     if the frameset is empty,
                                     if second operand not valid
    @li CPL_ERROR_ILLEGAL_INPUT      if einput fits-file isn't of type RAW,
                                     F2D or F2I,
                                     if noise is already present in input data
    @li CPL_ERROR_DATA_NOT_FOUND     if GAIN- or RON-keyword is missing in
                                     fits-header

*/
static int kmo_noise_map(cpl_parameterlist *parlist, cpl_frameset *frameset)
{
    int                 i                   = 0,
                        ret_val             = 0,
                        ndsamples           = 0;
    cpl_propertylist    *sub_header         = NULL,
                        *main_header        = NULL;
    cpl_image           *img                = NULL,
                        *noise_img          = NULL;
    double              gain                = 0.0,
                        readnoise           = 0.0;
    main_fits_desc      desc;
    cpl_frame           *frame              = NULL;
    const char          *readmode           = NULL;

    KMO_TRY
    {
        kmo_init_fits_desc(&desc);

        /* --- check input --- */
        KMO_TRY_ASSURE((parlist != NULL) &&
                       (frameset != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(cpl_frameset_get_size(frameset) == 1,
                       CPL_ERROR_NULL_INPUT,
                       "A fits-file must be provided!");

        KMO_TRY_EXIT_IF_NULL(
            frame = kmo_dfs_get_frame(frameset, "0"));

        desc = kmo_identify_fits_header(
                        cpl_frame_get_filename(frame));
        KMO_TRY_CHECK_ERROR_STATE_MSG("Provided fits file doesn't seem to be "
                                      "in KMOS-format!");

        KMO_TRY_ASSURE((desc.fits_type == raw_fits),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Input data hasn't correct data type "
                       "(KMOSTYPE must be RAW)!");

        KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset) == 1,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Cannot identify RAW and CALIB frames!");

        cpl_msg_info("", "--- Parameter setup for kmo_noise_map ----");

        cpl_msg_info("", "No parameters to set.");
        cpl_msg_info("", "-------------------------------------------");

        /* --- save primary extension --- */
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_save_main_header(frameset, NOISE_MAP, "", frame,
                                     NULL, parlist, cpl_func));

        /* --- load each data-frame, save it, calculate noise,
               save it as well --- */
        for (i = 0; i < desc.nr_ext; i++)
        {
            /* load data and save it away again */
            KMO_TRY_EXIT_IF_NULL(
                sub_header = kmo_dfs_load_sub_header(frameset, "0", i + 1,
                                                     FALSE));

            KMO_TRY_EXIT_IF_NULL(
                img = kmo_dfs_load_image(frameset, "0", i + 1, FALSE, TRUE, NULL));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(sub_header,
                                        FALSE,
                                        FALSE,
                                        desc.frame_type,
                                        desc.sub_desc[i].device_nr));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_dfs_save_image(img, NOISE_MAP, "", sub_header, 0./0.));

            /* calculate initial noise estimate */
            KMO_TRY_EXIT_IF_NULL(
                main_header = kmo_dfs_load_primary_header(frameset, "0"));

            readmode = cpl_propertylist_get_string(main_header, READMODE);
            KMO_TRY_CHECK_ERROR_STATE("ESO DET READ CURNAME keyword in main "
                                      "header missing!");
            gain = kmo_dfs_get_property_double(sub_header, GAIN);
            KMO_TRY_CHECK_ERROR_STATE_MSG(
                "GAIN-keyword in fits-header is missing!");

            if (strcmp(readmode, "Nondest") == 0) {
                // NDR: non-destructive readout mode
                ndsamples = cpl_propertylist_get_int(main_header, NDSAMPLES);
                KMO_TRY_CHECK_ERROR_STATE("ESO DET READ NDSAMPLES keyword in main "
                                          "header missing!");

                readnoise = kmo_calc_readnoise_ndr(ndsamples);
                KMO_TRY_CHECK_ERROR_STATE();
            } else {
                // normal readout mode
                readnoise = kmo_dfs_get_property_double(sub_header, RON);
                KMO_TRY_CHECK_ERROR_STATE_MSG(
                    "READNOISE-keyword in fits-header is missing!");

            }
            KMO_TRY_EXIT_IF_NULL(
                noise_img = kmo_calc_noise_map(img, gain, readnoise));

            cpl_propertylist_delete(main_header); main_header = NULL;

            /* save noise */
            KMO_TRY_EXIT_IF_ERROR(
                kmo_update_sub_keywords(sub_header,
                                        TRUE,
                                        FALSE,
                                        desc.frame_type,
                                        desc.sub_desc[i].device_nr));

            KMO_TRY_EXIT_IF_ERROR(
                kmo_dfs_save_image(noise_img, NOISE_MAP, "", sub_header, 0./0.));

            cpl_propertylist_delete(sub_header); sub_header = NULL;
            cpl_image_delete(img); img = NULL;
            cpl_image_delete(noise_img); noise_img= NULL;
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();

        ret_val = -1;
    }

    cpl_propertylist_delete(sub_header); sub_header = NULL;
    cpl_image_delete(img); img = NULL;
    cpl_image_delete(noise_img); noise_img = NULL;
    kmo_free_fits_desc(&desc);
    return ret_val;
}

/**@}*/
