/* 
 * 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 <stdio.h>
#include <string.h>

#include <cpl.h>
#include <cpl_wcs.h>

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

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

static int kmos_sky_tweak_create(cpl_plugin *);
static int kmos_sky_tweak_exec(cpl_plugin *);
static int kmos_sky_tweak_destroy(cpl_plugin *);
static int kmos_sky_tweak(cpl_parameterlist *, cpl_frameset *);

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

static char kmos_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 kmos_sky_tweak  OH line removal
 */
/*----------------------------------------------------------------------------*/

/**@{*/

/*-----------------------------------------------------------------------------
 *                              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_sky_tweak",
            "Removal of OH sky lines",
            kmos_sky_tweak_description,
            "Erich Wiezorrek, Yves Jung",
            "usd-help@eso.org",
            kmos_get_license(),
            kmos_sky_tweak_create,
            kmos_sky_tweak_exec,
            kmos_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 kmos_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 */

    /* --ifu */
    p = cpl_parameter_new_value("kmos.kmos_sky_tweak.ifu",
            CPL_TYPE_INT, "Only reduce the specified IFU",
            "kmos.kmos_sky_tweak", 0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ifu");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --discard_subband */
    p = cpl_parameter_new_value("kmos.kmos_sky_tweak.discard_subband",
            CPL_TYPE_BOOL, "Ignore last sub-band in the sky tweaking",
            "kmos.kmos_sky_tweak", FALSE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "discard_subband");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --stretch_sky */
    p = cpl_parameter_new_value("kmos.kmos_sky_tweak.stretch_sky",
            CPL_TYPE_BOOL, "Stretch sky", "kmos.kmos_sky_tweak", FALSE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "stretch");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --stretch_degree */
    p = cpl_parameter_new_value("kmos.kmos_sky_tweak.stretch_degree",
            CPL_TYPE_INT, "Stretch polynomial degree", "kmos.kmos_sky_tweak", 
            8);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "stretch_degree");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --stretch_resampling */
    p = cpl_parameter_new_value("kmos.kmos_sky_tweak.stretch_resampling",
            CPL_TYPE_STRING, "Stretch resampling method (linear/spline)", 
            "kmos.kmos_sky_tweak", "spline");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "stretch_resampling");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --plot */
    p = cpl_parameter_new_value("kmos.kmos_sky_tweak.plot",
            CPL_TYPE_INT, "Enable plotting", "kmos.kmos_sky_tweak", 0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "plot");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --tbsub */
    p = cpl_parameter_new_value("kmos.kmos_sky_tweak.tbsub", CPL_TYPE_BOOL,
            "Subtract thermal background from input cube",
            "kmos.kmos_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 kmos_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 kmos_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 kmos_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 kmos_sky_tweak(
        cpl_parameterlist   *   parlist, 
        cpl_frameset        *   frameset)
{
    const cpl_parameter *   par ;
    int                     ox, nr_object_frames, nr_obj_devices,
                            nr_sky_devices, ifu_nr, sky_index,
                            obj_index, tbsub, reduce_ifu, discard_subband,
                            stretch, stretch_degree, stretch_resampling, plot ;
    const char          *   sval ;
    const char          *   obj_fn ;
    const char          *   sky_fn ;
    cpl_frame           **  object_frames ;
    cpl_frame           *   object_frame ;
    cpl_frame           *   sky_frame ;
    cpl_imagelist       *   obj_data ;
    cpl_imagelist       *   sky_data ;
    cpl_imagelist       *   new_sky ;
    cpl_imagelist       *   result ;
    cpl_propertylist    *   main_header ;
    cpl_propertylist    *   sub_header ;
    main_fits_desc          obj_fits_desc,
                            sky_fits_desc;

    /* Check entries */
    if (parlist == NULL || frameset == NULL) {
        cpl_msg_error(__func__, "Null Inputs") ;
        cpl_error_set(__func__, CPL_ERROR_NULL_INPUT) ;
        return -1 ;
    }
    if (cpl_frameset_count_tags(frameset, CUBE_OBJECT) == 0 || 
            cpl_frameset_count_tags(frameset, CUBE_SKY) == 0) {
        cpl_msg_error(__func__, "Missing Inputs") ;
        cpl_error_set(__func__, CPL_ERROR_FILE_NOT_FOUND) ;
        return -1 ;
    }
    if (cpl_frameset_count_tags(frameset, CUBE_SKY) != 1) {
        cpl_msg_error(__func__, "one CUBE_SKY is expected") ;
        cpl_error_set(__func__, CPL_ERROR_FILE_NOT_FOUND) ;
        return -1 ;
    }

    /* Initialise */
    nr_obj_devices = nr_sky_devices = sky_index = obj_index = 0 ;

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

    /* Get Parameters */
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_sky_tweak.tbsub");
    tbsub = cpl_parameter_get_bool(par);
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_sky_tweak.ifu");
    reduce_ifu = cpl_parameter_get_int(par);
    par = cpl_parameterlist_find_const(parlist, 
            "kmos.kmos_sky_tweak.discard_subband");
    discard_subband = cpl_parameter_get_bool(par);
    par=cpl_parameterlist_find_const(parlist,"kmos.kmos_sky_tweak.stretch_sky");
    stretch = cpl_parameter_get_bool(par);
    par=cpl_parameterlist_find_const(parlist,
            "kmos.kmos_sky_tweak.stretch_degree");
    stretch_degree = cpl_parameter_get_int(par);
    par=cpl_parameterlist_find_const(parlist,
            "kmos.kmos_sky_tweak.stretch_resampling");
    sval = cpl_parameter_get_string(par);
    if (!strcmp(sval, "linear")) {
        stretch_resampling = 1 ;
    } else if (!strcmp(sval, "spline")) {
        stretch_resampling = 2 ;
    } else {
        stretch_resampling = -1 ;
    }
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_sky_tweak.plot");
    plot = cpl_parameter_get_int(par);

    /* Check Parameters */
    if (stretch_resampling < 0) {
        cpl_msg_warning(__func__, 
                "Invalid resampling method specified - use spline") ;
        stretch_resampling = 2 ;
    }

    nr_object_frames = cpl_frameset_count_tags(frameset, CUBE_OBJECT);
    object_frames = cpl_malloc(nr_object_frames * sizeof(cpl_frame*));

    for (ox = 0; ox < nr_object_frames; ox++) {
        if (ox == 0) {
            object_frames[ox] = cpl_frameset_find(frameset, CUBE_OBJECT);
        } else {
            object_frames[ox] = cpl_frameset_find(frameset, NULL);
        }
    }

    sky_frame = cpl_frameset_find(frameset, CUBE_SKY);
    sky_fits_desc = kmo_identify_fits_header(cpl_frame_get_filename(sky_frame));
    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;

    sky_fn = cpl_frame_get_filename(sky_frame);

    /* Look on the Object files */
    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));
        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;
        if (nr_sky_devices != nr_obj_devices && nr_sky_devices != 1) {
            cpl_msg_error(__func__,  "SKY frame ext nr must be 1 or match OBJ");
            return -1 ;
        }

        obj_fn = cpl_frame_get_filename(object_frame);
        main_header = kmclipm_propertylist_load(obj_fn, 0);

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

        /* Loop on the IFUs */
        for (ifu_nr = 1; ifu_nr <= nr_obj_devices; ifu_nr++) {
            /* Compute only one ifu */
            if (reduce_ifu != 0 && reduce_ifu != ifu_nr) continue ;

            cpl_msg_info(__func__, "Processing IFU#: %d", ifu_nr);
            cpl_msg_indent_more() ;

            /* 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);
                            
            /* Only reduce valid IFUs */
            new_sky = result = NULL ;
            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);
                obj_data=kmclipm_imagelist_load(obj_fn, CPL_TYPE_FLOAT,
                        obj_index);

                /* Apply the sky tweaking */
                result = kmos_priv_sky_tweak(obj_data, sky_data, 
                        sub_header, .3, tbsub, discard_subband,
                        stretch, stretch_degree, stretch_resampling, plot, 
                        &new_sky);
                cpl_imagelist_delete(new_sky);
                cpl_imagelist_delete(obj_data);
                cpl_imagelist_delete(sky_data);
            } else {
                /* Empty IFU */
                cpl_msg_warning(__func__, "Empty IFU - skip") ;
            } 

            kmo_dfs_save_cube(result, SKY_TWEAK, "", sub_header, 0./0.);

            cpl_propertylist_delete(sub_header); 
            cpl_imagelist_delete(result);
            cpl_msg_indent_less() ;
        }

        kmo_free_fits_desc(&obj_fits_desc);
        cpl_propertylist_delete(main_header);
    }
    kmo_free_fits_desc(&sky_fits_desc);
    cpl_free(object_frames);
    return 0;
}

/**@}*/
