/* $Id: sinfo_utl_ima_arith.c,v 1.14 2009-01-30 14:56:12 amodigli Exp $
 *
 * This file is part of the SINFONI 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
 */

/*
 * $Author: amodigli $
 * $Date: 2009-01-30 14:56:12 $
 * $Revision: 1.14 $
 * $Name: not supported by cvs2svn $
 */

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

/*-----------------------------------------------------------------------------
                                Includes
 ----------------------------------------------------------------------------*/
#include <string.h>

/* cpl */
#include <cpl.h>

/* irplib */
#include <irplib_utils.h>

#include <sinfo_tpl_utils.h>
#include <sinfo_pfits.h>
#include <sinfo_tpl_dfs.h>
#include <sinfo_key_names.h>
#include <sinfo_pro_types.h>
#include <sinfo_functions.h>
#include <sinfo_msg.h>
#include <sinfo_error.h>
#include <sinfo_utils_wrappers.h>

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

static int sinfo_utl_ima_arith_create(cpl_plugin *) ;
static int sinfo_utl_ima_arith_exec(cpl_plugin *) ;
static int sinfo_utl_ima_arith_destroy(cpl_plugin *) ;
static int sinfo_utl_ima_arith(cpl_parameterlist *, cpl_frameset *) ;

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

static char sinfo_utl_ima_arith_description[] =
                "This recipe performs image computation.\n"
                "The input files are 2 images\n"
                "their associated tags should be IMA.\n"
                "The output is an image resulting from the IMA op IMA where op indicates\n"
                "the operation to be performed specified by the parameter \n"
                "sinfoni.sinfo_utl_ima_arith.op having alias 'op'\n"
                "\n";

/*-----------------------------------------------------------------------------
                                Functions code
 ----------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/**
 * @defgroup sinfo_utl_ima_arith  Recipe to do operations on an image
 */
/*---------------------------------------------------------------------------*/

/**@{*/
/*---------------------------------------------------------------------------*/
/**
  @brief    Build the list of available plugins, for this module. 
  @param    list    the plugin list
  @return   0 if everything is ok

  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,
                    SINFONI_BINARY_VERSION,
                    CPL_PLUGIN_TYPE_RECIPE,
                    "sinfo_utl_ima_arith",
                    "Computes result of ima1 op ima2",
                    sinfo_utl_ima_arith_description,
                    "Andrea Modigliani",
                    "Andrea.Modigliani@eso.org",
                    sinfo_get_license(),
                    sinfo_utl_ima_arith_create,
                    sinfo_utl_ima_arith_exec,
                    sinfo_utl_ima_arith_destroy) ;

    cpl_pluginlist_append(list, plugin) ;

    return 0;
}

/*---------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options    
  @param    plugin  the plugin
  @return   0 if everything is ok

  Create the recipe instance and make it available to the application using 
  the interface. 
 */
/*---------------------------------------------------------------------------*/
static int sinfo_utl_ima_arith_create(cpl_plugin * plugin)
{
    cpl_recipe      * recipe ;
    cpl_parameter   * p ;

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

    /* Create the parameters list in the cpl_recipe object */
    recipe->parameters = cpl_parameterlist_new() ; 

    /* Fill the parameters list */
    /* --stropt */
    p = cpl_parameter_new_value("sinfoni.sinfo_utl_ima_arith.op", 
                    CPL_TYPE_STRING,
                    "A possible operation",
                    "sinfoni.sinfo_utl_ima_arith","+");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "op") ;
    cpl_parameterlist_append(recipe->parameters, p) ;

    /* --doubleopt */
    p = cpl_parameter_new_value("sinfoni.sinfo_utl_ima_arith.value", 
                    CPL_TYPE_DOUBLE, "a value", "sinfoni.sinfo_utl_ima_arith", 9999.) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "value") ;
    cpl_parameterlist_append(recipe->parameters, p) ;

    /* Return */
    return 0;
}

