/*
 * esolibSelector.c
 *
 *  Created on: Sep 25, 2009
 *      Author: agabasch
 */


/* AMDLIB usage switch */
#define USE_AMDLIB YES
//#define amdlibPRECISION 1E-6
/*-----------------------------------------------------------------------------
  Includes
  -----------------------------------------------------------------------------*/
#include "esolibSelector.h"

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

#include <string.h>
#include <math.h>
#include <cpl.h>
#include "amdrs.h"
#include "amber_dfs.h"
#include "amber_qc.h"

/* AMBER structures */
amdlibINS_CFG        insCfg;
amdlibOI_ARRAY       array;
amdlibOI_TARGET      target;
amdlibPHOTOMETRY     photometry;
amdlibVIS            vis;
amdlibVIS2           vis2;
amdlibVIS3           vis3;
amdlibWAVELENGTH     wave;
amdlibPISTON         pst;
amdlibSPECTRUM       spectrum;
amdlibERROR_MSG      errMsg;

double *tmpArray[amdlibNBASELINE];
double threshold[amdlibNBASELINE];

/* CPL data */
cpl_frame         *  pFrame=NULL;
cpl_propertylist  *  pHeader;

cpl_parameterlist *  gparlist;
cpl_frameset      *  gframelist;

FILE              *  pFITSFile;

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";

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


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


static amdlibCOMPL_STAT amdlibWriteOiArray(fitsfile        *filePtr,
		amdlibOI_ARRAY  *array,
		amdlibERROR_MSG errMsg);


static amdlibCOMPL_STAT amdlibWriteOiTarget(fitsfile        *filePtr,
		amdlibOI_TARGET *target,
		amdlibERROR_MSG errMsg);

static amdlibCOMPL_STAT amdlibWriteOiWavelength(fitsfile         *filePtr,
		char             *insName,
		amdlibWAVELENGTH *wave,
		amdlibERROR_MSG  errMsg);

static amdlibCOMPL_STAT amdlibWriteOiVisSelectedOnly( fitsfile        *filePtr,
		char            *insName,
		char            *arrName,
		amdlibVIS       *vis,
		amdlibSELECTION *selectedFrames,
		amdlibBAND            band,
		amdlibERROR_MSG errMsg);

static amdlibCOMPL_STAT amdlibWriteOiVis2SelectedOnly( fitsfile        *filePtr,
		char            *insName,
		char            *arrName,
		amdlibVIS2      *vis2,
		amdlibSELECTION *selectedFrames,
		amdlibBAND            band,
		amdlibERROR_MSG errMsg );

static amdlibCOMPL_STAT amdlibWriteOiVis3SelectedOnly( fitsfile        *filePtr,
		char            *insName,
		char            *arrName,
		amdlibVIS3      *vis3,
		amdlibSELECTION *selectedFrames,
		amdlibBAND            band,
		amdlibERROR_MSG errMsg );

static amdlibCOMPL_STAT amdlibWriteAmberDataSelectedOnly( fitsfile         *filePtr,
		char             *insName,
		amdlibPHOTOMETRY *photometry,
		amdlibVIS        *vis,
		amdlibPISTON     *pst,
		amdlibWAVELENGTH *wave,
		amdlibSELECTION  *selectedFrames,
		amdlibBAND       bandfct,
		amdlibERROR_MSG  errMsg );

static amdlibCOMPL_STAT amdlibSaveOiFileESO( const char           *filename,
		/*                                      amdlibINS_CFG        *insCfg,*/
		amdlibSELECTION      *selectedFrames,
		amdlibOI_ARRAY       *array,
		amdlibOI_TARGET      *target,
		amdlibPHOTOMETRY     *photometry,
		amdlibVIS            *vis,
		amdlibVIS2           *vis2,
		amdlibVIS3           *vis3,
		amdlibWAVELENGTH     *wave,
		amdlibPISTON         *pst,
		amdlibBAND            band,
		amdlibSPECTRUM       *spectrum,
		amdlibERROR_MSG      errMsg );

static amdlibCOMPL_STAT amdlibGetThresholdESO(double          *array,
		int             arraySize,
		double          frameSelectionRatio,
		double          *threshold,
		amdlibERROR_MSG errMsg);

static cpl_propertylist * SelectorCreateProduct( const char * fctid, char * szRawFile,
		int iIsScience, char * szProductFile, cpl_propertylist * qclist,
		cpl_frameset  * framelist, cpl_parameterlist * parlist,
		const char * recipename);

static void SetupSelected( int iInclude, amdlibSELECTION * selectedFrames,
		amdlibVIS * vis,  amdlibBAND band);


/*-----------------------------------------------------------------------------
  Static variables
  -----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
  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)






/**
 * Computes the selection threshold, ie the minimal possible value for frame
 * selection criterion so as to a frame should be selected.
 *
 * @param array input data
 * @param arraySize size of array.
 * @param frameSelectionRatio percentage of good frames to keep.
 * @param threshold output threshold.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
static amdlibCOMPL_STAT amdlibGetThresholdESO(double          *array,
		int             arraySize,
		double          frameSelectionRatio,
		double          *threshold,
		amdlibERROR_MSG errMsg)
{
	int    i, j;
	double val;
	int    index;
	amdlibBOOLEAN isSorted;
	double * phantomArray;

	phantomArray = cpl_calloc(arraySize, sizeof(double));
	memcpy(phantomArray, array, arraySize*sizeof(double));
	/* Protected Bubble sort, leaves array invariant! */
	isSorted = amdlibFALSE;
	i = 0;
	while ((i < arraySize) && (isSorted == amdlibFALSE))
	{
		/* Assume array is already sorted */
		isSorted = amdlibTRUE;
		for (j = 1; j < arraySize - i; j++)
		{
			if (phantomArray[j] < phantomArray[j-1])
			{
				/* Invert both values */
				val = phantomArray[j-1];
				phantomArray[j-1] = phantomArray[j];
				phantomArray[j] = val;

				/* phantomArray is not sorted yet */
				isSorted = amdlibFALSE;
			}
		}
		i++;
	}

	/* Find threshold */
	index = floor((1.0-frameSelectionRatio)*arraySize);
	*threshold = phantomArray[index];
	cpl_free(phantomArray);

	if (isnan(*threshold))
	{
		amdlibSetErrMsg("Impossible to determine threshold; probably "
				"due to poor data quality");
		return amdlibFAILURE;
	}

	return amdlibSUCCESS;
}

/**
 * Write OI_ARRAY table in OI-FITS file
 *
 * This function writes the OI_ARRAY binary table defined in the IAU standard
 * in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created
 * @param array OI_ARRAY produced
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiArray(fitsfile        *filePtr,
		amdlibOI_ARRAY  *array,
		amdlibERROR_MSG errMsg)
{
	const int  tfields = 5;
	char       *ttype[] = {"TEL_NAME", "STA_NAME", "STA_INDEX", "DIAMETER",
			"STAXYZ"};
	char       *tform[] = {"8A", "8A", "I", "E", "3D"};
	char       *tunit[] = {"\0", "\0", "\0", "m", "m"};
	char       extname[] = "OI_ARRAY";
	int        status = 0;
	int        iRow;
	char       fitsioMsg[256];
	int        revision = amdlib_OI_REVISION;
	char       *str;

	amdlibLogTrace("amdlibWriteOiArray()");

	/* Algorithm */

	/* Create binary table */
	if (fits_create_tbl(filePtr, BINARY_TBL, 1, tfields, ttype, tform, tunit,
			extname, &status))
	{
		amdlibReturnFitsError("Creating Binary Table");
	}

	/* Write revision number */
	if (fits_write_key(filePtr, TINT, "OI_REVN", &revision,
			"Revision number of the table definition", &status))
	{
		amdlibReturnFitsError("OI_REVN");
	}

	/* Write array name */
	if (fits_write_key(filePtr, TSTRING, "ARRNAME", array->arrayName,
			"Array name", &status))
	{
		amdlibReturnFitsError("ARRNAME");
	}

	/* Write coordinate frame */
	if (fits_write_key(filePtr, TSTRING, "FRAME", array->coordinateFrame,
			"Coordinate frame", &status))
	{
		amdlibReturnFitsError("FRAME");
	}

	/* Write array center x coordinate */
	if (fits_write_key(filePtr, TDOUBLE, "ARRAYX",
			&array->arrayCenterCoordinates[0],
			"Array centre x coordinate", &status))
	{
		amdlibReturnFitsError("ARRAYX");
	}

	if (fits_write_key_unit(filePtr, "ARRAYX", "m", &status))
	{
		amdlibReturnFitsError("ARRAYX");
	}

	/* Write array center y coordinate */
	if (fits_write_key(filePtr, TDOUBLE, "ARRAYY",
			&array->arrayCenterCoordinates[1],
			"Array centre y coordinate", &status))
	{
		amdlibReturnFitsError("ARRAYY");
	}

	if (fits_write_key_unit(filePtr, "ARRAYY", "m", &status))
	{
		amdlibReturnFitsError("ARRAYY");
	}

	/* Write array center z coordinate */
	if (fits_write_key(filePtr, TDOUBLE, "ARRAYZ",
			&array->arrayCenterCoordinates[2],
			"Array centre z coordinate", &status))
	{
		amdlibReturnFitsError("ARRAYZ");
	}

	if (fits_write_key_unit(filePtr, "ARRAYZ", "m", &status))
	{
		amdlibReturnFitsError("ARRAYZ");
	}

	/* Write information on instrument */
	for (iRow = 1; iRow <= array->nbStations; iRow++)
	{
		str = array->element[iRow-1].telescopeName;
		if (fits_write_col(filePtr, TSTRING, 1, iRow, 1, 1, &str, &status))
		{
			amdlibReturnFitsError("telescopeName");
		}
		str = array->element[iRow-1].stationName;
		if (fits_write_col(filePtr, TSTRING, 2, iRow, 1, 1, &str, &status))
		{
			amdlibReturnFitsError("stationName");
		}
		if (fits_write_col(filePtr, TINT, 3, iRow, 1, 1,
				&array->element[iRow-1].stationIndex, &status))
		{
			amdlibReturnFitsError("stationIndex");
		}
		if (fits_write_col(filePtr, TDOUBLE, 4, iRow, 1, 1,
				&array->element[iRow-1].elementDiameter, &status))
		{
			amdlibReturnFitsError("diameter");
		}
		if (fits_write_col(filePtr, TDOUBLE, 5, iRow, 1, 3,
				array->element[iRow-1].stationCoordinates, &status))
		{
			amdlibReturnFitsError("staXYZ");
		}
	}
	return amdlibSUCCESS;
}

/**
 * Write OI_TARGET table in OI-FITS file.
 *
 * This function writes the OI_TARGET binary table defined in the IAU standard
 * in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param target OI_TARGET produced.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiTarget(fitsfile        *filePtr,
		amdlibOI_TARGET *target,
		amdlibERROR_MSG errMsg)
{
	const int  tfields = 17;
	char       *ttype[] = {"TARGET_ID", "TARGET", "RAEP0", "DECEP0", "EQUINOX",
			"RA_ERR", "DEC_ERR", "SYSVEL", "VELTYP", "VELDEF", "PMRA", "PMDEC",
			"PMRA_ERR", "PMDEC_ERR", "PARALLAX", "PARA_ERR", "SPECTYP"};
	char       *tform[] = {"I", "16A", "D", "D", "E", "D", "D", "D", "8A",
			"8A", "D", "D", "D", "D", "E", "E", "16A"};
	char       *tunit[] = {"\0", "\0", "deg", "deg", "year", "deg", "deg",
			"m/s", "\0", "\0", "deg/year", "deg/year", "deg/year", "deg/year",
			"deg", "deg", "\0"};
	char       extname[] = "OI_TARGET";
	char       fitsioMsg[256];
	int        status = 0;
	int        revision = amdlib_OI_REVISION;
	int        iRow;
	char       *str;

	amdlibLogTrace("amdlibWriteOiTarget()");

	/* Create binary table */
	if (fits_create_tbl(filePtr, BINARY_TBL, 1, tfields, ttype, tform, tunit,
			extname, &status))
	{
		amdlibReturnFitsError("Creating Binary Table");
	}

	/* Write revision number of the table definition */
	if (fits_write_key(filePtr, TINT, "OI_REVN", &revision,
			"Revision number of the table definition", &status))
	{
		amdlibReturnFitsError("OI_REVN");
	}

	for (iRow = 1; iRow <= target->nbTargets; iRow++)
	{
		/* Write target identity */
		if (fits_write_col(filePtr, TINT, 1, iRow, 1, 1,
				&target->element[iRow-1].targetId, &status))
		{
			amdlibReturnFitsError("targetId");
		}

		str = target->element[iRow-1].targetName;

		/* Write target name */
		if (fits_write_col(filePtr, TSTRING, 2, iRow, 1, 1, &str, &status))
		{
			amdlibReturnFitsError("targetName");
		}

		/* Write right ascension at mean equinox */
		if (fits_write_col(filePtr, TDOUBLE, 3, iRow, 1, 1,
				&target->element[iRow-1].raEp0, &status))
		{
			amdlibReturnFitsError("raEp0");
		}
		/* Write declinaison at mean equinox */
		if (fits_write_col(filePtr, TDOUBLE, 4, iRow, 1, 1,
				&target->element[iRow-1].decEp0, &status))
		{
			amdlibReturnFitsError("decEp0");
		}
		/* Write equinox of raEp0 and decEp0 */
		if (fits_write_col(filePtr, TDOUBLE, 5, iRow, 1, 1,
				&target->element[iRow-1].equinox, &status))
		{
			amdlibReturnFitsError("equinox");
		}
		/* Write error in apparent RA */
		if (fits_write_col(filePtr, TDOUBLE, 6, iRow, 1, 1,
				&target->element[iRow-1].raErr, &status))
		{
			amdlibReturnFitsError("raErr");
		}
		/* Write error in apparent DEC */
		if (fits_write_col(filePtr, TDOUBLE, 7, iRow, 1, 1,
				&target->element[iRow-1].decErr, &status))
		{
			amdlibReturnFitsError("decErr");
		}
		/* Write systemic radial velocity */
		if (fits_write_col(filePtr, TDOUBLE, 8, iRow, 1, 1,
				&target->element[iRow-1].sysVel, &status))
		{
			amdlibReturnFitsError("sysVel");
		}

		str = target->element[iRow-1].velTyp;
		/* Write velocity type of sysVel */
		if (fits_write_col(filePtr, TSTRING, 9, iRow, 1, 1, &str, &status))
		{
			amdlibReturnFitsError("velTyp");
		}

		str = target->element[iRow-1].velDef;
		/* Write definition of radial velocity : "OPTICAL" or "RADIO" */
		if (fits_write_col(filePtr, TSTRING, 10, iRow, 1, 1, &str, &status))
		{
			amdlibReturnFitsError("velDef");
		}

		/* Write proper motion in RA */
		if (fits_write_col(filePtr, TDOUBLE, 11, iRow, 1, 1,
				&target->element[iRow-1].pmRa, &status))
		{
			amdlibReturnFitsError("pmRa");
		}
		/* Write proper motion in DEC */
		if (fits_write_col(filePtr, TDOUBLE, 12, iRow, 1, 1,
				&target->element[iRow-1].pmDec, &status))
		{
			amdlibReturnFitsError("pmDec");
		}
		/* Write error on pmRa */
		if (fits_write_col(filePtr, TDOUBLE, 13, iRow, 1, 1,
				&target->element[iRow-1].pmRaErr, &status))
		{
			amdlibReturnFitsError("pmRaErr");
		}
		/* Write error on pmDec */
		if (fits_write_col(filePtr, TDOUBLE, 14, iRow, 1, 1,
				&target->element[iRow-1].pmDecErr, &status))
		{
			amdlibReturnFitsError("pmDecErr");
		}
		/* Write parallax value */
		if (fits_write_col(filePtr, TDOUBLE, 15, iRow, 1, 1,
				&target->element[iRow-1].parallax, &status))
		{
			amdlibReturnFitsError("parallax");
		}
		/* Write error in parallax value */
		if (fits_write_col(filePtr, TDOUBLE, 16, iRow, 1, 1,
				&target->element[iRow-1].paraErr, &status))
		{
			amdlibReturnFitsError("paraErr");
		}
		/* Write spectral type */
		str = target->element[iRow-1].specTyp;
		if (fits_write_col(filePtr, TSTRING, 17, iRow, 1, 1, &str, &status))
		{
			amdlibReturnFitsError("specTyp");
		}
	}
	return amdlibSUCCESS;
}


