/* 
 * 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 <cpl.h>
#include <cpl_wcs.h>

#include "kmo_dfs.h"
#include "kmo_error.h"
#include "kmo_constants.h"
#include "kmo_priv_sky_tweak.h"

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

static int kmo_sky_tweak_create(cpl_plugin *);
static int kmo_sky_tweak_exec(cpl_plugin *);
static int kmo_sky_tweak_destroy(cpl_plugin *);
static int kmo_sky_tweak(cpl_parameterlist *, cpl_frameset *);

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

static char kmo_sky_tweak_description[] =
" This recipes is an advanced tool to remove OH sky lines.\n"
"\n"
"BASIC PARAMETERS:\n"
"-----------------\n"
"--tbsub\n"
"If set to TRUE subtract the thermal background from the input cube.\n"
"Default value is TRUE.\n"
"\n"
"---------------------------------------------------------------------------\n"
"  Input files:\n"
"   DO CATG           Type   Explanation                    Required #Frames\n"
"   --------          -----  -----------                    -------- -------\n"
"   CUBE_OBJECT       F3I    object cubes                       Y      >=1  \n"
"   CUBE_SKY          F3I    sky cube                           Y       1   \n"
"\n"
"  Output files:\n"
"   DO_CATG           Type   Explanation\n"
"   --------          -----  -----------\n"
"   OBJECT_S          F3I    Corrected object cubes\n"
"---------------------------------------------------------------------------\n"
"\n";

/*----------------------------------------------------------------------------*/
/**
  @defgroup kmo_sky_tweak kmo_sky_tweak OH line removal
 */
/*----------------------------------------------------------------------------*/

/**@{*/

/*----------------------------------------------------------------------------*/
/**
  @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_sky_tweak",
            "Removal of OH sky lines",
            kmo_sky_tweak_description,
            "Erich Wiezorrek",
            "usd-help@eso.org",
            kmos_get_license(),
            kmo_sky_tweak_create,
            kmo_sky_tweak_exec,
            kmo_sky_tweak_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_sky_tweak_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 */

    /* --tbsub */
    p = cpl_parameter_new_value("kmos.kmo_sky_tweak.tbsub", CPL_TYPE_BOOL,
            "Subtract thermal background from input cube."
            "(TRUE (apply) or FALSE (don't apply)",
            "kmos.kmo_sky_tweak", TRUE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "tbsub");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    return 0;

}

