/* $Id: uves_utl_remove_crh_single.c,v 1.4 2012-05-09 12:50:33 amodigli Exp $
 *
 * This file is part of the UVES 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: 2012-05-09 12:50:33 $
 * $Revision: 1.4 $
 * $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 <uves_tpl_utils.h>
#include <uves_pfits.h>
#include <uves_key_names.h>
#include <uves_raw_types.h>
#include <uves_pro_types.h>
#include <uves_functions.h>
*/

#include <uves_dfs.h>
#include <uves_msg.h>
#include <uves_error.h>
#include <uves_utils_wrappers.h>
#include <uves_remove_crh_single.h>
#include <uves_cpl_size.h>

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

static int uves_utl_remove_crh_single_create(cpl_plugin *) ;
static int uves_utl_remove_crh_single_exec(cpl_plugin *) ;
static int uves_utl_remove_crh_single_destroy(cpl_plugin *) ;
static int uves_utl_remove_crh_single(cpl_parameterlist *, cpl_frameset *) ;

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

static char uves_utl_remove_crh_single_description[] =
"This recipe performs image computation.\n"
"The input files is one image\n"
"their associated tags should be IMA.\n"
"The output is the image cleaned from CRHs\n"
"Information on relevant parameters can be found with\n"
"esorex --params uves_utl_remove_crh_single\n"
"esorex --help uves_utl_remove_crh_single\n"
"\n";