/*---------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/
static int sinfo_utl_ima_arith_exec(cpl_plugin * plugin)
{
    cpl_recipe  *   recipe ;
    int code=0;
    cpl_errorstate initial_errorstate = cpl_errorstate_get();

    /* 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_error_reset();
    irplib_reset();
    code = sinfo_utl_ima_arith(recipe->parameters, recipe->frames) ;


    if (!cpl_errorstate_is_equal(initial_errorstate)) {                      
        /* Dump the error history since recipe execution start.                
           At this point the recipe cannot recover from the error */           
        cpl_errorstate_dump(initial_errorstate, CPL_FALSE, NULL);              
    } 

    return code ;
}

/*---------------------------------------------------------------------------*/
/**
  @brief    Destroy what has been created by the 'create' function
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/
static int sinfo_utl_ima_arith_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    Get the command line options and execute the data reduction
  @param    parlist     the parameters list
  @param    framelist   the frames list
  @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/
static int 
sinfo_utl_ima_arith( cpl_parameterlist   *   parlist, 
                     cpl_frameset        *   framelist)
{
    cpl_parameter       *   param= NULL ;
    const char          *   operation=NULL;
    double                     value=1 ;
    cpl_frame           *   frm_ima1=NULL ;
    cpl_frame           *   frm_ima2=NULL ;
    cpl_image           *   ima1=NULL ;
    cpl_image           *   ima2=NULL ;
    int switch_ima2     = 0;
    const char                *   name_o=NULL ;
    cpl_propertylist    *   plist=NULL ;
    cpl_image           *   image=NULL ;
    cpl_frame           *   product_frame=NULL;
    cpl_frameset * raw_set=NULL;
    int nraw=0;
    int n=0;
    sinfo_msg("Welcome to SINFONI Pipeline release %d.%d.%d",
              SINFONI_MAJOR_VERSION,SINFONI_MINOR_VERSION,SINFONI_MICRO_VERSION);

    /* HOW TO RETRIEVE INPUT PARAMETERS */
    /* --stropt */
    check_nomsg(param=cpl_parameterlist_find(parlist, 
                    "sinfoni.sinfo_utl_ima_arith.op"));
    check_nomsg(operation=cpl_parameter_get_string(param));

    /* --boolopt */
    check_nomsg(param=cpl_parameterlist_find(parlist,
                    "sinfoni.sinfo_utl_ima_arith.value"));
    check_nomsg(value = cpl_parameter_get_double(param)) ;

    /* Identify the RAW and CALIB frames in the input frameset */
    check(sinfo_dfs_set_groups(framelist),
          "Cannot identify RAW and CALIB frames") ;

    /* HOW TO ACCESS INPUT DATA */
    n=cpl_frameset_get_size(framelist);
    if(n<1) {
        sinfo_msg_error("Empty input frame list!");
        goto cleanup ;
    }

    /* HOW TO ACCESS INPUT DATA */
    check_nomsg(raw_set=cpl_frameset_new());

    check(sinfo_contains_frames_kind(framelist,raw_set,PRO_IMA),
          "Found no input frames with tag %s",PRO_IMA);
    check_nomsg(nraw=cpl_frameset_get_size(raw_set));
    if (nraw<1) {
        sinfo_msg_error("Found no input frames with tag %s",PRO_IMA);
        goto cleanup;
    } else {
        check_nomsg(frm_ima1=cpl_frameset_get_frame(framelist,0));
        check_nomsg(ima1=cpl_image_load(cpl_frame_get_filename(frm_ima1),
                        CPL_TYPE_FLOAT,0,0));
        if (nraw>1) {
            check_nomsg(frm_ima2=cpl_frameset_get_frame(framelist,1));
            check_nomsg(ima2 = cpl_image_load(cpl_frame_get_filename(frm_ima2),
                            CPL_TYPE_FLOAT,0,0));
            switch_ima2=1;
        } else if (value == 9999.) {
            sinfo_msg_error("Found only one input frames with tag %s",PRO_IMA);
            goto cleanup;
        } else {
            sinfo_msg("Perform image arithmetics on frame %s",PRO_IMA);
        }
    }

    sinfo_free_frameset(&raw_set);

    /* HOW TO GET THE VALUE OF A FITS KEYWORD */
    check(plist=cpl_propertylist_load(cpl_frame_get_filename(frm_ima1),0),
          "Cannot read the FITS header") ;

    /* Now performing the data reduction */
    /* Let's generate one image for the example */
    if (value == 9999.) {

        if(ima1 != NULL && ima2 != NULL) {
            sinfo_msg("ima1 %s ima2",operation);
            if (strcmp(operation,"+") == 0 ) {
                check(image = cpl_image_add_create(ima1, ima2),
                                "Cannot generate the %s image",operation) ;
            } else if (strcmp(operation,"-") == 0 ) {
                check(image = cpl_image_subtract_create(ima1, ima2),
                                "Cannot generate the %s image",operation) ;
            } else if (strcmp(operation,"*") == 0 ) {
                check(image = cpl_image_multiply_create(ima1, ima2),
                                "Cannot generate the %s image",operation) ;
            } else if (strcmp(operation,"/") == 0 ) {
                check(image = cpl_image_divide_create(ima1, ima2),
                                "Cannot generate the %s image",operation) ;
            } else {
                sinfo_msg_error("Operation %s not supported",operation);
                goto cleanup;
            }
            sinfo_free_image(&ima1);
            sinfo_free_image(&ima2);

        }

    } else {
        sinfo_msg("ima1 %s %f",operation,value);

        if(switch_ima2 == 1) {
            sinfo_free_image(&ima2);
        }

        if (strcmp(operation,"+") == 0 ) {
            check(image = cpl_image_add_scalar_create(ima1, value),
                            "Cannot apply the %s operator",operation) ;
        } else if (strcmp(operation,"-") == 0 ) {
            check(image = cpl_image_subtract_scalar_create(ima1, value),
                            "Cannot apply the %s operator",operation) ;
        } else if (strcmp(operation,"*") == 0 ) {
            check(image = cpl_image_multiply_scalar_create(ima1, value),
                            "Cannot apply the %s operator",operation) ;
        } else if (strcmp(operation,"/") == 0 ) {
            check(image = cpl_image_divide_scalar_create(ima1, value),
                            "Cannot apply the %s operator",operation) ;
        } else {
            sinfo_msg_error("Operation %s not supported",operation);
            goto cleanup;
        }

        sinfo_free_image(&ima1);

    }


    /* HOW TO SAVE A PRODUCT ON DISK  */
    /* Set the file name */
    name_o = "ima_res.fits" ;

    /* Create product frame */
    check_nomsg(product_frame = cpl_frame_new());
    check_nomsg(cpl_frame_set_filename(product_frame, name_o)) ;
    check_nomsg(cpl_frame_set_tag(product_frame, SI_UTL_IMA_ARITH_PROIMA)) ;
    check_nomsg(cpl_frame_set_type(product_frame, CPL_FRAME_TYPE_IMAGE)) ;
    check_nomsg(cpl_frame_set_group(product_frame, CPL_FRAME_GROUP_PRODUCT)) ;
    check(cpl_frame_set_level(product_frame, CPL_FRAME_LEVEL_FINAL),
          "Error while initialising the product frame") ;

    /* Add DataFlow keywords */
    check_nomsg(cpl_propertylist_erase_regexp(plist, "^ESO PRO CATG",0));
    check(cpl_dfs_setup_product_header(plist, 
                    product_frame,
                    framelist,
                    parlist,
                    "sinfo_utl_ima_arith",
                    "SINFONI",
                    KEY_VALUE_HPRO_DID,NULL),
          "Problem in the product DFS-compliance") ;

    /* Save the file */
    check(cpl_image_save(image, 
                    name_o,
                    CPL_BPP_IEEE_FLOAT,
                    plist,
                    CPL_IO_DEFAULT),
          "Could not save product");
    sinfo_free_propertylist(&plist) ;
    sinfo_free_image(&image);

    /* Log the saved file in the input frameset */
    check_nomsg(cpl_frameset_insert(framelist, product_frame)) ;


    cleanup:

    sinfo_free_image(&ima1);
    sinfo_free_image(&ima2);
    sinfo_free_frameset(&raw_set);
    sinfo_free_propertylist(&plist) ;
    /* This is usually freed by esorex: but what about if errors occurs?
    sinfo_free_frame(&product_frame) ;
     */
    sinfo_free_image(&image) ;

    if (cpl_error_get_code()) {
        return -1 ;
    } else {
        return 0 ;
    }

}
/**@}*/