/*----------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
*/
/*----------------------------------------------------------------------------*/
static int kmo_sky_tweak_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_sky_tweak(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_sky_tweak_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 operator not valid,
                                   if first operand not 3d or
                                   if second operand not valid
  @li CPL_ERROR_INCOMPATIBLE_INPUT if the dimensions of the two operands do
                                   not match
*/
/*----------------------------------------------------------------------------*/
static int kmo_sky_tweak(cpl_parameterlist *parlist, cpl_frameset *frameset)
{
    int              ret_val                = 0;

    int              ox                     = 0,
                     nr_object_frames       = 0,
                     nr_obj_devices         = 0,
                     nr_sky_devices         = 0,
                     ifu_nr                 = 0,
                     sky_index              = 0,
                     obj_index              = 0,
                     tbsub                  = TRUE;
    const char       *obj_fn                = NULL,
                     *sky_fn                = NULL;

    cpl_frame        **object_frames        = NULL,
                     *object_frame          = NULL,
                     *sky_frame             = NULL;
    cpl_imagelist    *obj_data              = NULL,
                     *sky_data              = NULL,
                     *tweaked_data          = NULL;
    cpl_propertylist *main_header           = NULL,
                     *sub_header            = NULL;
    main_fits_desc   obj_fits_desc,
                     sky_fits_desc;

    KMO_TRY
    {
        /* Check entries */
        KMO_TRY_ASSURE((parlist != NULL) && (frameset != NULL),
                CPL_ERROR_NULL_INPUT, "Not all input data is provided");

        KMO_TRY_ASSURE(!((cpl_frameset_count_tags(frameset, CUBE_OBJECT) == 0)
                    && (cpl_frameset_count_tags(frameset, CUBE_SKY) == 0)),
                CPL_ERROR_FILE_NOT_FOUND,
                "CUBE_OBJECT or CUBE_SKY frames missing in frameset");

        KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, CUBE_SKY) == 1,
                CPL_ERROR_FILE_NOT_FOUND,
                "Exactly one CUBE_SKY frame is expected in frameset");

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

        tbsub = kmo_dfs_get_parameter_bool(parlist, "kmos.kmo_sky_tweak.tbsub");
        KMO_TRY_CHECK_ERROR_STATE();

        nr_object_frames = cpl_frameset_count_tags(frameset, CUBE_OBJECT);
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_EXIT_IF_NULL(
            object_frames = cpl_malloc(nr_object_frames * sizeof(cpl_frame*)));

        for (ox = 0; ox < nr_object_frames; ox++) {
            if (ox == 0) {
                KMO_TRY_EXIT_IF_NULL(object_frames[ox] = 
                        cpl_frameset_find(frameset, CUBE_OBJECT));
            } else {
                KMO_TRY_EXIT_IF_NULL(object_frames[ox] = 
                        cpl_frameset_find(frameset, NULL));
            }
            obj_fits_desc = kmo_identify_fits_header(
                    cpl_frame_get_filename(object_frames[ox]));
            KMO_TRY_CHECK_ERROR_STATE_MSG("Wrong FITS file format");
            KMO_TRY_ASSURE(obj_fits_desc.fits_type == f3i_fits,
                    CPL_ERROR_ILLEGAL_INPUT,
                    "Object fits file type must be F3I");
            kmo_free_fits_desc(&obj_fits_desc);
        }

        KMO_TRY_EXIT_IF_NULL(sky_frame = cpl_frameset_find(frameset, CUBE_SKY));
        sky_fits_desc = kmo_identify_fits_header(
                cpl_frame_get_filename(sky_frame));
        KMO_TRY_CHECK_ERROR_STATE_MSG("Wrong FITS file format");
        KMO_TRY_ASSURE(sky_fits_desc.fits_type == f3i_fits,
                CPL_ERROR_ILLEGAL_INPUT, "Sky fits file type must be F3I");

        if (sky_fits_desc.ex_noise == TRUE) {
            nr_sky_devices = sky_fits_desc.nr_ext / 2;
        } else {
            nr_sky_devices = sky_fits_desc.nr_ext;
        }

        KMO_TRY_EXIT_IF_NULL(sky_fn = cpl_frame_get_filename(sky_frame));

        for (ox = 0; ox < nr_object_frames; ox++) {
            object_frame = object_frames[ox];
            obj_fits_desc = kmo_identify_fits_header(
                    cpl_frame_get_filename(object_frame));
            KMO_TRY_CHECK_ERROR_STATE_MSG("Wrong Object FITS file format");
            if (obj_fits_desc.ex_noise == TRUE) {
                nr_obj_devices = obj_fits_desc.nr_ext / 2;
            } else {
                nr_obj_devices = obj_fits_desc.nr_ext;
            }
            KMO_TRY_ASSURE((nr_sky_devices == nr_obj_devices) || 
                    (nr_sky_devices == 1), CPL_ERROR_ILLEGAL_INPUT,
                    "SKY frame ext. number must be 1 or match OBJECT frame");

            KMO_TRY_EXIT_IF_NULL(obj_fn = cpl_frame_get_filename(object_frame));

            KMO_TRY_EXIT_IF_NULL(main_header = kmclipm_propertylist_load(
                        obj_fn, 0));

            KMO_TRY_EXIT_IF_ERROR(kmo_dfs_save_main_header(frameset, SKY_TWEAK,
                        "", object_frame, main_header, parlist, cpl_func));

            for (ifu_nr = 1; ifu_nr <= nr_obj_devices; ifu_nr++) {
                cpl_msg_info(cpl_func, "Processing IFU#: %d", ifu_nr);

                /* Get sky index */
                if (nr_sky_devices == nr_obj_devices) {
                    sky_index = kmo_identify_index(sky_fn, ifu_nr, FALSE);
                } else {
                    sky_index = kmo_identify_index(sky_fn, 1, FALSE);
                }
                
                /* Get Object index */
                obj_index = kmo_identify_index(obj_fn, ifu_nr, FALSE);

                /* Load Object header */
                sub_header = kmclipm_propertylist_load(obj_fn, obj_index);
                                
                KMO_TRY_CHECK_ERROR_STATE();
                
                /* Only reduce valid IFUs */
                if (obj_fits_desc.sub_desc[obj_index-1].valid_data &&
                        sky_fits_desc.sub_desc[sky_index-1].valid_data) {
                    /* Load sky and object */
                    sky_data = kmclipm_imagelist_load(sky_fn, CPL_TYPE_FLOAT, 
                            sky_index);
                    KMO_TRY_EXIT_IF_NULL(sky_data) ;
                    obj_data=kmclipm_imagelist_load(obj_fn, CPL_TYPE_FLOAT,
                            obj_index);
                    KMO_TRY_EXIT_IF_NULL(obj_data) ;
                    tweaked_data = kmo_priv_sky_tweak(obj_data, sky_data, 
                            sub_header, .3, tbsub);
                    KMO_TRY_EXIT_IF_NULL(tweaked_data) ;
                } else {
                    /* Empty IFU */
                    cpl_msg_warning(__func__, "Empty IFU - skip") ;
                } 

                KMO_TRY_EXIT_IF_ERROR(kmo_dfs_save_cube(tweaked_data, 
                            SKY_TWEAK, "", sub_header, 0./0.));

                cpl_propertylist_delete(sub_header); sub_header = NULL;
                cpl_imagelist_delete(obj_data); obj_data = NULL;
                cpl_imagelist_delete(sky_data); sky_data = NULL;
                cpl_imagelist_delete(tweaked_data); tweaked_data = NULL;
            }

            kmo_free_fits_desc(&obj_fits_desc);
            cpl_propertylist_delete(main_header); main_header = NULL;
        }
        kmo_free_fits_desc(&sky_fits_desc);
        cpl_free(object_frames); object_frames = NULL;
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_val = -1;
    }
    return ret_val;
}

/**@}*/
