/* $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: not supported by cvs2svn $
 */

#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"
        "If AverageFrames is set (--AverageFrames=TRUE), the final product \n"
        "will contain the average of the frames\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) ;

	p = cpl_parameter_new_value("amber.amber_selector.selection_AverageFrames",
            CPL_TYPE_BOOL, "Average all frames after selection",
            "amber.amber_selector", FALSE) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "AverageFrames") ;
    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;
    int             AverageFrames=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;
	cpl_frameset_iterator * it = NULL;

	/* 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_AverageFrames");
    AverageFrames=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);

	it = cpl_frameset_iterator_new(framelist);
	cur_frame = cpl_frameset_iterator_get(it);
	if (cur_frame == NULL) {
	    cpl_frameset_iterator_delete(it);
		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) ;
		cpl_frameset_iterator_advance(it, 1);
		cur_frame = cpl_frameset_iterator_get(it);

	}
	cpl_frameset_iterator_delete(it);

	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_position(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, AverageFrames, 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, AverageFrames, 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 ;
}