/*-----------------------------------------------------------------------------
                                Functions code
 ----------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/**
 * @defgroup uves_utl_remove_crh_single  Recipe to remove CRHs from 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,
                    UVES_BINARY_VERSION,
                    CPL_PLUGIN_TYPE_RECIPE,
                    "uves_utl_remove_crh_single",
                    "Remove CRHs from an image",
                    uves_utl_remove_crh_single_description,
                    "Andrea Modigliani",
                    "Andrea.Modigliani@eso.org",
                    uves_get_license(),
                    uves_utl_remove_crh_single_create,
                    uves_utl_remove_crh_single_exec,
                    uves_utl_remove_crh_single_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 uves_utl_remove_crh_single_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("uves.uves_utl_remove_crh_single.crh_frac_max", 
                                CPL_TYPE_DOUBLE, 
                                "Maximum fraction of allowed CRHs", 
                                "uves.uves_utl_remove_crh_single",0.7);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "crh_frac_max") ;
    cpl_parameterlist_append(recipe->parameters, p) ;

    /* --doubleopt */
    p = cpl_parameter_new_value("uves.uves_utl_remove_crh_single.sigma_lim", 
				CPL_TYPE_DOUBLE, 
				"Maximum sigma in kappa-sigma clip",
				"uves.uves_utl_remove_crh_single", 25.) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sigma_lim") ;
    cpl_parameterlist_append(recipe->parameters, p) ;



    p = cpl_parameter_new_value("uves.uves_utl_remove_crh_single.f_lim", 
				CPL_TYPE_DOUBLE, 
				"Max fraction of bad pixels allowed",
				"uves.uves_utl_remove_crh_single", 0.7) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "f_lim") ;
    cpl_parameterlist_append(recipe->parameters, p) ;


    p = cpl_parameter_new_value("uves.uves_utl_remove_crh_single.max_iter",
				CPL_TYPE_INT, 
				"Max fraction of bad pixels allowed",
				"uves.uves_utl_remove_crh_single",5) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "max_iter") ;
    cpl_parameterlist_append(recipe->parameters, p) ;



    p = cpl_parameter_new_value("uves.uves_utl_remove_crh_single.gain",
				CPL_TYPE_DOUBLE, 
				"Detector's gain",
				"uves.uves_utl_remove_crh_single",2.42) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "gain") ;
    cpl_parameterlist_append(recipe->parameters, p) ;


    p = cpl_parameter_new_value("uves.uves_utl_remove_crh_single.ron",
				CPL_TYPE_DOUBLE, 
				"Detector's ron",
				"uves.uves_utl_remove_crh_single",1.) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ron") ;
    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 uves_utl_remove_crh_single_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 = uves_utl_remove_crh_single(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 uves_utl_remove_crh_single_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 
uves_utl_remove_crh_single( cpl_parameterlist   *   parlist, 
        cpl_frameset        *   framelist)
{
    cpl_parameter       *   param= NULL ;
    cpl_frameset * raw_on=NULL;
    cpl_frameset * raw_off=NULL;
    double crh_frac_max=0;
    double sigma_lim=0;
    double f_lim=0;
    int max_iter=0;
    double gain=0;
    double ron=0;
    int nraw=0;
   
    cpl_image* ima_res=NULL;
    cpl_image* ima_on=NULL;
    cpl_image* ima_off=NULL;


    cpl_propertylist* plist1=NULL;
    cpl_propertylist* plist2=NULL;
    cpl_propertylist* pliste=NULL;

    const char* name_r="ima_res.fits";

    cpl_frame* product_frame=NULL;
    cpl_frame* frame_on=NULL;
    cpl_frame* frame_off=NULL;
    cpl_size next1=0;
    const char* name1=NULL;

    cpl_size next2=0;
    const char* name2=NULL;

    int noff=0;
    int chips=0;
    int nfrm=0;
    int i=0;
  

    uves_msg("Welcome to UVES Pipeline release %d.%d.%d",
        UVES_MAJOR_VERSION,UVES_MINOR_VERSION,UVES_MICRO_VERSION);

    /* HOW TO RETRIEVE INPUT PARAMETERS */
    check_nomsg(param=cpl_parameterlist_find(parlist, 
                                             "uves.uves_utl_remove_crh_single.crh_frac_max"));
    check_nomsg(crh_frac_max=cpl_parameter_get_double(param));

    check_nomsg(param=cpl_parameterlist_find(parlist,
                                             "uves.uves_utl_remove_crh_single.sigma_lim"));
    check_nomsg(sigma_lim = cpl_parameter_get_double(param)) ;


    check_nomsg(param=cpl_parameterlist_find(parlist,
                                             "uves.uves_utl_remove_crh_single.f_lim"));
    check_nomsg(f_lim = cpl_parameter_get_double(param)) ;

    check_nomsg(param=cpl_parameterlist_find(parlist,
                                             "uves.uves_utl_remove_crh_single.max_iter"));
    check_nomsg(max_iter = cpl_parameter_get_int(param)) ;

    check_nomsg(param=cpl_parameterlist_find(parlist,
                                             "uves.uves_utl_remove_crh_single.gain"));
    check_nomsg(gain = cpl_parameter_get_double(param)) ;


    check_nomsg(param=cpl_parameterlist_find(parlist,
                                             "uves.uves_utl_remove_crh_single.ron"));
    check_nomsg(ron = cpl_parameter_get_double(param)) ;
  



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

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

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

    check(uves_contains_frames_kind(framelist,raw_on,"RAW_IMA"),
     "Found no input frames with tag %s","RAW_IMA");
    check_nomsg(nraw=cpl_frameset_get_size(raw_on));
    if (nraw<1) {
      uves_msg_error("Found no input frames with tag %s","RAW_IMA");
      goto cleanup;
    }

    uves_msg("nraw=%d",nraw);

    check_nomsg(frame_on=cpl_frameset_get_first(raw_on));
    check_nomsg(next1=cpl_frame_get_nextensions(frame_on));
    check_nomsg(name1=cpl_frame_get_filename(frame_on));
    check_nomsg(plist1=cpl_propertylist_load(name1,0));
    uves_msg("CRH affected file name =%s",name1);



    if (nfrm>1) {
      /* if input has more than a frame, search for bias */
      check_nomsg(raw_off=cpl_frameset_new());
 
      chips=cpl_propertylist_get_int(plist1,"ESO DET CHIPS");

      /* deal with BLUE/RED arms and search for proper bias*/
      if(chips==2) {
	check(uves_contains_frames_kind(framelist,raw_off,"BIAS_RED"),
	      "Found no input frames with tag %s","BIAS_RED");
      } else {
	check(uves_contains_frames_kind(framelist,raw_off,"BIAS_BLUE"),
	      "Found no input frames with tag %s","BIAS_BLUE");
      }
 
      check_nomsg(noff=cpl_frameset_get_size(raw_off));
      if (noff<1) {
	uves_msg_error("Found no input bias frames");
      
      } else {

	frame_off=cpl_frameset_get_first(raw_off);
	next2=cpl_frame_get_nextensions(frame_off);
        /* check that raw frame and bias are coherent else exit */
        if(next2 != next1) {
	  uves_msg_error("Raw frames with different number of extensions");
	  uves_msg_error("Something wrong! Exit");
          goto cleanup;
	}
	name2=cpl_frame_get_filename(frame_off);
        uves_msg("Bias file name =%s",name2);
	check_nomsg(cpl_image_save(NULL, name_r,CPL_BPP_IEEE_FLOAT,
				   plist1,CPL_IO_DEFAULT));

        /* subtract bias on each extension */
	if(next1==0) {
          /*  
             subtract bias, 
             correct for cosmics, 
             save result 
          */

	  check_nomsg(ima_on=cpl_image_load(name1,CPL_TYPE_FLOAT,0,0));
	  check_nomsg(ima_off=cpl_image_load(name2,CPL_TYPE_FLOAT,0,0));
	  check_nomsg(cpl_image_subtract(ima_on,ima_off));


	  cpl_image_save(ima_on,"image_with_crh.fits",CPL_BPP_IEEE_FLOAT,
			 NULL,CPL_IO_DEFAULT);
   

	  check(ima_res=uves_remove_crh_single(ima_on,crh_frac_max,
                                                 sigma_lim,f_lim,
						 max_iter,gain,ron),
		  "fail to remove CRHs");

	  check_nomsg(cpl_image_add(ima_res,ima_off));
      
	  check_nomsg(cpl_image_save(ima_res, name_r,CPL_BPP_IEEE_FLOAT,
				     plist1,CPL_IO_DEFAULT));
	} else {
	  uves_msg("next=%" CPL_SIZE_FORMAT "",next1);
          /* loop over extensions, 
             subtract bias, 
             correct for cosmics, 
             save result 
          */



	  for(i=1;i<=next1;i++) {
            uves_msg("name1=%s",name1);
	    uves_msg("name2=%s",name2);
	    check_nomsg(ima_on=cpl_image_load(name1,CPL_TYPE_FLOAT,0,i));


	    check_nomsg(pliste=cpl_propertylist_load(name1,i));

	    if(next2==0) {
	      check_nomsg(ima_off=cpl_image_load(name2,CPL_TYPE_FLOAT,0,0));

	    } else {
	      check_nomsg(ima_off=cpl_image_load(name2,CPL_TYPE_FLOAT,0,i));

	    }
            uves_msg("ima_on=%p ima_off=%p",ima_on,ima_off);
	    check_nomsg(cpl_image_subtract(ima_on,ima_off));

	    cpl_image_save(ima_on,"image_with_crh.fits",CPL_BPP_IEEE_FLOAT,
			   NULL,CPL_IO_DEFAULT);
   
            check(ima_res=uves_remove_crh_single(ima_on,crh_frac_max,
                                                 sigma_lim,f_lim,
						 max_iter,gain,ron),
		  "fail to remove CRHs");

	    check_nomsg(cpl_image_add(ima_res,ima_off));

 
	    if(i>0) {
	      check_nomsg(cpl_image_save(ima_res, name_r,CPL_BPP_IEEE_FLOAT,
					 pliste,CPL_IO_EXTEND));
	    }
	    uves_free_image(&ima_on);
	    uves_free_image(&ima_off);
	    uves_free_image(&ima_res);
	    cpl_propertylist_delete(pliste); pliste=NULL; 

 
	  }


	}
      }

      uves_free_frameset(&raw_off);
      uves_free_frameset(&raw_on);


    } else {
      uves_msg("Please, provide a bias frame. Exit.");
      goto cleanup;
    }
 



    /* HOW TO SAVE A PRODUCT ON DISK  */
    /* Set the file name */
 
    /* Create product frame */
    check_nomsg(product_frame = cpl_frame_new());
    check_nomsg(cpl_frame_set_filename(product_frame, name_r)) ;
    check_nomsg(cpl_frame_set_tag(product_frame, "PRODUCT")) ;
    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(plist1, "^ESO PRO CATG",0));
    /*
    check(cpl_dfs_setup_product_header(plist, 
                                       product_frame, 
                                       framelist, 
                                       parlist,
                                       "uves_utl_remove_crh_single", 
                                       "UVES", 
                                       DICTIONARY),
      "Problem in the product DFS-compliance") ;
    */
    /* Save the file 
    check(cpl_image_save(ima_res, 
                         name_r, 
                         CPL_BPP_IEEE_FLOAT, 
                         plist1,
                         CPL_IO_DEFAULT),
      "Could not save product");
    */
    cpl_propertylist_delete(plist1) ; plist1=NULL;

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


 cleanup:
     uves_free_frameset(&raw_off);
     uves_free_image(&ima_on);
     uves_free_image(&ima_off);
     uves_free_image(&ima_res);

     if(pliste!=NULL) cpl_propertylist_delete(pliste); pliste=NULL; 

     if (plist1!=NULL) cpl_propertylist_delete(plist1);plist1=NULL;
     if (plist2!=NULL) cpl_propertylist_delete(plist2);plist2=NULL;


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

}
/**@}*/
