/* $Id: amber_selector.c,v 1.53 2011/09/26 12:54:06 agabasch Exp $
 *
 * This file is part of the AMBER 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: agabasch $
 * $Date: 2011/09/26 12:54:06 $
 * $Revision: 1.53 $
 * $Name: amber-4_2_2 $
 */

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

/* AMDLIB usage switch */
#define USE_AMDLIB YES
//#define amdlibPRECISION 1E-6
/*-----------------------------------------------------------------------------
  Includes
  ----------------------------------------------------------------------------*/
#include <string.h>
#include <math.h>
#include <cpl.h>
#include "amdrs.h"
#include "amber_qc.h"
#include "esolibSelector.h"

const char * selMethod1 = "First_x_Frames";
const char * selMethod2 = "Fringe_SNR_gt_x";
const char * selMethod3 = "Fringe_SNR_percentage_x";
const char * selMethod4 = "Flux_gt_x";
const char * selMethod5 = "Flux_percentage_x";
const char * selMethod6 = "Exclude_Frames_by_ASCII_File";
const char * selMethod7 = "Include_Frames_by_ASCII_File";
const char * selMethod8 = "IO-Test_no_filtering";
const char * selMethod9 = "Absolute_piston_value_lt_x";
const char * selMethod10= "Absolute_piston_value_percentage_x";


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

static int amber_selector_create(cpl_plugin *) ;
static int amber_selector_exec(cpl_plugin *) ;
static int amber_selector_destroy(cpl_plugin *) ;
static int amber_selector(cpl_parameterlist *, cpl_frameset *) ;
/*-----------------------------------------------------------------------------
  Static variables
  ----------------------------------------------------------------------------*/

static char amber_selector_man[] =
		"This recipe selects frames stored in an AMBER OI product by various\n"
		"methods and writes a filtered OI product. After selecting a method "
		"one\n"
		"can set a selection criteria for every baseline by defining the\n"
		"variables X1, X2, and X3. If only one baseline is available, X2 and "
		"X3\n"
		"can be neglected. Moreover one can choose the combining method of "
		"the\n"
		"single criteria by setting the Boolean variable ANDselection.\n"
		"\n"
		"If ANDselection is not set (--ANDselection=FALSE), a frame passes "
		"the\n"
		"filter if at least one of the selection criteria (X1 || X2 || X3) is\n"
		"fulfilled.\n"
		"\n"
		"If ANDselection is set (--ANDselection=TRUE), a frame passes the\n"
		"filter only if ALL selection criteria (X1 && X2 && X3) are "
		"fulfilled.\n"
		"\n"
		"Please note, that for three baselines in both cases the visibility\n"
		"triplet is written to the product file!\n\n"
		"Input files:\n\n"
		"  DO category:               Type:      Explanation:      Required: \n"
		"  SCIENCE_REDUCED            Products   OI-Fits file                \n"
		"  or                                                                \n"
		"  SCIENCE_REDUCED_FILTERED   Products   OI-Fits file                \n"
		"  or                                                                \n"
		"  CALIB_REDUCED              Products   OI-Fits file               Y\n"
		"  or                                                                \n"
		"  CALIB_REDUCED_FILTERED     Products   OI-Fits file                \n"
		"  or                                                                \n"
		"  SCIENCE_CALIBRATED         Products   OI-Fits file              \n\n"
		"Output files:\n\n"
		"  DO category:               Data type: Explanation:                \n"
		"  SCIENCE_REDUCED_FILTERED   Products:  filtered OI-fits file       \n"
		"  or                                                                \n"
		"  CALIB_REDUCED_FILTERED     Products:  filtered OI-fits file    \n\n";

/*-----------------------------------------------------------------------------
  Functions code
  ----------------------------------------------------------------------------*/

/* FIXME: Cast string literal in fits_create_tbl(), ..to avoid compiler warnings
   - and pray that CFITSIO does not try to modify them :-(((( */


#define amdlib_OI_REVISION 1 /*Current revision number*/
#define amdlibNM_TO_M   1e-9 /* conversion nanometers to meters */

/** Usefull macro to error when reading/writing IO6FITS file */ 
#define amdlibOiReturnError(routine,msg)                        \
		fits_get_errstatus(status, (char*)fitsioMsg);                \
		amdlibERROR("%s(): %s - %s\n", routine, msg, fitsioMsg);     \
		sprintf(errMsg, "%s(): %s - %s", routine, msg, fitsioMsg);   \
		return (amdlibFAILURE)