/**
 * Write OI_WAVELENGTH table in OI-FITS file.
 *
 * This function writes the OI_WAVELENGTH binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param insName name of the instrument.
 * @param wave OI_WAVELENGTH produced.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiWavelength(fitsfile         *filePtr,
		char             *insName,
		amdlibWAVELENGTH *wave,
		amdlibERROR_MSG  errMsg)
{
	int        status = 0;
	char       fitsioMsg[256];
	const int  tfields = 2;
	char       *ttype[] = {"EFF_WAVE", "EFF_BAND"};
	char       *tform[] = {"E", "E"};
	char       *tunit[] = {"m", "m"};
	char       extname[] = "OI_WAVELENGTH";
	int        revision = amdlib_OI_REVISION;
	int        i;
	amdlibDOUBLE      waveInM, bwInM;

	amdlibLogTrace("amdlibWriteOiWavelength()");

	/* Create binary table */
	if (fits_create_tbl (filePtr, BINARY_TBL, 1, tfields, ttype, tform, tunit,
			extname, &status))
	{
		amdlibReturnFitsError("Creating Binary Table");
	}

	/* Write revision number of the table definition */
	if (fits_write_key (filePtr, TINT, "OI_REVN", &revision,
			"Revision number of the table definition", &status))
	{
		amdlibReturnFitsError("OI_REVN");
	}

	/* Write spectral setup unique identifier */
	if (fits_write_key (filePtr, TSTRING, "INSNAME", insName,
			"Instrument name", &status))
	{
		amdlibReturnFitsError("INSNAME");
	}

	for (i = 1; i <= wave->nbWlen; i ++)
	{
		waveInM = (wave->wlen[i-1]) * amdlibNM_TO_M;
		bwInM = (wave->bandwidth[i-1]) * amdlibNM_TO_M;
		/* Write EFFective_WAVElengh table */
		if (fits_write_col (filePtr, TDOUBLE, 1, i, 1, 1, &waveInM, &status))
		{
			amdlibReturnFitsError("EFFective_WAVElengh");
		}

		/* Write EFFective_BANDwidth table */
		if (fits_write_col (filePtr, TDOUBLE, 2, i, 1, 1, &bwInM, &status))
		{
			amdlibReturnFitsError("EFFective_BANDwidth");
		}
	}

	return amdlibSUCCESS;
}


#define amdlibWriteOiVis_FREEALL()  free(visError); free(flag); free(convertToDeg);


/**
 * Write OI_VIS table in OI-FITS file
 *
 * This function writes the OI_VIS binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created
 * @param insName name of the detector
 * @param arrName array name 
 * @param vis OI_VIS produced
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiVisSelectedOnly(fitsfile        *filePtr, 
		char            *insName,
		char            *arrName,
		amdlibVIS       *vis,
		amdlibSELECTION *selectedFrames,
		amdlibBAND            band,
		amdlibERROR_MSG errMsg)

{
	/* Local Declarations */

	int           nbWlen = vis->nbWlen;
	int           lVis;
	double        *convertToDeg;
	amdlibCOMPLEX *visError;
	unsigned char *flag;
	amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;

	int           status = 0;
	char          fitsioMsg[256];
	const int     tfields = 14;
	char          *ttype[] = {"TARGET_ID", "TIME", "MJD", "INT_TIME",
			"VISDATA", "VISERR", "VISAMP", "VISAMPERR", "VISPHI", "VISPHIERR",
			"UCOORD", "VCOORD", "STA_INDEX", "FLAG"};
	char          *Tform[] = {"I", "D", "D", "D", "?C", "?C", "?D", "?D",
			"?D", "?D", "1D", "1D", "2I", "?L"};
	char          *tform[tfields];
	char          *tunit[] = {"\0", "s", "day", "s", "\0", "\0", "\0", "\0",
			"deg", "deg", "m", "m", "\0", "\0"};
	char          extname[] = "OI_VIS";
	int           revision = amdlib_OI_REVISION;
	char          tmp[16];
	int           iFrame = 0, iBase = 0, iVis = 0, i = 0;
	double        expTime;
	int iReal=0;
	amdlibLogTrace("amdlibWriteOiVis()");

	/* Algorithm */
	if (vis->thisPtr != vis)
	{
		amdlibSetErrMsg("Unitialized vis structure");
		return amdlibFAILURE;
	}

	if (vis->nbFrames < 1)
	{
		return amdlibSUCCESS;
	}

	/* Create table structure: Make up Tform: substitute nbWlen for '?' */
	for (i = 0; i < tfields; i++)
	{
		if (Tform[i][0] == '?')
		{
			sprintf(tmp, "%d%s", nbWlen, &Tform[i][1]);
			tform[i] = calloc((strlen(tmp)+1), sizeof(char));
			strcpy(tform[i], tmp);
		}
		else
		{
			tform[i] = calloc((strlen(Tform[i])+1), sizeof(char));
			strcpy(tform[i], Tform[i]);
		}
	}
	if (fits_create_tbl(filePtr, BINARY_TBL, 0, tfields, ttype,
			tform, tunit, extname, &status))
	{
		amdlibReturnFitsError("BINARY_TBL");
	}

	for (i = 0; i < tfields; i++)
	{
		free(tform[i]);
	}

	/* Write revision number of the table definition */
	if (fits_write_key(filePtr, TINT, "OI_REVN", &revision,
			"Revision number of the table definition", &status))
	{
		amdlibReturnFitsError("OI_REVN");
	}

	/* Give date */
	if (fits_write_key(filePtr, TSTRING, "DATE-OBS", vis->dateObs,
			"UTC start date of observations", &status))
	{
		amdlibReturnFitsError("DATE-OBS");
	}

	/* Write names of array and detector */
	if (fits_write_key (filePtr, TSTRING, "ARRNAME", arrName,
			"Array Name", &status))
	{
		amdlibReturnFitsError("ARRNAME");
	}
	if (fits_write_key(filePtr, TSTRING, "INSNAME", insName,
			"Instrument name", &status))
	{
		amdlibReturnFitsError("INSNAME");
	}

	visError  = calloc(nbWlen, sizeof(amdlibCOMPLEX));
	convertToDeg  = calloc(nbWlen, sizeof(double));
	flag = calloc(nbWlen, sizeof(unsigned char));

	/* Write columns */
	for (iFrame = 0; iFrame < vis->nbFrames; iFrame++)
	{
		for (iBase = 0; iBase < vis->nbBases; iBase++)
		{
			if( selectedFrames->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE )
			{

				iReal = (iFrame*vis->nbBases)+iBase;
				iVis++;

				/* Write TARGET_IDentity as an index into IO_TARGET table */
				if (fits_write_col(filePtr, TINT, 1, iVis, 1, 1,
						&(vis->table[iReal].targetId), &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("TARGET_IDentity");
				}

				/* Write utc TIME of observation */
				if (fits_write_col(filePtr, TDOUBLE, 2, iVis, 1, 1,
						&(vis->table[iReal].time), &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("TIME");
				}

				/* Write observation date in ModifiedJulianDay */
				if (fits_write_col(filePtr, TDOUBLE, 3, iVis, 1, 1,
						&(vis->table[iReal].dateObsMJD), &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("ModifiedJulianDay");
				}

				/* Write INTegration_TIME (seconds) */
				expTime = (double)vis->table[iReal].expTime;
				if (fits_write_col(filePtr, TDOUBLE, 4, iVis, 1, 1,
						&expTime, &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("INTegration_TIME");
				}

				/* Writing ComPleXVISibility (no unit) */
				if (fits_write_col(filePtr,TDBLCOMPLEX, 5, iVis, 1, nbWlen,
						(double*)vis->table[iReal].vis, &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("ComPleXVISibility");
				}

				/* Write ComPleXVISibilityERRor (no unit) */
				/* Need to write Error, NOT sigma2 */
				for (lVis = 0; lVis < nbWlen; lVis++)
				{
					visError[lVis].re =
							amdlibSignedSqrt(vis->table[iReal].sigma2Vis[lVis].re);
					visError[lVis].im =
							amdlibSignedSqrt(vis->table[iReal].sigma2Vis[lVis].im);
				}

				if (fits_write_col(filePtr,TDBLCOMPLEX,  6, iVis, 1,
						nbWlen, (double *)visError, &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("ComPleXVISibilityERRor");
				}

				/* Write VISibilityAMPlitude (no unit) */
				if (fits_write_col(filePtr, TDOUBLE, 7, iVis, 1, nbWlen,
						vis->table[iReal].diffVisAmp, &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("VISibilityAMPlitude");
				}

				/* Write VISibilityAMPlitudeERRor (no unit) */
				if (fits_write_col(filePtr, TDOUBLE, 8, iVis, 1, nbWlen,
						vis->table[iReal].diffVisAmpErr, &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("VISibilityAMPlitudeERRor");
				}

				/* Write VISibilityPHI (degrees) */
				for(lVis = 0; lVis < nbWlen; lVis++)
				{
					if(vis->table[iReal].diffVisPhi[lVis]!=amdlibBLANKING_VALUE)
					{
						convertToDeg[lVis] = 180.0 / M_PI *
								vis->table[iReal].diffVisPhi[lVis];
					}
					else
					{
						convertToDeg[lVis] = amdlibBLANKING_VALUE;
					}
				}

				if (fits_write_colnull(filePtr, TDOUBLE, 9, iVis, 1, nbWlen,
						convertToDeg, &amdlibBval, &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("VISibilityPHI");
				}

				/* Write VISibilityPHIERRor (degrees) */
				for(lVis = 0; lVis < nbWlen; lVis++)
				{
					if(vis->table[iReal].diffVisPhiErr[lVis]!=amdlibBLANKING_VALUE)
					{
						convertToDeg[lVis] = 180.0 / M_PI *
								vis->table[iReal].diffVisPhiErr[lVis];
					}
					else
					{
						convertToDeg[lVis] = amdlibBLANKING_VALUE;
					}
				}
				if (fits_write_colnull(filePtr, TDOUBLE, 10, iVis, 1, nbWlen,
						convertToDeg, &amdlibBval, &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("VISibilityPHIERRor");
				}
				/* Write UCOORDinate (meters) */
				if (fits_write_col(filePtr, TDOUBLE, 11, iVis, 1, 1,
						&(vis->table[iReal].uCoord), &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("UCOORDinate");
				}

				/* Write VCOORDinates (meters) */
				if (fits_write_col(filePtr, TDOUBLE, 12, iVis, 1, 1,
						&(vis->table[iReal].vCoord), &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("VCOORDinates");
				}

				/* Write STAtion_INDEX contributing to the data */
				if (fits_write_col(filePtr, TINT, 13, iVis, 1, 2,
						vis->table[iReal].stationIndex, &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("STAtion_INDEX");
				}

				/* Write flag as unsigned char -- does not work with booleans */
				for (lVis = 0; lVis < nbWlen; lVis++)
				{
					flag[lVis] = vis->table[iReal].flag[lVis];
				}

				if (fits_write_col(filePtr, TLOGICAL, 14, iVis, 1, nbWlen,
						flag, &status))
				{
					amdlibWriteOiVis_FREEALL();
					amdlibReturnFitsError("FLAG");
				}
			} /* if frame was selected */
		}
	}
	/* free allocated space at end of job */
	amdlibWriteOiVis_FREEALL();
	return amdlibSUCCESS;
}
#undef   amdlibWriteOiVis_FREEALL

#define amdlibWriteOiVis2_FREEALL()  free(flag);
/**
 * Write OI_VIS2 table in OI-FITS file
 *
 * This function writes the OI_VIS2 binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created
 * @param insName name of the detector
 * @param arrName array name 
 * @param vis2 OI_VIS2 produced
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiVis2SelectedOnly( fitsfile        *filePtr,
		char            *insName,
		char            *arrName,
		amdlibVIS2      *vis2,
		amdlibSELECTION *selectedFrames,
		amdlibBAND            band,

		amdlibERROR_MSG errMsg)
{
	int        nbWlen = vis2->nbWlen;
	int        lVis;
	int        status = 0;
	char       fitsioMsg[256];
	const int  tfields = 10;
	char       *ttype[] = {"TARGET_ID", "TIME", "MJD", "INT_TIME", "VIS2DATA",
			"VIS2ERR", "UCOORD", "VCOORD", "STA_INDEX", "FLAG"};
	char       *Tform[] = {"I", "D", "D", "D", "?D", "?D", "1D", "1D", "2I",
			"?L"};
	char       *tform[tfields];
	char       *tunit[] = {"\0", "s", "day", "s", "\0", "\0", "m", "m", "\0",
			"\0"};
	char       extname[] = "OI_VIS2";
	int        revision = amdlib_OI_REVISION;
	int        iFrame = 0, iBase = 0, iVis = 0, i = 0;
	char       tmp[16];
	double     expTime;
	unsigned char *flag;
	int iReal=0;
	amdlibLogTrace("amdlibWriteOiVis2()");

	if (vis2->thisPtr != vis2)
	{
		amdlibSetErrMsg("Unitialized vis2 structure");
		return amdlibFAILURE;
	}
	if (vis2->nbFrames < 1)
	{
		return amdlibSUCCESS;
	}

	/* Create table structure. Make up Tform: substitute nbWlen for '?' */
	for (i = 0; i < tfields; i++)
	{
		if (Tform[i][0] == '?')
		{
			sprintf(tmp, "%d%s", nbWlen, &Tform[i][1]);
			tform[i] = calloc((strlen(tmp)+1), sizeof(char));
			strcpy(tform[i], tmp);
		}
		else
		{
			tform[i] = calloc((strlen(Tform[i])+1), sizeof(char));
			strcpy(tform[i], Tform[i]);
		}
	}

	if (fits_create_tbl (filePtr, BINARY_TBL, 0, tfields,
			ttype, tform, tunit, extname, &status))
	{
		amdlibReturnFitsError("BINARY_TBL");
	}

	for (i = 0; i < tfields; i++)
	{
		free(tform[i]);
	}

	/* Write revision number of the table definition */
	if (fits_write_key (filePtr, TINT, "OI_REVN", &revision,
			"Revision number of the table definition", &status))
	{
		amdlibReturnFitsError("OI_REVN");
	}

	/* Give date */
	if (fits_write_key(filePtr, TSTRING,"DATE-OBS", vis2->dateObs,
			"UTC start date of observations", &status))
	{
		amdlibReturnFitsError("DATE-OBS");
	}

	/* Write array and detector names */
	if (fits_write_key (filePtr, TSTRING, "ARRNAME", arrName,
			"Array Name", &status))
	{
		amdlibReturnFitsError("ARRNAME");
	}
	if (fits_write_key(filePtr, TSTRING, "INSNAME", insName,
			"Instrument name", &status))
	{
		amdlibReturnFitsError("INSNAME");
	}

	flag = calloc(nbWlen, sizeof(unsigned char));

	/* Write columns */
	for (iFrame = 0; iFrame < vis2->nbFrames; iFrame++)
	{
		for (iBase = 0; iBase < vis2->nbBases; iBase++)
		{
			if( selectedFrames->band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE )
			{
				iReal = (iFrame*vis2->nbBases)+iBase;
				iVis++;
				/* Write TARGET_IDentity */
				if (fits_write_col (filePtr, TINT, 1, iVis, 1, 1,
						&(vis2->table[iReal].targetId), &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("TARGET_IDentity");
				}

				/* Write time */
				if (fits_write_col (filePtr, TDOUBLE, 2, iVis, 1, 1,
						&(vis2->table[iReal].time), &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("Time");
				}
				if (fits_write_col (filePtr, TDOUBLE, 3, iVis, 1, 1,
						&(vis2->table[iReal].dateObsMJD), &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("dateObsMJD");
				}
				expTime = (double)vis2->table[iReal].expTime;
				if (fits_write_col (filePtr, TDOUBLE, 4, iVis, 1, 1,
						&expTime, &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("expTime");
				}

				/* Write squared visibility */
				if (fits_write_col(filePtr, TDOUBLE, 5, iVis, 1, nbWlen,
						vis2->table[iReal].vis2, &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("vis2");
				}
				/* Write error in the squared visibility */
				if (fits_write_col(filePtr, TDOUBLE, 6, iVis, 1, nbWlen,
						vis2->table[iReal].vis2Error, &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("vis2Error");
				}

				/* Write U and V coordinates on the data */
				if (fits_write_col (filePtr, TDOUBLE, 7, iVis, 1, 1,
						&(vis2->table[iReal].uCoord), &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("uCoord");
				}
				if (fits_write_col (filePtr, TDOUBLE, 8, iVis, 1, 1,
						&(vis2->table[iReal].vCoord), &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("vCoord");
				}

				/* Write station index */
				if (fits_write_col (filePtr, TINT, 9, iVis, 1, 2,
						vis2->table[iReal].stationIndex, &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("stationIndex");
				}
				/* Write flag as unsigned char -- does not work with booleans */
				for (lVis = 0; lVis < nbWlen; lVis++)
				{
					flag[lVis] = vis2->table[iReal].flag[lVis];
				}
				if (fits_write_col(filePtr, TLOGICAL, 10, iVis, 1, nbWlen,
						flag, &status))
				{
					amdlibWriteOiVis2_FREEALL();
					amdlibReturnFitsError("FLAG");
				}
			} /* selected frames only */
		}
	}
	/* free allocated space at end of job */
	amdlibWriteOiVis2_FREEALL();
	return amdlibSUCCESS;
}
#define amdlibWriteOiVis3_FREEALL() free(flag); free(convertToDeg);
/**
 * Write OI_T3 table in OI-FITS file.
 *
 * This function writes the OI_T3 binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param insName name of the detector.
 * @param arrName array name.
 * @param vis3 OI_T3 produced.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiVis3SelectedOnly(fitsfile        *filePtr,
		char            *insName,
		char            *arrName,
		amdlibVIS3      *vis3,
		amdlibSELECTION *selectedFrames,
		amdlibBAND            band,
		amdlibERROR_MSG errMsg)
{
	int        nbWlen =vis3->nbWlen;
	int        lVis;
	int        status = 0;
	char       fitsioMsg[256];
	const int  tfields = 14;
	char       *ttype[] = {"TARGET_ID", "TIME", "MJD", "INT_TIME", "T3AMP",
			"T3AMPERR", "T3PHI", "T3PHIERR", "U1COORD", "V1COORD", "U2COORD",
			"V2COORD", "STA_INDEX", "FLAG"};
	char       *Tform[] = {"I", "D", "D", "D", "?D", "?D", "?D", "?D", "1D",
			"1D", "1D", "1D", "3I", "?L"};
	char       *tform[tfields];
	char       *tunit[] = {"\0", "s", "day", "s", "\0", "\0", "deg", "deg",
			"m", "m", "m", "m", "\0", "\0"};
	char       extname[] = "OI_T3";
	int        revision = amdlib_OI_REVISION;
	int        i;
	char       tmp[16];
	double     expTime;
	double    *convertToDeg;
	unsigned char *flag;
	amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;

	int        iFrame = 0, iClos = 0, iT3 = 0;

	amdlibLogTrace("amdlibWriteOiVis3()");

	/*Vis3 may not be initialized (only 1 baseline), this is not a defect*/
	if (vis3->thisPtr != vis3)
	{
		return amdlibSUCCESS;
	}
	/*If initialized but empty, do nothing gracefully*/
	if (vis3->nbFrames < 1)
	{
		return amdlibSUCCESS;
	}

	/* Create table structure. Make up Tform: substitute nbWlen for '?' */
	for (i = 0; i < tfields; i++)
	{
		if (Tform[i][0] == '?')
		{
			sprintf(tmp, "%d%s", nbWlen, &Tform[i][1]);
			tform[i] = calloc((strlen(tmp)+1), sizeof(char));
			strcpy(tform[i], tmp);
		}
		else
		{
			tform[i] = calloc((strlen(Tform[i])+1), sizeof(char));
			strcpy(tform[i], Tform[i]);
		}
	}
	if (fits_create_tbl(filePtr, BINARY_TBL, 0, tfields, ttype, tform, tunit,
			extname, &status))
	{
		amdlibReturnFitsError("Creating Binary Table");
	}

	for (i = 0; i < tfields; i++)
	{
		free(tform[i]);
	}

	/* Write revision number of the table definition */
	if (fits_write_key (filePtr, TINT, "OI_REVN", &revision,
			"Revision number of the table definition", &status))
	{
		amdlibReturnFitsError("OI_REVN");
	}

	/* Give date */
	if (fits_write_key(filePtr, TSTRING,"DATE-OBS", vis3->dateObs,
			"UTC start date of observations", &status))
	{
		amdlibReturnFitsError("DATE-OBS");
	}

	/* Write names of detector and array */
	if (fits_write_key (filePtr, TSTRING, "ARRNAME", arrName,
			"Array Name", &status))
	{
		amdlibReturnFitsError("ARRNAME");
	}
	if (fits_write_key(filePtr, TSTRING, "INSNAME", insName,
			"Instrument name", &status))
	{
		amdlibReturnFitsError("INSNAME");
	}

	convertToDeg  = calloc(nbWlen, sizeof(double));
	flag = calloc(nbWlen, sizeof(unsigned char));

	/* Write columns */
	for (iFrame = 0; iFrame < vis3->nbFrames; iFrame++)
	{
		/* Always all bases are either rejected or not. Testing base 0 is hence enough */
		if( selectedFrames->band[band].isSelectedPt[0][iFrame] == amdlibTRUE )
		{

			for (iClos = 0; iClos < vis3->nbClosures; iClos++)
			{
				/* Write dtarget identity */
				if (fits_write_col(filePtr, TINT, 1, iT3+1, 1, 1,
						&(vis3->table[iT3].targetId), &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("TARGET_ID");
				}

				/* Write time */
				if (fits_write_col(filePtr, TDOUBLE, 2, iT3+1, 1, 1,
						&(vis3->table[iT3].time), &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("TIME");
				}
				if (fits_write_col(filePtr, TDOUBLE, 3, iT3+1, 1, 1,
						&(vis3->table[iT3].dateObsMJD), &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("DATEOBS");
				}
				expTime = (double)vis3->table[iT3].expTime;
				if (fits_write_col(filePtr, TDOUBLE, 4, iT3+1, 1, 1,
						&expTime, &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("EXPTIME");
				}

				/* Write visibility amplitude and its associated error */
				if (fits_write_col(filePtr, TDOUBLE, 5, iT3+1, 1, nbWlen,
						vis3->table[iT3].vis3Amplitude, &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("VIS3AMP");
				}
				if (fits_write_col(filePtr, TDOUBLE, 6, iT3+1, 1, nbWlen,
						vis3->table[iT3].vis3AmplitudeError,
						&status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("VIS3AMPERR");
				}

				/* Write visibility phase and its associated error,
				 * converted in degrees */
				for (lVis = 0; lVis < nbWlen; lVis++)
				{
					if(vis3->table[iT3].vis3Phi[lVis]!=amdlibBLANKING_VALUE)
					{
						convertToDeg[lVis] = 180.0 / M_PI *
								vis3->table[iT3].vis3Phi[lVis];
					}
					else
					{
						convertToDeg[lVis] =amdlibBLANKING_VALUE;
					}

				}
				if (fits_write_colnull(filePtr, TDOUBLE, 7, iT3+1, 1, nbWlen,
						convertToDeg, &amdlibBval, &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("VIS3PHI");
				}
				for(lVis = 0; lVis < nbWlen; lVis++)
				{
					if(vis3->table[iT3].vis3PhiError[lVis]!=amdlibBLANKING_VALUE)
					{
						convertToDeg[lVis] = 180.0 / M_PI *
								vis3->table[iT3].vis3PhiError[lVis];
					}
					else
					{
						convertToDeg[lVis] =amdlibBLANKING_VALUE;
					}
				}
				if (fits_write_colnull(filePtr, TDOUBLE, 8, iT3+1, 1, nbWlen,
						convertToDeg, &amdlibBval, &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("VIS3PHIERR");
				}

				/* Write U and V coordinates of the data */
				if (fits_write_col(filePtr, TDOUBLE, 9, iT3+1, 1, 1,
						&(vis3->table[iT3].u1Coord), &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("U1COORD");
				}
				if (fits_write_col(filePtr, TDOUBLE, 10, iT3+1, 1, 1,
						&(vis3->table[iT3].v1Coord), &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("V1COORD");
				}
				if (fits_write_col(filePtr, TDOUBLE, 11, iT3+1, 1, 1,
						&(vis3->table[iT3].u2Coord), &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("U2COORD");
				}
				if (fits_write_col(filePtr, TDOUBLE, 12, iT3+1, 1, 1,
						&(vis3->table[iT3].v2Coord), &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("V2COORD");
				}

				/* Write station index */
				if (fits_write_col(filePtr, TINT, 13, iT3+1, 1, 3,
						vis3->table[iT3].stationIndex, &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("STATINDEX");
				}

				/* Write flag as unsigned char -- does not work with booleans */
				for (lVis = 0; lVis < nbWlen; lVis++)
				{
					flag[lVis] = vis3->table[iT3].flag[lVis];
				}
				if (fits_write_col(filePtr, TLOGICAL, 14, iT3+1, 1, nbWlen,
						flag, &status))
				{
					amdlibWriteOiVis3_FREEALL();
					amdlibReturnFitsError("FLAG");
				}

				iT3++;

			}
		} /* Selected Frames only */
	}
	amdlibWriteOiVis3_FREEALL();

	return amdlibSUCCESS;
}

/* ------------------------------------------------------------------------------ */
#define amdlibWriteAmberData_FREEALL() free(errTempVal);          \
		free(tempBandNumber);
/**
 * Write AMBER_DATA table in OI-FITS file.
 *
 * This function writes the AMBER_DATA binary table in the OI-FITS file 
 * given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param insName name of the detector.
 * @param photometry photometry data.
 * @param vis AMBER_DATA produced.
 * @param pst piston data.
 * @param wave structure containing the wavelengths.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteAmberDataSelectedOnly(fitsfile         *filePtr,
		char             *insName,
		amdlibPHOTOMETRY *photometry,
		amdlibVIS        *vis,
		amdlibPISTON     *pst,
		amdlibWAVELENGTH *wave,
		amdlibSELECTION  *selectedFrames,
		amdlibBAND       bandfct,
		amdlibERROR_MSG  errMsg)
{
	int        nbWlen = photometry->nbWlen;
	int        status = 0;
	char       fitsioMsg[256];
	const int  tfields = 13;
	amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;
	char       *Ttype[] =
	{
			"TARGET_ID", "TIME", "MJD", "INT_TIME",
			"FLUX_SUM", "FLUX_SUM_CORRECTION", "FLUX_RATIO",
			"FLUX_RATIO_CORRECTION", "FLUX_PRODUCT",
			"OPD", "OPD_ERR", "FRINGE_SNR", "STA_INDEX"
	};

	char       *Tform[] = {"I", "D", "D", "D", "?D", "?D", "?D", "?D", "?D",
			"?E", "?E", "?D", "2I"};

	char       *Tunit[] = {"\0", "s", "day", "s", "e-", "e-", "\0", "\0",
			"e-^2", "m", "m", "\0", "\0"};

	char       *ttype[tfields];
	char       *tform[tfields];
	char       *tunit[tfields];

	char       extname[] = "AMBER_DATA";
	int        revision = amdlib_OI_REVISION;
	int        colNum;
	int        i;
	int        lVis;
	char       tmp[16];
	int        iFrame = 0, iBase = 0, iVis = 0;
	double     *errTempVal;
	int        *tempBandNumber;
	int        band, iBand;
	int        nbBands = 0;
	amdlibBAND_DESC *bandDesc;
	amdlibDOUBLE      bound;
	char       keyName[amdlibKEYW_NAME_LEN+1];
	double     expTime;
	amdlibDOUBLE      pistonTmpVal[amdlibNB_BANDS];
	amdlibDOUBLE      pstErrTmpVal[amdlibNB_BANDS];
	double     frgTmpVal[amdlibNB_BANDS];
	int iReal=0;
	amdlibLogTrace("amdlibWriteAmberData()");

	if (vis->thisPtr != vis)
	{
		amdlibSetErrMsg("Unitialized vis structure");
		return amdlibFAILURE;
	}

	/*If initialized but empty, do nothing gracefully*/
	if (vis->nbFrames < 1)
	{
		return amdlibSUCCESS;
	}

	if (photometry->thisPtr != photometry)
	{
		amdlibSetErrMsg("Unitialized photometry structure");
		return amdlibFAILURE;
	}
	/*If initialized but empty, do nothing gracefully*/
	if (photometry->nbFrames < 1)
	{
		return amdlibSUCCESS;
	}

	if (pst->thisPtr != pst)
	{
		amdlibSetErrMsg("Unitialized piston structure");
		return amdlibFAILURE;
	}
	/*If initialized but empty, do nothing gracefully*/
	if (pst->nbFrames < 1)
	{
		return amdlibSUCCESS;
	}

	/* Create table structure. Make up Tform: substitute nbWlen or
	 * nbBands for '?' */
	/* (informational) check number of bands */
	nbBands=0;
	for (band = amdlibJ_BAND; band <= amdlibK_BAND; band++)
	{
		if (pst->bandFlag[band] == amdlibTRUE)
		{
			nbBands++;
		}
	}
	for (i = 0; i < tfields; i++)
	{
		ttype[i] = calloc(strlen(Ttype[i])+1, sizeof(char));
		strcpy(ttype[i],Ttype[i]);
		tunit[i] = calloc(strlen(Tunit[i])+1, sizeof(char));
		strcpy(tunit[i],Tunit[i]);

		if (Tform[i][0] == '?')
		{
			if ((i == 9) || (i == 10) || (i == 11))
			{
				sprintf(tmp, "%d%s", nbBands, &Tform[i][1]);
			}
			else
			{
				sprintf(tmp, "%d%s", nbWlen, &Tform[i][1]);
			}
			tform[i] = calloc((strlen(tmp)+1), sizeof(char));
			strcpy(tform[i], tmp);
		}
		else
		{
			tform[i] = calloc((strlen(Tform[i])+1), sizeof(char));
			strcpy(tform[i], Tform[i]);
		}
	}

	if (fits_create_tbl(filePtr, BINARY_TBL, 0, tfields, ttype, tform, tunit,
			extname, &status))
	{
		amdlibReturnFitsError("Creating Binary Table");
	}

	for (i = 0; i < tfields; i++)
	{
		free(ttype[i]);
		free(tform[i]);
		free(tunit[i]);
	}

	/* Give date */
	if (fits_write_key(filePtr, TSTRING, "DATE-OBS", vis->dateObs,
			"UTC start date of observations", &status))
	{
		amdlibReturnFitsError("DATE-OBS");
	}

	/* Write detector name */
	if (fits_write_key (filePtr, TSTRING, "INSNAME", insName,
			"Instrument name", &status))
	{
		amdlibReturnFitsError("INSNAME");
	}

	/* Write revision number of the table definition */
	if (fits_write_key (filePtr, TINT, "AMB_REVN", &revision,
			"Revision number of the table definition", &status))
	{
		amdlibReturnFitsError("AMB_REVN");
	}

	/* Write information on spectral bands. */
	if (fits_write_key (filePtr, TINT, "HIERARCH ESO QC NBBANDS",
			&nbBands, "Number of spectral bands", &status))
	{
		amdlibReturnFitsError("HIERARCH ESO QC NBBANDS");
	}

	iBand = 1;
	for (band = amdlibJ_BAND; band <= amdlibK_BAND; band++)
	{
		if (pst->bandFlag[band] == amdlibTRUE)
		{
			bandDesc = amdlibGetBandDescription(band);
			sprintf(keyName, "HIERARCH ESO QC BAND%d NAME", iBand);
			if (fits_write_key(filePtr, TSTRING, keyName, bandDesc->name,
					"Name of spectral band", &status))
			{
				amdlibReturnFitsError(keyName);
			}
			memset(keyName, '\0', (amdlibKEYW_NAME_LEN+1) * sizeof(char));
			sprintf(keyName, "HIERARCH ESO QC BAND%d LOWBOUND", iBand);
			bound = bandDesc->lowerBound/1000;
			if (fits_write_key(filePtr, TDOUBLE, keyName, &bound,
					"Lower bound of spectral band", &status))
			{
				amdlibReturnFitsError(keyName);
			}
			memset(keyName, '\0', (amdlibKEYW_NAME_LEN+1) * sizeof(char));
			sprintf(keyName, "HIERARCH ESO QC BAND%d UPBOUND", iBand);
			bound = bandDesc->upperBound/1000;
			if (fits_write_key(filePtr, TDOUBLE, keyName, &bound,
					"Upper bound of spectral band", &status))
			{
				amdlibReturnFitsError(keyName);
			}
			iBand++;
		}

	}

	errTempVal = calloc(nbWlen, sizeof(double));
	tempBandNumber = calloc(nbWlen, sizeof(int));
	/* Retrieve Band number for each Wlen */
	for (lVis = 0; lVis < nbWlen; lVis++)
	{
		tempBandNumber[lVis] = amdlibGetBand(wave->wlen[lVis]);
	}

	/* Write columns */
	for (iFrame = 0; iFrame < vis->nbFrames; iFrame++)
	{
		/* Always all bases are either rejected or not. Testing base 0 is hence enough */

		if( selectedFrames->band[bandfct].isSelectedPt[0][iFrame] == amdlibTRUE )
		{

			for (iBase = 0; iBase < vis->nbBases; iBase++)
			{
				iReal = (iFrame*vis->nbBases)+iBase;
				colNum = 1;
				iVis++;
				/* Write target identity */
				if (fits_write_col(filePtr, TINT, colNum, iVis, 1, 1,
						&(vis->table[iReal].targetId), &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("Target Id");
				}
				colNum++;

				/* Write time */
				if (fits_write_col(filePtr, TDOUBLE, colNum, iVis, 1, 1,
						&(vis->table[iReal].time), &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("Time");
				}
				colNum++;

				if (fits_write_col(filePtr, TDOUBLE, colNum, iVis, 1, 1,
						&(vis->table[iReal].dateObsMJD), &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("dateObsMJD");
				}
				colNum++;

				expTime = (double)vis->table[iReal].expTime;
				if (fits_write_col(filePtr, TDOUBLE, 4, iVis, 1, 1,
						&expTime, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("expTime");
				}
				colNum++;

				/* Write information on photometry */
				/* Write fluxSumPiPj (number of electrons collected in the spectral
				 * channel) and associated error */
				if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
						photometry->table[iReal].fluxSumPiPj,
						&amdlibBval, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("baseFluxSum");
				}
				colNum++;

				for (lVis = 0; lVis < nbWlen; lVis++)
				{
					if (!(photometry->table[iReal].sigma2FluxSumPiPj[lVis] ==
							amdlibBLANKING_VALUE))
					{
						errTempVal[lVis] =
								sqrt(photometry->table[iReal].sigma2FluxSumPiPj[lVis]);
					}
					else
					{
						errTempVal[lVis] = amdlibBLANKING_VALUE;
					}
				}

				if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
						errTempVal, &amdlibBval, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("baseFluxSumCorrection");
				}
				colNum++;

				/* Write flux ratio in the spectral channel and associated error */
				if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
						photometry->table[iReal].fluxRatPiPj,
						&amdlibBval, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("baseFluxRatio");
				}
				colNum++;

				for (lVis = 0; lVis < nbWlen; lVis++)
				{
					if (!(photometry->table[iReal].sigma2FluxRatPiPj[lVis] ==
							amdlibBLANKING_VALUE))
					{
						errTempVal[lVis]=
								sqrt(photometry->table[iReal].sigma2FluxRatPiPj[lVis]);
					}
					else
					{
						errTempVal[lVis] = amdlibBLANKING_VALUE;
					}
				}
				if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
						errTempVal, &amdlibBval, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("baseFluxRatioCorrection");
				}
				colNum++;

				/* Write flux product */
				if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
						photometry->table[iReal].PiMultPj,
						&amdlibBval, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("baseFluxProduct");
				}
				colNum++;

				/* Write piston value and associated error */
				int i = 0;
				for (band = amdlibJ_BAND; band <= amdlibK_BAND; band++)
				{
					if (pst->bandFlag[band] == amdlibTRUE )
					{
						if (!amdlibCompareDouble(pst->pistonOPDArray[band][iReal],amdlibBLANKING_VALUE))
						{
							pistonTmpVal[i] = pst->pistonOPDArray[band][iReal] *
									amdlibNM_TO_M;
							pstErrTmpVal[i] = pst->sigmaPistonArray[band][iReal] *
									amdlibNM_TO_M;
						}
						else
						{
							pistonTmpVal[i] = amdlibBLANKING_VALUE;
							pstErrTmpVal[i] = amdlibBLANKING_VALUE;
						}
						frgTmpVal[i] = vis->table[iReal].frgContrastSnrArray[band];
						i++;
					}
				}
				if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbBands,
						pistonTmpVal, &amdlibBval, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("piston");
				}
				colNum++;

				if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbBands,
						pstErrTmpVal, &amdlibBval, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("pistonErr");
				}
				colNum++;

				/* Write fringe contrast SNR */
				if (fits_write_col(filePtr, TDOUBLE, colNum, iVis, 1, nbBands,
						frgTmpVal, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("frgContrastSnr");
				}
				colNum++;

				/* Write station idexes corresponding to the baseline */
				if (fits_write_col(filePtr, TINT, colNum, iVis, 1, 2,
						vis->table[iReal].stationIndex, &status))
				{
					amdlibWriteAmberData_FREEALL();
					amdlibReturnFitsError("stationIndex");
				}
				colNum++;
			}
		}/* Selectd Frames only */
	}
	amdlibWriteAmberData_FREEALL();
	return amdlibSUCCESS;
}
#undef   amdlibWriteAmberData_FREEALL




/**
 * Write OI-FITS file.
 *
 * This function create OI-FITS file containing the OI_ARRAY, OI_TARGET, OI_VIS,
 * OI_VIS2, OI_T3 and OI_WAVELENGTH binary tables defined in the IAU standard,
 * and the AMBER_DATA binary table which is specific to AMBER. The AMBER_DATA
 * binary table contains photometry and piston data.
 *
 * @note
 * If the file exists, it is overwritten.
 *
 * @param filename name of the OI-FITS file to create
 * @param insCfg array containing keywords of the primary header.
 * @param array structure containing information of OI_ARRAY binary table
 * @param target structure containing information of OI_TARGET binary table
 * @param photometry structure containing photometry which is stored in
 * AMBER_DATA binary table.
 * @param vis structure containing information of OI_VIS binary table
 * @param vis2 structure containing information of OI_VIS2 binary table
 * @param vis3 structure containing information of OI_T3 binary table
 * @param wave structure containing information of OI_WAVELENGTH binary table
 * @param pst structure containing piston which is stored in AMBER_DATA binary
 * table.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
static amdlibCOMPL_STAT amdlibSaveOiFileESO( const char           *filename,
		/*                                      amdlibINS_CFG        *insCfg,*/
		amdlibSELECTION      *selectedFrames,
		amdlibOI_ARRAY       *array,
		amdlibOI_TARGET      *target,
		amdlibPHOTOMETRY     *photometry,
		amdlibVIS            *vis,
		amdlibVIS2           *vis2,
		amdlibVIS3           *vis3,
		amdlibWAVELENGTH     *wave,
		amdlibPISTON         *pst,
		amdlibBAND            band,
		amdlibSPECTRUM       *spectrum,
		amdlibERROR_MSG      errMsg )
{
	int         status = 0;
	fitsfile    *oiFile;
#ifndef ESO_CPL_PIPELINE_VARIANT
	struct stat statBuf;
	time_t      timeSecs;
	struct tm   *timeNow;
	char        strTime[amdlibKEYW_VAL_LEN+1];
#endif
	char        fitsioMsg[256];
	const char  *routine = "amdlibSaveOiFile";
	char        insName[amdlibKEYW_VAL_LEN+1];
	char        arrName[amdlibKEYW_VAL_LEN+1];
	char        version[32];

	amdlibLogTrace("amdlibSaveOiFile()\n");

#ifndef ESO_CPL_PIPELINE_VARIANT
	/*
	 * amdlib will create the file on its own
	 */
	/* First remove previous IO file (if any) */
	if (stat(filename, &statBuf) == 0)
	{
		if (remove(filename) != 0)
		{
			sprintf(errMsg,
					"%s(): could not overwrite file %s", routine, filename);
			return (amdlibFAILURE);
		}
	}

	/* Create new FITS file */
	if (fits_create_file(&oiFile, filename, &status))
	{
		amdlibOiReturnError(routine, "opening file");
	}
#else
	/*
	 *  The ESO pipeline will create that file and prepare the header
	 *  with DMD compliant keywords. Hence amdlib shall just append to it
	 */
	cpl_msg_info(cpl_func, "Opening File %s for appending...", filename);


	/* Append to FITS file */
	if( fits_open_file(&oiFile, filename, READWRITE, &status) )
	{
		cpl_msg_info(cpl_func,"%s->fits_open_file cannot open in append mode",
				filename);
		amdlibOiReturnError(routine, "opening file");
	}
	else
	{
		cpl_msg_info(cpl_func,"%s->fits_open_file append OK", filename);
	}
#endif


	/* Build a uniq INSNAME using P2VM id (if possible), else use default */
	sprintf(insName, "AMBER");

	/* Retrieve ARRNAME if possible, else use default.*/
	if (array != NULL)
	{
		strncpy(arrName, array->arrayName, amdlibKEYW_VAL_LEN+1);
	}
	else
	{
		sprintf(arrName, "UNKNOWN");
	}

	/* Write OI_ARRAY binary table */
	if (array != NULL)
	{
		if (amdlibWriteOiArray(oiFile, array,  errMsg) != amdlibSUCCESS)
		{
			fits_close_file(oiFile, &status);
			amdlibOiReturnError(routine, "writing OI_ARRAY table");
		}
	}

	/* Write OI_TARGET binary table */
	if (target != NULL)
	{
		if (amdlibWriteOiTarget(oiFile, target, errMsg) != amdlibSUCCESS)
		{
			fits_close_file(oiFile, &status);
			amdlibOiReturnError(routine, "writing OI_TARGET table");
		}
	}

	/* Write OI_WAVELENGTH binary table */
	if (wave != NULL)
	{
		if (amdlibWriteOiWavelength(oiFile, insName,
				wave, errMsg) != amdlibSUCCESS)
		{
			fits_close_file(oiFile, &status);
			amdlibOiReturnError(routine, "writing OI_WAVELENGTH table");
		}
	}

	/* Write OI_VIS binary table */
	if (vis != NULL)
	{
		if (amdlibWriteOiVisSelectedOnly(oiFile, insName, arrName, vis, selectedFrames, band, errMsg) != amdlibSUCCESS)
		{
			fits_close_file(oiFile, &status);
			amdlibOiReturnError(routine, "writing OI_VIS table");
		}
	}

	/* Write OI_VIS2 binary table */
	if (vis2 != NULL)
	{
		if (amdlibWriteOiVis2SelectedOnly( oiFile, insName, arrName, vis2, selectedFrames, band, errMsg) != amdlibSUCCESS)
		{
			fits_close_file(oiFile, &status);
			amdlibOiReturnError(routine, "writing OI_VIS2 table");
		}
	}

	/* Write OI_T3 binary table */
	if (vis3 != NULL)
	{
		if (amdlibWriteOiVis3SelectedOnly( oiFile, insName, arrName, vis3, selectedFrames, band, errMsg) != amdlibSUCCESS)
		{
			fits_close_file(oiFile, &status);
			amdlibOiReturnError(routine, "writing OI_T3");
		}
	}

	/* Write AMBER_DATA binary table */
	if ((photometry != NULL) && (vis != NULL) && (pst != NULL))
	{
		if (amdlibWriteAmberDataSelectedOnly( oiFile, insName, photometry, vis, pst, wave,
				selectedFrames, band, errMsg) != amdlibSUCCESS)

		{
			fits_close_file(oiFile, &status);
			amdlibOiReturnError(routine, "writing AMBER_DATA table");
		}
	}

	/* Write AMBER_SPECTRUM binary table */
	if (spectrum != NULL)
	{
		if (amdlibWriteAmberSpectrum(oiFile, wave, spectrum,
				errMsg) != amdlibSUCCESS)
		{
			fits_close_file(oiFile, &status);
			return amdlibFAILURE;
		}
	}


	if (fits_movabs_hdu(oiFile, 1, 0, &status) != 0)
	{
		fits_close_file(oiFile, &status);
		amdlibOiReturnError(routine, "main header");
	}

#ifndef ESO_CPL_PIPELINE_VARIANT
	/* Complete main header */
	if (insCfg != NULL)
	{
		/* Add DATE */
		timeSecs = time(NULL);
		timeNow = gmtime(&timeSecs);
		strftime(strTime, sizeof(strTime), "%Y-%m-%dT%H:%M:%S", timeNow);
		if (fits_write_key(oiFile, TSTRING, (char *)"DATE", strTime,
				(char *)"Date this file was written", &status) != 0)
		{
			fits_close_file(oiFile, &status);
			amdlibOiReturnError(routine, "completing main header");
		}

		/* Add other keywords */
		for (i = 0; i < insCfg->nbKeywords; i++)
		{
			amdlibKEYW_LINE keywLine;
			if ((strstr(insCfg->keywords[i].name, "SIMPLE") == NULL) &&
					(strstr(insCfg->keywords[i].name, "BITPIX") == NULL) &&
					(strstr(insCfg->keywords[i].name, "NAXIS ") == NULL) &&
					(strstr(insCfg->keywords[i].name, "EXTEND") == NULL) &&
					(strstr(insCfg->keywords[i].name, "DATE  ") == NULL))
			{
				sprintf((char*)keywLine, "%s=%s/%s", insCfg->keywords[i].name,
						insCfg->keywords[i].value,
						insCfg->keywords[i].comment);
				if (fits_write_record(oiFile, keywLine, &status) != 0)
				{
					fits_close_file(oiFile, &status);
					amdlibOiReturnError(routine, "completing main header");
				}
			}
		}

		if (fits_update_key(oiFile, TSTRING, "HIERARCH ESO PRO CATG",
				"SCIENCE_REDUCED", "", &status) != 0)
		{
			amdlibOiReturnError(routine, "HIERARCH ESO PRO CATG");
		}
	}

#endif

	/* Add amdlib version */
	amdlibGetVersion(version);
	if (fits_update_key(oiFile, TSTRING, (char *)"HIERARCH ESO OCS DRS VERSION",
			version,
			(char *)"Data Reduction SW version", &status) != 0)
	{
		amdlibOiReturnError(routine, "HIERARCH ESO OCS DRS VERSION");
	}

	/* Close FITS file */
	if (fits_close_file(oiFile, &status))
	{
		amdlibOiReturnError(routine,"closing file");
	}

	return (amdlibSUCCESS);
}


static cpl_propertylist * SelectorCreateProduct( const char * fctid, char * szRawFile,
		int iIsScience, char * szProductFile, cpl_propertylist * qclist,
		cpl_frameset *  framelist, cpl_parameterlist * parlist,
		const char * recipename)
		{
	int  iStatus = 0;
	char szMessage[1024];
	char szFilenameProduct[1024];

	cpl_table        * pTable;
	cpl_propertylist * pTableHeader;
	int                iTable;
	int                iError;
	cpl_frame        * cur_frame=NULL;
	cpl_frameset_iterator * it = NULL;



	it = cpl_frameset_iterator_new(framelist);

	cur_frame = cpl_frameset_iterator_get(it);
	while(cur_frame)
	{
		/*      Check the right filename */
		if (strcmp((char*)cpl_frame_get_filename(cur_frame), szRawFile)) {

		    cpl_frameset_iterator_advance(it, 1);
		    cur_frame = cpl_frameset_iterator_get(it);
			continue;
		}
		else{
			cpl_msg_info(cpl_func, "Inheriting Header from: %s",
					cpl_frame_get_filename(cur_frame));
			break;
		}
	}
	cpl_frameset_iterator_delete(it);


	/*cpl_frame        *  pFrame;*/
	/*cpl_propertylist *  pHeader;*/

	/* For DFS fill Header function later */;
	/*    pHeader = cpl_propertylist_new();   */
	/*pFrame  = cpl_frame_new();*/

	strcpy( szFilenameProduct, szProductFile );

	pFrame = cpl_frame_new();

	if( pFrame )
	{
		cpl_frame_set_filename( pFrame, szFilenameProduct );
		cpl_frame_set_type( pFrame, CPL_FRAME_TYPE_TABLE );

		if( iIsScience )
			cpl_frame_set_tag( pFrame, "SCIENCE_REDUCED_FILTERED" );
		else
			cpl_frame_set_tag( pFrame, "CALIB_REDUCED_FILTERED" );

		cpl_frame_set_group( pFrame, CPL_FRAME_GROUP_PRODUCT );
		cpl_frame_set_level( pFrame, CPL_FRAME_LEVEL_FINAL );
	}
	else
	{
		cpl_msg_info( fctid, "No memory for product frame." );
		iStatus = 15;
	}

	/*
     sprintf( szMessage, "cpl_frame_set (\"%s\")", cpl_frame_get_filename(pframeSCIENCE[iProductNumber])  );
     cpl_msg_info( fctid, "%s", szMessage );
	 */

	/*
	 * Create the Product file, start with filling the header
	 *
	 * Attention: for the time of this workaround for cpl 3D tables the amdlib must
	 * be patched to NOT create OWN files, but APPEND to existing ones!!
	 *
	 * see comment below
	 *
	 */


	/*
	 * Workaround for cpl_dfs_setup_product_header picking the wrong Header in this CPL release and
	 * also might pick the wrong in the future! It uses the first RAW frame, but this recipe can handle
	 * many raw frames. Hence:
	 *
	 * Read the Header of the RAW file to be written as a product and send it to the function
	 */

	/*
     sprintf( szMessage, "Extracting product header from file %s.",  szRawFile );
     cpl_msg_info( fctid, "%s", szMessage );
	 */

	pHeader = cpl_propertylist_load(  szRawFile, 0 );



	/* Add the necessary DFS fits header information to the product */
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(4, 8, 0)
	if( cpl_dfs_setup_product_header(  pHeader,
			pFrame,
			framelist,
			parlist,
			recipename, /* const char *  recid,  */
			PACKAGE "/" PACKAGE_VERSION, /* const char *  pipeline_id,  */
			PRODUCT_DID,  /* const char *  dictionary_id */
			cur_frame
	)  != CPL_ERROR_NONE )
	{
		/* Error */
		sprintf( szMessage, "Error in setting up the product header." );
		cpl_msg_info( fctid, "%s", szMessage );
		iStatus = 16;
	}
#else
	if( cpl_dfs_setup_product_header(  pHeader,
			pFrame,
			framelist,
			parlist,
			recipename, /* const char *  recid,  */
			PACKAGE "/" PACKAGE_VERSION, /* const char *  pipeline_id,  */
			PRODUCT_DID  /* const char *  dictionary_id */
	)  != CPL_ERROR_NONE )
	{
		/* Error */
		sprintf( szMessage, "Error in setting up the product header." );
		cpl_msg_info( fctid, "%s", szMessage );
		iStatus = 16;
	}
#endif




	/* Erase all old QC parameter */
	cpl_propertylist_erase_regexp(pHeader, "^ESO QC FRAMES SELECTED SNRGT2", 0);
	cpl_propertylist_erase_regexp(pHeader, "^ESO QC FRAMES SELECTED PERCENT", 0);
	/*    cpl_propertylist_erase_regexp(pHeader, "^ESO QC *", 0); */

	/* Append new QC parameter */
	cpl_propertylist_copy_property_regexp(pHeader, qclist, "^ESO QC",0);
	/*   cpl_propertylist_append(pHeader,qclist); */




	/* Write the product including proper DFS header*/

	pTable       = NULL;
	pTableHeader = NULL;
	iTable = 1;
	iError = CPL_ERROR_NONE;

	/*
     sprintf( szMessage, "Creating product file %s...", szFilenameProduct );
     cpl_msg_info( fctid, "%s", szMessage );
	 */

	/*
	 * Workaround:
	 * CPL cannot handle 3D-tables, hence, write just the header to a fits file and let
	 * the amdlibWriteOI just append the data (and not create a whole file as intended by amdlib design
	 * see Module amdlibWriteOI.c
	 */


	cpl_image_save(NULL, szFilenameProduct, CPL_BPP_IEEE_FLOAT, pHeader, CPL_IO_DEFAULT );


	if (CPL_ERROR_NONE != cpl_image_save(NULL, szFilenameProduct, CPL_BPP_16_SIGNED, pHeader, CPL_IO_DEFAULT ))
	{
		cpl_msg_error(cpl_func,"Error in cpl_image_save");
	}


	if (cpl_error_get_code() != CPL_ERROR_NONE) {
		cpl_msg_error(cpl_func, "%s():%d: An error is already set: %s",
				cpl_func, __LINE__, cpl_error_get_where());
		cpl_error_get_message();
	}



	return pHeader;
		}


/*----------------------------------------------------------------------------*/
/**
   @brief    Setup of a new array of selected frames
   @param
   @return   0 if everything is ok


 */
/*----------------------------------------------------------------------------*/
static void SetupSelected( int iInclude, amdlibSELECTION * selectedFrames, amdlibVIS * vis,  amdlibBAND band)
{
	/* Loop through frames and count good ones */
	int iFrame = 0;
	int iBase  = 0;

	for (iFrame = 0; iFrame < vis->nbFrames; iFrame++)
	{
		for( iBase = 0; iBase < vis->nbBases; iBase++)
		{
			/* In case we later want to "include" files, all the others must be "excluded" */
			if( iInclude )
				selectedFrames->band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE;
			else
				selectedFrames->band[band].isSelectedPt[iBase][iFrame] = amdlibTRUE;

		} /* For all bases */
	} /* for all frames */
}

int amber_selector_lib(double x1, double x2, double x3, int ANDselection, int AverageFrames,
		const char * szMethod, const char * inname, const char * outname,
		int iIsScience, cpl_frameset * framelist, cpl_parameterlist * parlist,
		const char * recipename)
{

	/* CPL structures */
	cpl_frameset  * cur_set;

	char   szMessage[1024];
	char * szFilnameOI=NULL;
	char   szProductfile[1024];
	char   szASCII[1024];
	const char * outname_selector_paf=NULL;
	char   szCommand[1024];
	char   szBuffer[1204];
	char * pszFilename;
	char * pszFileTag;
	FILE * fp  = NULL;
	int  iStatus;
	int  base;
	int  iInclude;
	int  iSelFrame;
	int  iFramesLeft;
	int  iFrameIsGood;
	double fThreshold[3];
	amdlibSELECTION selectedFrames;
	/*
   const char  *   tag ;
   int             nframes ;
   int             i ;
	 */
	const char * fctid = "amber_selector";
	int iTotal;
	int iFrame;
	int iFittingFrame;
	double dLowSNR;
	double dHighSNR;
	double dFringeSNR;
	int    bFrameOK;
	amdlibCPT_VIS_OPTIONS visOptions;
	amdlibBAND band;
	int iReal;
	cpl_propertylist * pHeader_tmp;
	cpl_vector * MaxSnrArray[3];
	double MaxSnrArray_at_fThreshold[3];
	cpl_vector * MaxOpdArray[3];
	double MaxOpdArray_at_fThreshold[3];
	double frameSelectionRatio[3];
	amdlibPHOTOMETRY_TABLE_ENTRY **photTablePtr = NULL;
	int iBase=0;
	int index =0;
	int index2 =0;
	int iWlen =0;
	amdlibBOOLEAN select[amdlibNBASELINE] = {amdlibTRUE, amdlibTRUE, amdlibTRUE};
	cpl_propertylist      * qclist;

	cpl_propertylist *  pHeader;

	iStatus      = 0;
	base         = 0;
	iInclude     = 0;
	iSelFrame    = 0;
	iFramesLeft  = 0;
	iFrameIsGood = 0;

	fThreshold[0] = x1;
	fThreshold[1] = x2;
	fThreshold[2] = x3;

	cur_set     = NULL;
	pszFilename = NULL;
	pszFileTag  = NULL;



	strcpy(szProductfile, outname);


	cpl_msg_info( fctid, "Start of DataReduction");

	szFilnameOI = cpl_sprintf("%s",inname);
	if( iIsScience != -1 )
	{
		amdlibClearInsCfg(&insCfg);
		/*if( amdlibLoadOiFile( szFilnameOI, &insCfg, &array, &target, &photometry,
        &vis, &vis2, &vis3, &wave, &pst, errMsg ) == amdlibSUCCESS )


        amdlib NEVER returns amdlibSUCCESS here?!?
		 */

		/*     iStatus = amdlibLoadOiFile( szFilnameOI, &insCfg, &array,
		 * &target, &photometry, &vis, &vis2, &vis3, &wave, &pst, errMsg ); */
		iStatus = amdlibLoadOiFile( szFilnameOI, &insCfg, &array, &target,
				&photometry, &vis, &vis2, &vis3, &wave, &pst, &spectrum,errMsg);


		if(photometry.thisPtr==NULL || pst.thisPtr==NULL || spectrum.thisPtr==NULL)
		{
			if(photometry.thisPtr==NULL) cpl_msg_error(cpl_func,"problems in "
					"reading the photometry structure");
			if(pst.thisPtr       ==NULL) cpl_msg_error(cpl_func,"problems in "
					"reading the piston structure");
			if(spectrum.thisPtr  ==NULL) cpl_msg_error(cpl_func,"problems in "
					"reading the spectrum structure");
			cpl_msg_info( cpl_func, "Amdlib error message: %s", errMsg );

			/* Reset error message */
			memset(errMsg, '\0', sizeof(amdlibERROR_MSG));

			/*Release the memory*/
			if(array.thisPtr        !=NULL)	amdlibReleaseOiArray(&array);
			if(target.thisPtr       !=NULL)	amdlibReleaseOiTarget(&target);
			if(photometry.thisPtr   !=NULL)	amdlibReleasePhotometry(&photometry);
			if(vis.thisPtr          !=NULL)	amdlibReleaseVis(&vis);
			if(vis2.thisPtr         !=NULL)	amdlibReleaseVis2(&vis2);
			if(vis3.thisPtr         !=NULL)	amdlibReleaseVis3(&vis3);
			if(wave.thisPtr         !=NULL)	amdlibReleaseWavelength(&wave);
			if(spectrum.thisPtr     !=NULL)	amdlibReleaseSpectrum(&spectrum);
			if(pst.thisPtr          !=NULL)	amdlibReleasePiston(&pst);
			cpl_free(szFilnameOI);
			return amdlibFAILURE;

		}

		/*sprintf( szMessage, "%d", iStatus );
        cpl_msg_info( fctid, "%s", szMessage );*/

		/* 1 seems to be a warning ?!? */
		if( iStatus == amdlibSUCCESS /* || iStatus == 1*/ )
		{

			/* Reset to OK - no error */
			iStatus = 0;


			sprintf ( szMessage, "Status: %d for input file %s [%s]", iStatus, szFilnameOI, errMsg );
			cpl_msg_info( fctid, "%s", szMessage );

			/* Count number of frames */
			iTotal =  vis.nbFrames * vis.nbBases;
			sprintf( szMessage, "Number of Frames = %d giving %d Visibilities",  iTotal / vis.nbBases, iTotal );
			cpl_msg_info( fctid, "%s", szMessage );

			/* Count frames for Photometry and Piston */

			sprintf( szMessage, "Photometry: %d / Piston: %d", photometry.nbFrames, pst.nbFrames );
			cpl_msg_info( fctid, "%s", szMessage );


			/* Count Frames with Fringe SNR greater than xxx */
			iFrame        = 0;
			iFittingFrame = 0;
			dLowSNR    = 99999999.9;
			dHighSNR   = 0;
			dFringeSNR = 0;
			bFrameOK   = 0;

			/* Setup the default Options for Storing and Frame Selection */

			/* Number of frames used to measure a visibility in output */
			visOptions.binSize=1;
			/* Noise computation type */
			visOptions.errorType=amdlibSTATISTICAL_ERROR;
			/* Piston computation type */
			visOptions.pistonType=amdlibUNWRAPPED_PHASE;
			/* Forces to use the given p2vm even if its id is not OK */
			visOptions.noCheckP2vmId=1;
			/* Frame selection */
			visOptions.frameSelectionType=amdlibNO_FRAME_SEL;
			/* Percentage of frames to be selected */
			visOptions.frameSelectionRatio=fThreshold[0];

			/*          Orig: visOptions = { 1, amdlibSTATISTICAL_ERROR, amdlibUNWRAPPED_PHASE, 1, amdlibNO_FRAME_SEL, fThreshold }; */

			/*       amdlibBAND band = amdlibK_BAND; */
			band = amdlibH_BAND;

			/* Test the band */
			band = amdlibGetBand(wave.wlen[0]);
			/*          printf( "The band oben is : %d \n", band  );       */
			/*
           if (amdlibGetBand(wave.wlen[wave.nbWlen-1]) != band)
           {
           sprintf ( szMessage, "Sorry, frame selection is only possible for one band. This data contains more than one band" );
           cpl_msg_info( fctid, "%s", szMessage );
           iStatus = 666;
           }
			 */


			/* Method 1: Take the first x frames only */
			if( !strcmp( szMethod, selMethod1 ) )
			{
				; /* No corresponding amdlib selection */
			}
			/* Method 2: Fringe_SNR_gt_x */
			else if( !strcmp( szMethod, selMethod2 ) )
			{
				visOptions.frameSelectionType = amdlibFRG_CONTRAST_SEL_THR;
			}
			/* Method 3: Fringe_SNR_percentage_x */
			else if( !strcmp( szMethod, selMethod3 ) )
			{
				visOptions.frameSelectionType = amdlibFRG_CONTRAST_SEL_PCG;
				fThreshold[0] /= 100.0;
				fThreshold[1] /= 100.0;
				fThreshold[2] /= 100.0;
			}
			/* Method 4: Flux_gt_x */
			else  if( !strcmp( szMethod, selMethod4 ) )
			{
				visOptions.frameSelectionType = amdlibFLUX_SEL_THR;
			}
			/* Method 5: Flux_percentage_x */
			else if( !strcmp( szMethod, selMethod5 ) )
			{
				visOptions.frameSelectionType = amdlibFLUX_SEL_PCG;
				fThreshold[0] /= 100.0;
				fThreshold[1] /= 100.0;
				fThreshold[2] /= 100.0;

			}
			/* Method 9: Absolute_piston_value_lt_x */
			else if( !strcmp( szMethod, selMethod9 ) )
			{
				visOptions.frameSelectionType = amdlibOPD_SEL_THR;
			}

			/* Method 10: Absolute_piston_value_percentage_x */
			else if( !strcmp( szMethod, selMethod10 ) )
			{
				visOptions.frameSelectionType = amdlibOPD_SEL_PCG;
				fThreshold[0] /= 100.0;
				fThreshold[1] /= 100.0;
				fThreshold[2] /= 100.0;
			}

			/* Frame selection */
			{
				sprintf( szMessage, "Now selecting ..." );
				cpl_msg_info( fctid, "%s", szMessage );

				amdlibAllocateSelection( &selectedFrames, vis.nbFrames,vis.nbBases,errMsg );

				/* Method 8 */
				if( !strcmp( szMethod, selMethod8 ) )
				{
					/* Include all frames */
					iInclude = 0;
					SetupSelected( iInclude, &selectedFrames, &vis , band);

					sprintf( szMessage, "All frames have been selected for testing purpose." );
					cpl_msg_info( fctid, "%s", szMessage );
				}

				/* Method 2 */
				if( !strcmp( szMethod, selMethod2 ) )
				{
					/* Select first x frames */
					if( fThreshold[0] < 0 || fThreshold[1] < 0 || fThreshold[2] < 0 )
					{
						/* No negative or Zero values for that method */
						iStatus = 5;

						sprintf( szMessage, "This method does not allow thresholds less or equal to zero." );
						cpl_msg_error( fctid, "%s", szMessage );
					}

					if( !iStatus )
					{
						/* Invalidate all frames first for later inclusion */
						iInclude = 1;
						SetupSelected( iInclude, &selectedFrames, &vis ,band);

						/* Include all Frames with Fringe_SNR_gt_x */
						for (iFrame = 0; iFrame < vis.nbFrames; iFrame++)
						{
							iReal=iFrame*vis.nbBases;
							iFrameIsGood = 0;

							if(ANDselection==1){
								if( vis.nbBases == 3 &&
										(vis.table[iReal].frgContrastSnrArray[band] > fThreshold[0] &&
												vis.table[iReal+1].frgContrastSnrArray[band] > fThreshold[1] &&
												vis.table[iReal+2].frgContrastSnrArray[band] > fThreshold[2]))
									iFrameIsGood = 1;
							}

							if(ANDselection==0){
								if( vis.nbBases == 3 &&
										(vis.table[iReal].frgContrastSnrArray[band] > fThreshold[0] ||
												vis.table[iReal+1].frgContrastSnrArray[band] > fThreshold[1] ||
												vis.table[iReal+2].frgContrastSnrArray[band] > fThreshold[2]))
									iFrameIsGood = 1;
							}

							if( vis.nbBases == 1 &&
									(vis.table[iReal].frgContrastSnrArray[band] > fThreshold[0]))
								iFrameIsGood = 1;

							/* Set all three bases to inlcude */

							if( iFrameIsGood){
								for( base = 0; base < vis.nbBases; base++){
									selectedFrames.band[band].isSelectedPt[base][iFrame] = amdlibTRUE;
									/* cpl_msg_info(cpl_func,"iFrame: %d Base: %d  vis.table[iFrame].frgContrastSnrArray[band]: %f vis.table[iFrame+base].vis[band]: %f %f", */
									/* iFrame, base, vis.table[iFrame].frgContrastSnrArray[band], */
									/* vis.table[iFrame].vis->re, vis.table[iFrame].vis->im); */
								}
							}
						} /* for all frames */
					} /* if still OK */
				} /* Method 2 */


				/* Method 3 */
				if( !strcmp( szMethod, selMethod3 ) )
				{
					/* "Fringe_SNR_percentage_x" */
					if( fThreshold[0] < 0. || fThreshold[1] < 0. || fThreshold[2] < 0. || fThreshold[0] > 1. || fThreshold[1] > 1. ||  fThreshold[2] > 1.)
					{
						/* No negative or Zero values for that method */
						iStatus = 5;

						sprintf( szMessage, "This method does not allow thresholds less to zero or larger than 100" );
						cpl_msg_error( fctid, "%s", szMessage );
					}

					if( !iStatus )
					{
						/* Invalidate all frames first for later inclusion */
						iInclude = 1;
						SetupSelected( iInclude, &selectedFrames, &vis ,band);

						/* Include all Frames with Fringe_SNR_gt_x Percent*/


						/* Deriving a vector with the the Fringe contrast.
						 * If three Baselines are available, the maximum of
						 * the fringe contrast of ths three baselines is
						 * assigned to the vector. */


						MaxSnrArray[0]=cpl_vector_new(vis.nbFrames);
						cpl_vector_fill (MaxSnrArray[0], 0.);

						MaxSnrArray[1]=cpl_vector_new(vis.nbFrames);
						cpl_vector_fill (MaxSnrArray[1], 0.);

						MaxSnrArray[2]=cpl_vector_new(vis.nbFrames);
						cpl_vector_fill (MaxSnrArray[2], 0.);



						/* Derive a vector including the maximum frgContrastSnr if there are 3 baselines ... */
						for (iFrame = 0; iFrame < vis.nbFrames; iFrame++)
						{
							iReal=iFrame*vis.nbBases;

							if(vis.nbBases == 3){
								cpl_vector_set(MaxSnrArray[0], iFrame, vis.table[iReal].frgContrastSnrArray[band]);
								cpl_vector_set(MaxSnrArray[1], iFrame, vis.table[iReal+1].frgContrastSnrArray[band]);
								cpl_vector_set(MaxSnrArray[2], iFrame, vis.table[iReal+2].frgContrastSnrArray[band]);

							}
							else  if (vis.nbBases == 1){
								cpl_vector_set(MaxSnrArray[0], iFrame, vis.table[iReal].frgContrastSnrArray[band]);
							}

						}


						/* sort by decreasing data */
						cpl_vector_sort(MaxSnrArray[0],-1);
						cpl_vector_sort(MaxSnrArray[1],-1);
						cpl_vector_sort(MaxSnrArray[2],-1);

						/* Get the fringe-contrast value at the fThreshold level */
						/* fThreshold /= 100.0; */

						MaxSnrArray_at_fThreshold[0]=cpl_vector_get(MaxSnrArray[0], (int)floor((vis.nbFrames-1)*fThreshold[0]));
						MaxSnrArray_at_fThreshold[1]=cpl_vector_get(MaxSnrArray[1], (int)floor((vis.nbFrames-1)*fThreshold[1]));
						MaxSnrArray_at_fThreshold[2]=cpl_vector_get(MaxSnrArray[2], (int)floor((vis.nbFrames-1)*fThreshold[2]));


						if (fThreshold[0]>=1) MaxSnrArray_at_fThreshold[0]=-FLT_MAX;
						if (fThreshold[1]>=1) MaxSnrArray_at_fThreshold[1]=-FLT_MAX;
						if (fThreshold[2]>=1) MaxSnrArray_at_fThreshold[2]=-FLT_MAX;

						if (fThreshold[0]<=0) MaxSnrArray_at_fThreshold[0]=FLT_MAX;
						if (fThreshold[1]<=0) MaxSnrArray_at_fThreshold[1]=FLT_MAX;
						if (fThreshold[2]<=0) MaxSnrArray_at_fThreshold[2]=FLT_MAX;

						cpl_msg_info(cpl_func,"SNR threshold for baseline 1: %g",MaxSnrArray_at_fThreshold[0]);
						cpl_msg_info(cpl_func,"SNR threshold for baseline 2: %g",MaxSnrArray_at_fThreshold[1]);
						cpl_msg_info(cpl_func,"SNR threshold for baseline 3: %g",MaxSnrArray_at_fThreshold[2]);



						/*                   cpl_vector_dump(MaxSnrArray, stdout); */

						for (iFrame = 0; iFrame < vis.nbFrames; iFrame++)
						{
							iReal=iFrame*vis.nbBases;
							iFrameIsGood = 0;

							if(ANDselection==1){
								if( vis.nbBases == 3 &&
										(vis.table[iReal].frgContrastSnrArray[band] >= MaxSnrArray_at_fThreshold[0] &&
												vis.table[iReal+1].frgContrastSnrArray[band] >= MaxSnrArray_at_fThreshold[1] &&
												vis.table[iReal+2].frgContrastSnrArray[band] >= MaxSnrArray_at_fThreshold[2]))
									iFrameIsGood = 1;
							}

							if(ANDselection==0){
								if( vis.nbBases == 3 &&
										(vis.table[iReal].frgContrastSnrArray[band] >= MaxSnrArray_at_fThreshold[0] ||
												vis.table[iReal+1].frgContrastSnrArray[band] >= MaxSnrArray_at_fThreshold[1] ||
												vis.table[iReal+2].frgContrastSnrArray[band] >= MaxSnrArray_at_fThreshold[2]))
									iFrameIsGood = 1;
							}


							if( vis.nbBases == 1 &&
									(vis.table[iReal].frgContrastSnrArray[band] >= MaxSnrArray_at_fThreshold[0]))
								iFrameIsGood = 1;

							/* Set all three bases to inlcude */

							if( iFrameIsGood){
								for( base = 0; base < vis.nbBases; base++){
									selectedFrames.band[band].isSelectedPt[base][iFrame] = amdlibTRUE;
									/*                            cpl_msg_info(cpl_func,"iFrame: %d Base: %d  vis.table[iFrame].frgContrastSnrArray[band]: %f vis.table[iFrame+base].vis[band]: %f %f", */
									/*                                         iFrame, base, vis.table[iFrame].frgContrastSnrArray[band],  */
									/*                                         vis.table[iFrame].vis->re, vis.table[iFrame].vis->im); */
								}
							}
						} /* for all frames */
						cpl_vector_delete(MaxSnrArray[0]);
						cpl_vector_delete(MaxSnrArray[1]);
						cpl_vector_delete(MaxSnrArray[2]);

					} /* if still OK */
				} /* Method 3 */




				/* Method 4 */
				if( !strcmp( szMethod, selMethod4 ) || !strcmp( szMethod, selMethod5 ))
				{


					frameSelectionRatio[0]=fThreshold[0];
					frameSelectionRatio[1]=fThreshold[1];
					frameSelectionRatio[2]=fThreshold[2];

					/* Wrap phot table */
					photTablePtr =
							(amdlibPHOTOMETRY_TABLE_ENTRY **)amdlibWrap2DArray(photometry.table,
									photometry.nbBases, photometry.nbFrames,
									sizeof(amdlibPHOTOMETRY_TABLE_ENTRY),
									errMsg);
					if (photTablePtr == NULL)
					{
						return amdlibFAILURE;
					}
					/* Allocate memory for temporary arrays */
					for (iBase = 0; iBase < photometry.nbBases; iBase++)
					{
						tmpArray[iBase] = cpl_calloc(selectedFrames.band[band].nbSelectedFrames[iBase],
								sizeof(double));
						if (tmpArray[iBase] == NULL)
						{
							amdlibSetErrMsg("Could not allocate memory for temporary buffer");
							return amdlibFAILURE;
						}
					}
					/* Loop on frames, insert flux criterion in the temporary array
					 * for each baseline. Flux criterion is here
					 * sqrt(Pi*Pj)/signedsqrt(fluxPi+fluxPj+npix*RON^2)
					 * (summed over spectrum = all lambdas)
					 */
					for (iBase = 0; iBase < photometry.nbBases; iBase++)
					{
						index = 0;
						for (iFrame = 0; iFrame < photometry.nbFrames; iFrame++)
						{
							if (selectedFrames.band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE)
							{
								/* FIXME: handle of not a number:
                           they should not appear here ! */
								index2=0;

								for (iWlen = 0; iWlen < photometry.nbWlen; iWlen++)
								{
									if(!isnan(photTablePtr[iFrame][iBase].sigma2FluxSumPiPj[iWlen]))
									{
										tmpArray[iBase][index] +=
												amdlibSignedSqrt(photTablePtr[iFrame][iBase].PiMultPj[iWlen])
												/ amdlibSignedSqrt(photTablePtr[iFrame][iBase].sigma2FluxSumPiPj[iWlen]);
										index2++;
									}
								}
								/* bugfix: sqrt(nbWlen) instead of nbWlen (changed by
                           index for nans handling) */
								tmpArray[iBase][index] /= sqrt(index2);
								/* cpl_msg_info(cpl_func,"iBase %5d index %5d tmpArray[iBase][index] %f",iBase, index,  tmpArray[iBase][index]); */
								index++;
							}
						}
					}


					if (visOptions.frameSelectionType == amdlibFLUX_SEL_THR)
					{
						for (iBase = 0; iBase < photometry.nbBases; iBase++)
						{
							threshold[iBase] = frameSelectionRatio[iBase];
						}
					}
					else if (visOptions.frameSelectionType == amdlibFLUX_SEL_PCG)
					{
						/* Sort these temporary arrays and get associated thresholds */
						for (iBase = 0; iBase < photometry.nbBases; iBase++)
						{
							if (frameSelectionRatio[iBase]<=0){
								threshold[iBase]=FLT_MAX;
							}
							if (frameSelectionRatio[iBase]>0 && amdlibGetThresholdESO(tmpArray[iBase],
									selectedFrames.band[band].nbSelectedFrames[iBase],
									frameSelectionRatio[iBase],
									&threshold[iBase],
									errMsg) != amdlibSUCCESS)
							{
								amdlibLogWarning("Could not get threshold associated to "
										"the frameSelectionRatio");
								amdlibLogWarningDetail(errMsg);
								amdlibLogWarning("Could not perform frame selection on "
										"baseline %d in band %c",
										iBase+1, amdlibBandNumToStr(band));
								select[iBase] = amdlibFALSE;
							}

							cpl_msg_info(cpl_func,"Flux threshold for baseline %d: %g",iBase,threshold[iBase]);
						}

					}

					/* cpl_msg_info(cpl_func,"Threshold0:  %f  Threshold1:  %f  Threshold2:  %f",threshold[0], threshold[1],  threshold[2]); */
					/* Invalidate all frames first for later inclusion */
					iInclude = 1;
					SetupSelected( iInclude, &selectedFrames, &vis ,band);



					if( !strcmp( szMethod, selMethod4 ))
					{


						/* Include all Frames with Flux_gt_x */
						for (iFrame = 0; iFrame < vis.nbFrames; iFrame++)
						{
							iReal=iFrame*vis.nbBases;
							iFrameIsGood = 0;

							if(ANDselection==1){
								if( vis.nbBases == 3 &&
										(tmpArray[0][iFrame] > threshold[0] &&
												tmpArray[1][iFrame] > threshold[1] &&
												tmpArray[2][iFrame] > threshold[2]))
									iFrameIsGood = 1;
							}
							if(ANDselection==0){
								if( vis.nbBases == 3 &&
										(tmpArray[0][iFrame] > threshold[0] ||
												tmpArray[1][iFrame] > threshold[1] ||
												tmpArray[2][iFrame] > threshold[2]))
									iFrameIsGood = 1;
							}

							if( vis.nbBases == 1 &&
									(tmpArray[0][iFrame] > threshold[0]))
								iFrameIsGood = 1;

							/* Set all three bases to inlcude */

							if( iFrameIsGood){
								for( base = 0; base < vis.nbBases; base++){
									selectedFrames.band[band].isSelectedPt[base][iFrame] = amdlibTRUE;
									/* cpl_msg_info(cpl_func,"iFrame: %d Base: %d  vis.table[iFrame].frgContrastSnrArray[band]: %f vis.table[iFrame+base].vis[band]: %f %f", */
									/* iFrame, base, vis.table[iFrame].frgContrastSnrArray[band], */
									/* vis.table[iFrame].vis->re, vis.table[iFrame].vis->im); */
								}
							}
						} /* for all frames */
					}

					if( !strcmp( szMethod, selMethod5 ))
					{

						/* Include all Frames with Flux_percentage_x */
						for (iFrame = 0; iFrame < vis.nbFrames; iFrame++)
						{
							iReal=iFrame*vis.nbBases;
							iFrameIsGood = 0;

							if(ANDselection==1){
								if( vis.nbBases == 3 &&
										(tmpArray[0][iFrame] >= threshold[0] &&
												tmpArray[1][iFrame] >= threshold[1] &&
												tmpArray[2][iFrame] >= threshold[2]))
									iFrameIsGood = 1;
							}
							if(ANDselection==0){
								if( vis.nbBases == 3 &&
										(tmpArray[0][iFrame] >= threshold[0] ||
												tmpArray[1][iFrame] >= threshold[1] ||
												tmpArray[2][iFrame] >= threshold[2]))
									iFrameIsGood = 1;
							}

							if( vis.nbBases == 1 &&
									(tmpArray[0][iFrame] >= threshold[0]))
								iFrameIsGood = 1;

							/* Set all three bases to inlcude */

							if( iFrameIsGood){
								for( base = 0; base < vis.nbBases; base++){
									selectedFrames.band[band].isSelectedPt[base][iFrame] = amdlibTRUE;
									/* cpl_msg_info(cpl_func,"iFrame: %d Base: %d  vis.table[iFrame].frgContrastSnrArray[band]: %f vis.table[iFrame+base].vis[band]: %f %f", */
									/* iFrame, base, vis.table[iFrame].frgContrastSnrArray[band], */
									/* vis.table[iFrame].vis->re, vis.table[iFrame].vis->im); */
								}
							}
						} /* for all frames */

					}




					/*                /\* Loop on frames and stamp the good ones ( criterion upper than  */
					/*                 * threshold) *\/ */


					/*                for (iBase = 0; iBase < photometry.nbBases; iBase++) */
					/*                { */
					/*                   if (select[iBase] == amdlibTRUE) */
					/*                   { */
					/*                      index = 0; */
					/*                      for (iFrame = 0; iFrame < photometry.nbFrames; iFrame++) */
					/*                      { */
					/*                         if (selectedFrames.band[band].isSelectedPt[iBase][iFrame] == amdlibTRUE) */
					/*                         { */
					/*                            if (tmpArray[iBase][index] < threshold[iBase]) */
					/*                            { */
					/*                               selectedFrames.band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE; */
					/*                               selectedFrames.band[band].nbSelectedFrames[iBase]--; */
					/*                            } */
					/*                            index++; */
					/*                         } */
					/*                      } */
					/*                   } */
					/*                   /\* In the case no selection was possible, set all frames to false *\/ */
					/*                   else */
					/*                   { */
					/*                      for (iFrame = 0; iFrame < photometry.nbFrames; iFrame++) */
					/*                      { */
					/*                         selectedFrames.band[band].isSelectedPt[iBase][iFrame] = amdlibFALSE; */
					/*                      } */
					/*                      selectedFrames.band[band].nbSelectedFrames[iBase] = 0; */
					/*                   } */
					/*                } */



					for (iBase = 0; iBase < photometry.nbBases; iBase++)
					{
						cpl_free(tmpArray[iBase]);
					}
					amdlibFree2DArrayWrapping((void **)photTablePtr);
				}








				/* Method 8 */
				if( !strcmp( szMethod, selMethod8 ) )
				{
					/* Include all frames */
					iInclude = 0;
					SetupSelected( iInclude, &selectedFrames, &vis ,band);

					sprintf( szMessage, "All frames have been selected for testing purpose." );
					cpl_msg_info( fctid, "%s", szMessage );
				}

				/* Method 1 */
				if( !strcmp( szMethod, selMethod1 ) )
				{
					/* Select first x frames */
					if( fThreshold[0] <= 0 )
					{
						/* No negative or Zero values for that method */
						iStatus = 5;

						sprintf( szMessage, "This method does not allow thresholds less or equal to zero." );
						cpl_msg_error( fctid, "%s", szMessage );
					}

					if( fThreshold[0] >  vis.nbFrames )
					{
						/* Threshold too high */
						iStatus = 6;

						sprintf( szMessage, "This method does not allow thresholds greater than the total number of frames." );
						cpl_msg_error( fctid, "%s", szMessage );
					}

					if( !iStatus )
					{
						/* Invalidate all frames first for later inclusion */
						iInclude = 1;
						SetupSelected( iInclude, &selectedFrames, &vis ,band);

						/* Include first x Frames */
						for (iFrame = 0; iFrame < (int)fThreshold[0] ; iFrame++)
						{
							for( base = 0; base < vis.nbBases; base++)
								selectedFrames.band[band].isSelectedPt[base][iFrame] = amdlibTRUE;

						} /* for all frames */
					} /* if still OK */

				} /* Method 1 */

				/* Method 6 and 7 */
				if( !strcmp( szMethod, selMethod6 ) || !strcmp( szMethod, selMethod7 ) )
				{
					/* ASCII file selection */
					if( !strcmp( szMethod, selMethod7 ) )
					{
						iInclude = 1;
						strcpy( szASCII, "/tmp/amber_include.txt" );
					}
					else
					{
						iInclude = 0;
						strcpy( szASCII, "/tmp/amber_exclude.txt" );
					}

					SetupSelected( iInclude, &selectedFrames, &vis, band );

					sprintf( szMessage, "Default selection for ASCII file done..." );
					cpl_msg_info( fctid, "%s", szMessage );

					/* Open the ASCII file */
					fp = fopen( szASCII, "rt" );

					if( fp )
					{
						/* Walk through the file and exclude/include the given frames by their index number */
						while( !feof( fp ) )
						{
							fgets( szBuffer, sizeof(szBuffer)-2, fp );

							if( !feof( fp ) )
							{
								iSelFrame = atoi( szBuffer );

								if( iSelFrame <  vis.nbFrames && iSelFrame >=1 )
								{
									/*
                             sprintf( szMessage, "Found Frame %d in File %s.", iSelFrame, szASCII );
                             cpl_msg_info( fctid, "%s", szMessage );
									 */

									for( base = 0; base < vis.nbBases; base++ )
									{
										if( iInclude )
											selectedFrames.band[band].isSelectedPt[base][iSelFrame-1] = amdlibTRUE;
										else
											selectedFrames.band[band].isSelectedPt[base][iSelFrame-1] = amdlibFALSE;
									}
								}
								else
								{
									sprintf( szMessage, "Warning: Frame %d is out of Range and has been ignored.", iSelFrame );
									cpl_msg_info( fctid, "%s", szMessage );
								}
							} /* end of file ? */

						} /* While more lines in file */
						fclose( fp );
					}
					else
					{
						sprintf( szMessage, "Error: The File %s has not been found.", szASCII );
						cpl_msg_info( fctid, "%s", szMessage );
						iStatus = 1;
					}

					/* Set Method of this manual selection to nothing particular*/
					visOptions.frameSelectionType = amdlibNO_FRAME_SEL;
				} /* Method 6 and 7 */






				/* Method 9 */
				if( !strcmp( szMethod, selMethod9 ) )
				{
					if( fThreshold[0] < 0 || fThreshold[1] < 0 || fThreshold[2] < 0 )
					{
						/* No negative or Zero values for that method */
						iStatus = 5;

						sprintf( szMessage, "This method does not allow thresholds less to zero." );
						cpl_msg_error( fctid, "%s", szMessage );
					}

					if( !iStatus )
					{
						/* Invalidate all frames first for later inclusion */
						iInclude = 1;
						SetupSelected( iInclude, &selectedFrames, &vis ,band);

						/* Include all frames with an absloute pisten value < x */
						for (iFrame = 0; iFrame < vis.nbFrames; iFrame++)
						{
							iReal=iFrame*vis.nbBases;
							iFrameIsGood = 0;


							if(ANDselection==1){
								if( vis.nbBases == 3 &&
										(fabs(pst.pistonOPDArray[band][iReal] * amdlibNM_TO_M) < fThreshold[0] &&
												fabs(pst.pistonOPDArray[band][iReal+1] * amdlibNM_TO_M) < fThreshold[1] &&
												fabs(pst.pistonOPDArray[band][iReal+2] * amdlibNM_TO_M) < fThreshold[2]))
									iFrameIsGood = 1;
							}

							if(ANDselection==0){
								if( vis.nbBases == 3 &&
										(fabs(pst.pistonOPDArray[band][iReal] * amdlibNM_TO_M) < fThreshold[0] ||
												fabs(pst.pistonOPDArray[band][iReal+1] * amdlibNM_TO_M) < fThreshold[1] ||
												fabs(pst.pistonOPDArray[band][iReal+2] * amdlibNM_TO_M) < fThreshold[2]))
									iFrameIsGood = 1;
							}


							if( vis.nbBases == 1 &&
									(fabs(pst.pistonOPDArray[band][iReal] * amdlibNM_TO_M) < fThreshold[0]))
								iFrameIsGood = 1;

							/* Set all three bases to inlcude */

							if( iFrameIsGood){
								for( base = 0; base < vis.nbBases; base++){
									selectedFrames.band[band].isSelectedPt[base][iFrame] = amdlibTRUE;
								}
							}
						} /* for all frames */
					} /* if still OK */
				} /* Method 9 */








				/* Method 10 */
				if( !strcmp( szMethod, selMethod10 ) )
				{
					/* "Fringe_SNR_percentage_x" */
					if( fThreshold[0] < 0. || fThreshold[1] < 0. || fThreshold[2] < 0. || fThreshold[0] > 1. || fThreshold[1] > 1. ||  fThreshold[2] > 1.)
					{
						/* This method does not allow thresholds less to zero or larger than 100 */
						iStatus = 5;

						sprintf( szMessage, "This method does not allow thresholds less than zero or larger than 100" );
						cpl_msg_error( fctid, "%s", szMessage );
					}

					if( !iStatus )
					{
						/* Invalidate all frames first for later inclusion */
						iInclude = 1;
						SetupSelected( iInclude, &selectedFrames, &vis ,band);

						/* Include all Frames with the absolute pisten value >= x Percent*/
						/* Deriving a vector with the the OPD. */


						MaxOpdArray[0]=cpl_vector_new(vis.nbFrames);
						cpl_vector_fill (MaxOpdArray[0], FLT_MAX);

						MaxOpdArray[1]=cpl_vector_new(vis.nbFrames);
						cpl_vector_fill (MaxOpdArray[1], FLT_MAX);

						MaxOpdArray[2]=cpl_vector_new(vis.nbFrames);
						cpl_vector_fill (MaxOpdArray[2], FLT_MAX);



						/* Derive a vector including the maximum pistonOPD if there are 3 baselines ... */
						for (iFrame = 0; iFrame < vis.nbFrames; iFrame++)
						{
							iReal=iFrame*vis.nbBases;

							if(vis.nbBases == 3){
								cpl_vector_set(MaxOpdArray[0], iFrame, fabs(pst.pistonOPDArray[band][iReal] * amdlibNM_TO_M));
								cpl_vector_set(MaxOpdArray[1], iFrame, fabs(pst.pistonOPDArray[band][iReal+1] * amdlibNM_TO_M));
								cpl_vector_set(MaxOpdArray[2], iFrame, fabs(pst.pistonOPDArray[band][iReal+2] * amdlibNM_TO_M));

							}
							else  if (vis.nbBases == 1){
								cpl_vector_set(MaxOpdArray[0], iFrame, fabs(pst.pistonOPDArray[band][iReal] * amdlibNM_TO_M));
							}

						}


						/* sort by increasing data */
						cpl_vector_sort(MaxOpdArray[0],+1);
						cpl_vector_sort(MaxOpdArray[1],+1);
						cpl_vector_sort(MaxOpdArray[2],+1);

						/* Get the fringe-contrast value at the fThreshold level */
						/* fThreshold /= 100.0; */

						MaxOpdArray_at_fThreshold[0]=cpl_vector_get(MaxOpdArray[0], (int)floor((vis.nbFrames-1)*fThreshold[0]));
						MaxOpdArray_at_fThreshold[1]=cpl_vector_get(MaxOpdArray[1], (int)floor((vis.nbFrames-1)*fThreshold[1]));
						MaxOpdArray_at_fThreshold[2]=cpl_vector_get(MaxOpdArray[2], (int)floor((vis.nbFrames-1)*fThreshold[2]));


						if (fThreshold[0]>=1) MaxOpdArray_at_fThreshold[0]=FLT_MAX;
						if (fThreshold[1]>=1) MaxOpdArray_at_fThreshold[1]=FLT_MAX;
						if (fThreshold[2]>=1) MaxOpdArray_at_fThreshold[2]=FLT_MAX;

						if (fThreshold[0]<=0) MaxOpdArray_at_fThreshold[0]=FLT_MIN;
						if (fThreshold[1]<=0) MaxOpdArray_at_fThreshold[1]=FLT_MIN;
						if (fThreshold[2]<=0) MaxOpdArray_at_fThreshold[2]=FLT_MIN;

						cpl_msg_info(cpl_func,"Piston threshold for baseline 1: %g ",MaxOpdArray_at_fThreshold[0]);
						cpl_msg_info(cpl_func,"Piston threshold for baseline 2: %g ",MaxOpdArray_at_fThreshold[1]);
						cpl_msg_info(cpl_func,"Piston threshold for baseline 3: %g ",MaxOpdArray_at_fThreshold[2]);



						/*                   cpl_vector_dump(MaxOpdArray, stdout); */

						for (iFrame = 0; iFrame < vis.nbFrames; iFrame++)
						{
							iReal=iFrame*vis.nbBases;
							iFrameIsGood = 0;

							if(ANDselection==1){
								if( vis.nbBases == 3 &&
										(fabs(pst.pistonOPDArray[band][iReal]   * amdlibNM_TO_M) <= MaxOpdArray_at_fThreshold[0] &&
												fabs(pst.pistonOPDArray[band][iReal+1] * amdlibNM_TO_M) <= MaxOpdArray_at_fThreshold[1] &&
												fabs(pst.pistonOPDArray[band][iReal+2] * amdlibNM_TO_M) <= MaxOpdArray_at_fThreshold[2]))
									iFrameIsGood = 1;
							}

							if(ANDselection==0){
								if( vis.nbBases == 3 &&
										(fabs(pst.pistonOPDArray[band][iReal]   * amdlibNM_TO_M) <= MaxOpdArray_at_fThreshold[0] ||
												fabs(pst.pistonOPDArray[band][iReal+1] * amdlibNM_TO_M) <= MaxOpdArray_at_fThreshold[1] ||
												fabs(pst.pistonOPDArray[band][iReal+2] * amdlibNM_TO_M) <= MaxOpdArray_at_fThreshold[2]))
									iFrameIsGood = 1;
							}

							if( vis.nbBases == 1 &&
									(fabs(pst.pistonOPDArray[band][iReal]   * amdlibNM_TO_M) <= MaxOpdArray_at_fThreshold[0]))
								iFrameIsGood = 1;

							/* Set all three bases to inlcude */

							if( iFrameIsGood){
								for( base = 0; base < vis.nbBases; base++){
									selectedFrames.band[band].isSelectedPt[base][iFrame] = amdlibTRUE;
									/*                            cpl_msg_info(cpl_func,"iFrame: %d Base: %d  vis.table[iFrame].pistonOPDArray[band]: %f vis.table[iFrame+base].vis[band]: %f %f", */
									/*                                         iFrame, base, vis.table[iFrame].pistonOPDArray[band],  */
									/*                                         vis.table[iFrame].vis->re, vis.table[iFrame].vis->im); */
								}
							}
						} /* for all frames */
						cpl_vector_delete(MaxOpdArray[0]);
						cpl_vector_delete(MaxOpdArray[1]);
						cpl_vector_delete(MaxOpdArray[2]);

					} /* if still OK */
				} /* Method 10 */














				/*----------------------------------------------------------------------------------------*/
				/* Loop through frames and count good ones                                                */
				sprintf( szMessage, "Counting the frames that passed the filtering..." );
				cpl_msg_info( fctid, "%s", szMessage );
				iFittingFrame = 0;
				for (iFrame = 0; iFrame < vis.nbFrames && !iStatus; iFrame++)
				{
					for (base = 0; base < vis.nbBases; base++)
					{
						if( selectedFrames.band[band].isSelectedPt[base][iFrame] == amdlibTRUE )
							iFittingFrame++;
					}
				}
				/*             cpl_msg_info(cpl_func,"iFittingFrame: %d\n",iFittingFrame); */

				/* Correct the number of Frames for amdlib Saving */
				iFramesLeft         =  iFittingFrame / vis.nbBases;
				/*             vis.nbFrames        = iFramesLeft; */
				/*             vis2.nbFrames       = iFramesLeft; */
				/*             vis3.nbFrames       = iFramesLeft; */
				/*             photometry.nbFrames = iFramesLeft; */
				/*             pst.nbFrames        = iFramesLeft; */

				/*            cpl_msg_info(cpl_func,"iFramesLeft: %d\n",iFramesLeft); */

				/* Average visibilities, photometries and pistons on good frames */
				if( !iStatus && iFittingFrame && AverageFrames )
				{
					sprintf( szMessage, "Averaging the remaining %d frames...", iFittingFrame );
					cpl_msg_info( fctid, "%s", szMessage );

					if (amdlibAverageVisibilities( &photometry,
							&vis, &vis2, &vis3,
							&pst, band, &wave,
							&selectedFrames, errMsg) != amdlibSUCCESS)
					{
						/* Averaging failed! */
						iStatus = 555;
					} else {
					    /* Include the averaged frame again */
					    iInclude = 0;
					    SetupSelected( iInclude, &selectedFrames, &vis ,band);
					}


				}

			} /* Frame Selection  */


			strcpy( szMessage, "----------------------------------------------------------------------------" );
			cpl_msg_info( fctid, "%s", szMessage );
			sprintf( szMessage, "%d Frames = %d Visibilities passed the Filter = %.02f Percent",  iFramesLeft, iFittingFrame, 100.0 * (float)iFittingFrame / (float)iTotal );
			cpl_msg_info( fctid, "%s", szMessage );
			strcpy( szMessage, "----------------------------------------------------------------------------" );
			cpl_msg_info( fctid, "%s", szMessage );


			if( iStatus == 0 )
			{

				/* Frames remaining? */
				if( iFittingFrame == 0 )
				{
					sprintf( szMessage, "No Frames passed! Please check the filtering Criteria!!" );
					cpl_msg_warning( fctid, "%s", szMessage );
					iStatus = 4;
				}

#ifdef OLLER_SCHROTT
				/* Info about min/max, but not for Method1 */
				if( strcmp( szMethod, selMethod1 ) )
				{
					sprintf( szMessage, "For all frames: lowest Fringe SNR is %0.4f and highest is %0.4f", dLowSNR, dHighSNR );
					cpl_msg_info( fctid, "%s", szMessage );
				}
#endif

				/* Write the QC Parameter */
				qclist = cpl_propertylist_new();


				if( !strcmp( szMethod, selMethod3 ))
				{
					cpl_propertylist_update_double(qclist, "ESO QC X1 THRESHOLD", MaxSnrArray_at_fThreshold[0]);
					cpl_propertylist_update_double(qclist, "ESO QC X2 THRESHOLD", MaxSnrArray_at_fThreshold[1]);
					cpl_propertylist_update_double(qclist, "ESO QC X3 THRESHOLD", MaxSnrArray_at_fThreshold[2]);
				}
				else if(!strcmp( szMethod, selMethod4 )|| !strcmp( szMethod, selMethod5 ))
				{
					cpl_propertylist_update_double(qclist, "ESO QC X1 THRESHOLD", threshold[0]);
					cpl_propertylist_update_double(qclist, "ESO QC X2 THRESHOLD", threshold[1]);
					cpl_propertylist_update_double(qclist, "ESO QC X3 THRESHOLD", threshold[2]);
				}

				else if( !strcmp( szMethod, selMethod10 ))
				{
					cpl_propertylist_update_double(qclist, "ESO QC X1 THRESHOLD", MaxOpdArray_at_fThreshold[0]);
					cpl_propertylist_update_double(qclist, "ESO QC X2 THRESHOLD", MaxOpdArray_at_fThreshold[1]);
					cpl_propertylist_update_double(qclist, "ESO QC X3 THRESHOLD", MaxOpdArray_at_fThreshold[2]);
				}
				else
				{
					cpl_propertylist_update_double(qclist, "ESO QC X1 THRESHOLD", fThreshold[0]);
					cpl_propertylist_update_double(qclist, "ESO QC X2 THRESHOLD", fThreshold[1]);
					cpl_propertylist_update_double(qclist, "ESO QC X3 THRESHOLD", fThreshold[2]);

				}

				cpl_propertylist_update_int (qclist, "ESO QC FRAMES SELECTED"           , (int)iFramesLeft);
				cpl_propertylist_update_double(qclist, "ESO QC FRAMES PERCENTAGE SELECTED", 100.0*(float)iFittingFrame/(float)iTotal);
				cpl_propertylist_set_comment (qclist, "ESO QC FRAMES SELECTED"           , "Number of selected frames");
				cpl_propertylist_set_comment (qclist, "ESO QC FRAMES PERCENTAGE SELECTED", "Percentage of selected frames");

				cpl_propertylist_update_string (qclist,
						"ESO QC USEDVALUE METHOD", szMethod);
				cpl_propertylist_update_bool   (qclist,
						"ESO QC USEDVALUE AND", ANDselection);
				cpl_propertylist_update_double (qclist,
						"ESO QC USEDVALUE X1", x1);
				cpl_propertylist_update_double (qclist,
						"ESO QC USEDVALUE X2", x2);
				cpl_propertylist_update_double (qclist,
						"ESO QC USEDVALUE X3", x3);




				/*---------------------------------------------------------------------------------------------------------------*/
				/* Creates Product File and sets up DFO compliant Header */
				/*qc_properties =*/
				pHeader_tmp=SelectorCreateProduct( fctid, (char *)szFilnameOI,
						iIsScience, (char*)szProductfile, qclist, framelist,
						parlist, recipename);
				cpl_propertylist_delete(pHeader_tmp);

				cpl_propertylist_delete(qclist);

				/*---------------------------------------------------------------------------------------------------------------*/
				/* Writes instrument description header and scientific data */

				/*
              sprintf ( szMessage, "Now writing filtered Scientific Data to %s...", szProductfile );
              cpl_msg_info( fctid, "%s", szMessage );
				 */


				if( amdlibSaveOiFileESO( szProductfile, &selectedFrames,
						&array, &target,
						&photometry, &vis, &vis2,
						&vis3, &wave,
						&pst, band, &spectrum, errMsg
				) != amdlibSUCCESS)
				{
					iStatus = 3;
					sprintf ( szMessage, "ERROR saving Scientific Data to %s. [%s]", szProductfile, errMsg );
					cpl_msg_info( fctid, "%s", szMessage );

				}

				amdlibReleaseSelection(&selectedFrames);
				if(array.thisPtr     !=NULL) amdlibReleaseOiArray(&array);
				if(target.thisPtr    !=NULL) amdlibReleaseOiTarget(&target);
				if(photometry.thisPtr!=NULL) amdlibReleasePhotometry(&photometry);
				if(vis.thisPtr       !=NULL) amdlibReleaseVis(&vis);
				if(vis2.thisPtr      !=NULL) amdlibReleaseVis2(&vis2);
				if(vis3.thisPtr      !=NULL) amdlibReleaseVis3(&vis3);
				if(wave.thisPtr      !=NULL) amdlibReleaseWavelength(&wave);
				if(spectrum.thisPtr  !=NULL) amdlibReleaseSpectrum(&spectrum);
				if(pst.thisPtr       !=NULL) amdlibReleasePiston(&pst);



				/* ---------------------------------------------------------- */
				if(iStatus==0){

					amdlibClearInsCfg(&insCfg);
					if (amdlibLoadOiFile( szProductfile, &insCfg, &array, &target, &photometry,
							&vis, &vis2, &vis3, &wave, &pst, &spectrum, errMsg ) != amdlibSUCCESS)
					{
						amdlibLogError("Could not load OI-FITS file amber_filtered.fits" );
						amdlibLogErrorDetail(errMsg);
						return amdlibFAILURE;

					}

					pHeader = cpl_propertylist_load(szProductfile, 0 );

					amber_qc(&wave, &vis, &vis2, &vis3, NULL, pHeader, "uncal");

					/*Adding the JMMC acknowledgements*/
					amber_JMMC_acknowledgement(pHeader);

					cpl_image_save(NULL, szProductfile, CPL_BPP_16_SIGNED, pHeader, CPL_IO_DEFAULT );
					outname_selector_paf=cpl_sprintf("qc_%s.paf", szProductfile);



					if (cpl_propertylist_has(pHeader, "ESO QC ARC") == 1)
					{
						cpl_propertylist_append_string(pHeader,"ARCFILE",
								(cpl_propertylist_get_string(pHeader, "ESO QC ARC")));
						//cpl_dfs_save_paf("AMBER",recipename,pHeader,outname_selector_paf);
						cpl_propertylist_erase(pHeader,"ARCFILE");
					}

					cpl_free((void *)outname_selector_paf);

					cpl_propertylist_delete(pHeader);


					amdlibSaveOiFile( szProductfile, &insCfg, &array, &target,
							&photometry, &vis, &vis2, &vis3, &wave, &pst, &spectrum, errMsg);

					amdlibReleaseSelection(&selectedFrames);
					if(array.thisPtr     !=NULL) amdlibReleaseOiArray(&array);
					if(target.thisPtr    !=NULL) amdlibReleaseOiTarget(&target);
					if(photometry.thisPtr!=NULL) amdlibReleasePhotometry(&photometry);
					if(vis.thisPtr       !=NULL) amdlibReleaseVis(&vis);
					if(vis2.thisPtr      !=NULL) amdlibReleaseVis2(&vis2);
					if(vis3.thisPtr      !=NULL) amdlibReleaseVis3(&vis3);
					if(wave.thisPtr      !=NULL) amdlibReleaseWavelength(&wave);
					if(spectrum.thisPtr  !=NULL) amdlibReleaseSpectrum(&spectrum);
					if(pst.thisPtr       !=NULL) amdlibReleasePiston(&pst);
					/* Now copy the file to tmp to be able to plot the files after the
                  originals are renamed by ESOREX
					 */
					//sprintf( szCommand, "cp %s /tmp/%s", szProductfile, szProductfile );
					//system( szCommand );



				}
				/* ---------------------------------------------------------- */




			} /* Selection did not cause errors */

		}
		else
		{
			/* not an AMBER file */
			iStatus = 2;
			sprintf ( szMessage, "ERROR: Cannot read [%s]. OI table structure is not from AMBER or the file has been corrupted. [%s]", szFilnameOI, errMsg );
			cpl_msg_info( fctid, "%s", szMessage );
		}
	}
	else /* found fitting frame ? */
	{
		/* no fitting frame */
		iStatus = 1;
		sprintf ( szMessage, "ERROR: Please input one file with either SCIENCE_REDUCED or CALIB_REDUCED." );
		cpl_msg_info( fctid, "%s", szMessage );
	}


	if( pFrame && !iStatus )
	{
		/*
        sprintf( szMessage, "cpl_frame_insert [%s]", cpl_frame_get_filename(pFrame ) );
        cpl_msg_info( fctid, "%s", szMessage );
		 */
		cpl_frameset_insert( framelist, pFrame );


	}
	else
	{
		if(pFrame!=NULL)
		{
			cpl_frame_delete(pFrame);
		}
	}
	/*
     sprintf ( szMessage, "Status: %d for %s", iStatus, szProductfile );
     cpl_msg_info( fctid, "%s", szMessage );
	 */

	cpl_msg_info( fctid, "End of DataReduction");

	cpl_error_reset();

	if (szFilnameOI != NULL)
	{
		cpl_free(szFilnameOI);
	}
	return iStatus;
}