/* ---------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/**
   @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,
			AMBER_BINARY_VERSION,
			CPL_PLUGIN_TYPE_RECIPE,
			"amber_selector",
			"AMBER OI frame selector",
			amber_selector_man,
			"Armin Gabasch",
			PACKAGE_BUGREPORT,
			"",
			amber_selector_create,
			amber_selector_exec,
			amber_selector_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 amber_selector_create(cpl_plugin * plugin)
{
	cpl_recipe * recipe = (cpl_recipe *)plugin ;
	cpl_parameter * p ;

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

	/* Fill the parameters list */


	/* Selection Method */
	p = cpl_parameter_new_enum( "amber.amber_selector.selection_method",
			CPL_TYPE_STRING,
			"Selection methods",
			"amber.selection",
			selMethod2, 10,
			selMethod1, selMethod2, selMethod3, selMethod4,
			selMethod5, selMethod6, selMethod7, selMethod8, selMethod9,
			selMethod10 );

	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "selection-method") ;
	cpl_parameterlist_append(recipe->parameters, p) ;

	/* selection threshold float */
	p = cpl_parameter_new_value("amber.amber_selector.selection_x1",
			CPL_TYPE_DOUBLE, "X1 Value", "amber.amber_selector", 2.0 ) ;
	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "X1") ;
	cpl_parameterlist_append(recipe->parameters, p) ;

	p = cpl_parameter_new_value("amber.amber_selector.selection_x2",
			CPL_TYPE_DOUBLE, "X2 Value", "amber.amber_selector", 2.0 ) ;
	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "X2") ;
	cpl_parameterlist_append(recipe->parameters, p) ;


	p = cpl_parameter_new_value("amber.amber_selector.selection_x3",
			CPL_TYPE_DOUBLE, "X3 Value", "amber.amber_selector", 2.0 ) ;
	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "X3") ;
	cpl_parameterlist_append(recipe->parameters, p) ;


	p = cpl_parameter_new_value("amber.amber_selector.selection_ANDselection",
			CPL_TYPE_BOOL, "X1, X2, and X3 combining method",
			"amber.amber_selector", FALSE) ;
	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ANDselection") ;
	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 amber_selector_exec(cpl_plugin * plugin)
{
	cpl_recipe * recipe = (cpl_recipe *)plugin ;
	return amber_selector(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 amber_selector_destroy(cpl_plugin * plugin)
{
	cpl_recipe  *   recipe = (cpl_recipe *)plugin ;
	cpl_parameterlist_delete(recipe->parameters) ;
	return 0 ;
}



static int amber_selector(
		cpl_parameterlist  *   parlist,
		cpl_frameset       *   framelist)
{
	cpl_parameter * cur_param=NULL;
	cpl_frame     * cur_frame=NULL;
	int             ANDselection=0;
	double          x1=0,x2=0,x3=0;
	const char    * Method=NULL;
	int             iIsScience;
	int             returncode=0;
	int             framecount=0;
	char          * outname=NULL;
	char          * systemcall=NULL;
	cpl_errorstate  prestate = cpl_errorstate_get();
	const char          * tag=NULL;
	int             framelist_orig_length=0;

	/* Read the parameters */
	cur_param=cpl_parameterlist_find( parlist,
			"amber.amber_selector.selection_x1");
	x1 = cpl_parameter_get_double(cur_param);
	cur_param=cpl_parameterlist_find( parlist,
			"amber.amber_selector.selection_x2");
	x2 = cpl_parameter_get_double(cur_param);
	cur_param=cpl_parameterlist_find(parlist,
			"amber.amber_selector.selection_x3");
	x3=cpl_parameter_get_double(cur_param);
	cur_param=cpl_parameterlist_find(parlist,
			"amber.amber_selector.selection_ANDselection");
	ANDselection=cpl_parameter_get_bool(cur_param);
	cur_param=cpl_parameterlist_find( parlist,
			"amber.amber_selector.selection_method");
	Method=cpl_parameter_get_string(cur_param);
	cpl_msg_info(cpl_func,"Using Method: %s with x1=%g x2=%g x3=%g "
			"ANDselection %d", Method, x1, x2, x3, ANDselection);

	cur_frame = cpl_frameset_get_first(framelist);
	if (cur_frame == NULL) {
		return (int)cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
				"SOF does not have any file");
	}

	while(cur_frame)
	{
		cpl_frame_set_group(cur_frame, CPL_FRAME_GROUP_CALIB) ;
		cur_frame = cpl_frameset_get_next( framelist );
	}

	framelist_orig_length=cpl_frameset_get_size(framelist);


	prestate = cpl_errorstate_get();

	for(framecount=0; framecount<framelist_orig_length; framecount++){
		/*      Check the right tags */
		cur_frame=cpl_frameset_get_frame(framelist, framecount);
		tag = cpl_frame_get_tag(cur_frame);
		outname=cpl_sprintf("amber_filtered_%04d.fits",framecount);


		if (!strcmp(tag, "SCIENCE_REDUCED") ||
				!strcmp(tag,"SCIENCE_REDUCED_FILTERED") ||
				!strcmp(tag,"SCIENCE_CALIBRATED")) {
			iIsScience = 1;
			//trigger data reduction
			returncode=amber_selector_lib(x1, x2, x3, ANDselection, Method,
					cpl_frame_get_filename(cur_frame), outname,
					iIsScience, framelist, parlist, "amber_selector");
			if(returncode){
				cpl_msg_warning(cpl_func,"A problem occurred! Please check the "
						"filtering Criteria!!");


				cpl_msg_info(cpl_func,"Cleaning up working directory ...");
				systemcall=cpl_sprintf("rm -f %s",outname);
				system(systemcall);
				cpl_free(systemcall);
			}
		}
		if (!strcmp(tag, "CALIB_REDUCED") ||
				!strcmp(tag,"CALIB_REDUCED_FILTERED" )) {
			iIsScience = 0;
			//trigger data reduction
			returncode=amber_selector_lib(x1, x2, x3, ANDselection, Method,
					cpl_frame_get_filename(cur_frame), outname,
					iIsScience, framelist, parlist, "amber_selector");
			if(returncode){
				cpl_msg_warning(cpl_func,"A problem occurred! Please check the "
						"filtering Criteria!!");

				cpl_msg_info(cpl_func,"Cleaning up working directory ...");
				systemcall=cpl_sprintf("rm -f %s",outname);
				system(systemcall);
				cpl_free(systemcall);
			}
		}
		cpl_free(outname);

	}

	cpl_errorstate_set(prestate);

	/* return */
	if (cpl_error_get_code()) return -1 ;
	else return 0 ;
}


