/* $Id: amber_p2vm.c,v 1.25 2013-09-16 14:56:43 agabasch Exp $
 *
 * This file is part of the AMBER Pipeline
 * Copyright (C) 2002,2003 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author: agabasch $
 * $Date: 2013-09-16 14:56:43 $
 * $Revision: 1.25 $
 * $Name: not supported by cvs2svn $
 */

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

/* AMDLIB usage switch */
#define USE_AMDLIB YES

/*-----------------------------------------------------------------------------
                                Includes
 -----------------------------------------------------------------------------*/

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

/* PAF file and QC log */
#include "giqclog.h"
/* #include "giutils.h" */



/* amdlib structures */
amdlibERROR_MSG         errMsg;
static    amdlibDARK_DATA   dark; /*  = {NULL}; */
static    amdlibRAW_DATA          rawData;   /*  = {NULL}; */
amdlibP2VM_INPUT_DATA   p2vmData;  /*  = {NULL}; */
amdlibP2VM_MATRIX       p2vm;      /*  = {NULL}; */
amdlibWAVEDATA          waveData;

// Offsets after Spectral Calibration
amdlibDOUBLE off[3]={amdlibOFFSETY_NOT_CALIBRATED,
		amdlibOFFSETY_NOT_CALIBRATED,
		amdlibOFFSETY_NOT_CALIBRATED};

char  szFilenameInP2VM[10][1024];
char  szFilenameInWAVE[4][1024];


cpl_frame         *   pframeProduct;
cpl_parameterlist *   gparlist;
cpl_frameset      *   gframelist;

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



static int amber_p2vm_create(cpl_plugin *) ;
static int amber_p2vm_exec(cpl_plugin *) ;
static int amber_p2vm_destroy(cpl_plugin *) ;
static int amber_p2vm(cpl_parameterlist *, cpl_frameset *) ;
int CreateProduct(const char * fctid, char * szRawFile,
		char * szFilenameProduct, long zeroframes);
amdlibCOMPL_STAT amdlibComputeP2vm3T_ESO(const char  * badPixelFile,
		const char  * flatFieldFile,
		const char  * biasFile,
		const char  * inputFile1,
		const char  * inputFile2,
		const char  * inputFile3,
		const char  * inputFile4,
		const char  * inputFile5,
		const char  * inputFile6,
		const char  * inputFile7,
		const char  * inputFile8,
		const char  * inputFile9,
		const char  * p2vmFile,
		amdlibDOUBLE *newSpectralOffsets,
		const char  * fctid);
amdlibCOMPL_STAT amdlibComputeP2vm2T_ESO(const char  * badPixelFile,
		const char  * flatFieldFile,
		const char  * biasFile,
		const char  * inputFile1,
		const char  * inputFile2,
		const char  * inputFile3,
		const char  * inputFile4,
		const char  * p2vmFile,
		amdlibDOUBLE *newSpectralOffsets,
		const char  * fctid);


static long amber_check_zeroframes(cpl_parameterlist * parlist,
		cpl_frameset * framelist);

cpl_error_code amber_p2vm_qc(const char * szFilenameP2VM,
		cpl_propertylist * qclist);

/*----------------------------------------------------------------------------*/
/**
  @brief    Append the properties in one list to another
  @param    self   The PAF propertylist to append to
  @param    other  The propertylist whose elements are copied or NULL
  @return   CPL_ERROR_NONE or the relevant #_cpl_error_code_ on error

  If other is NULL or empty nothing happens

  Comments are copied as well, but it is (currently) undefined how this works
  for non-unique properties.

  In addition the hierarchical keys will be changed according to the PAF rules:

  No ESO as first part
  No blank separator but a .
 */
/*----------------------------------------------------------------------------*/
static cpl_error_code amber_propertylist_append(cpl_propertylist * self,
		const cpl_propertylist * other)
{

	int i, c;
	char szPAF[2048];
	char szTMP[2048];

	if (other == NULL) return CPL_ERROR_NONE;

	/*visir_assure_code(self, CPL_ERROR_NULL_INPUT);*/

	for (i=0; i < cpl_propertylist_get_size(other); i++)
	{
		const cpl_property * prop = cpl_propertylist_get((cpl_propertylist *)
				other, i);
		const char * name    = cpl_property_get_name(prop);
		/*const char * comment = cpl_property_get_comment(prop);*/

		cpl_error_code err;

		/* Change the key to PAF syntax */
		strcpy( szPAF, name );

		/* Get rid of ESO (HAR, what a joke!) */
		if( !strncmp( szPAF, "ESO", 3 ) )
		{
			strcpy( szTMP, &szPAF[4] );
			strcpy( szPAF, szTMP );
		}

		/* Substitute spaces by points (YEAH!) */
		for( c=0; c<(int)strlen(szPAF); c++ )
			if( szPAF[c] == ' ' )
				szPAF[c] = '.';

		strcpy( (char *)name, szPAF );

		/* Append prop to self */

		switch (cpl_property_get_type(prop)) {
		case CPL_TYPE_CHAR:
			err = cpl_propertylist_append_char(self, name,
					cpl_property_get_char(prop));
			break;
		case CPL_TYPE_BOOL:
			err = cpl_propertylist_append_bool(self, name,
					cpl_property_get_bool(prop));
			break;
		case CPL_TYPE_INT:
			err = cpl_propertylist_append_int(self, name,
					cpl_property_get_int(prop));
			break;
		case CPL_TYPE_LONG:
			err = cpl_propertylist_append_long(self, name,
					cpl_property_get_long(prop));
			break;
		case CPL_TYPE_FLOAT:
			err = cpl_propertylist_append_float(self, name,
					cpl_property_get_float(prop));
			break;
		case CPL_TYPE_DOUBLE:
			err = cpl_propertylist_append_double(self, name,
					cpl_property_get_double(prop));
			break;
		case CPL_TYPE_STRING:
			err = cpl_propertylist_append_string(self, name,
					cpl_property_get_string(prop));
			break;

			/* Avoid compiler warnings but do nothing */
		case CPL_TYPE_FLAG_ARRAY:
		case CPL_TYPE_INVALID:
		case CPL_TYPE_UCHAR:
		case CPL_TYPE_UINT:
		case CPL_TYPE_ULONG:
		case CPL_TYPE_POINTER:
		case CPL_TYPE_FLOAT_COMPLEX:
		case CPL_TYPE_DOUBLE_COMPLEX:
		case CPL_TYPE_UNSPECIFIED:
#if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(5, 2, 0)
		case CPL_TYPE_COMPLEX:
#endif
			/*case CPL_TYPE_COMPLEX: not anymode in CPL 3.0 */
			break;
			/*default:
            visir_assure_code(0, CPL_ERROR_UNSUPPORTED_MODE);*/
		}

		/*visir_assure_code(!err, err);

        if (comment && cpl_propertylist_set_comment(self, name, comment))
            visir_assure_code(0, cpl_error_get_code());*/
	}

	return CPL_ERROR_NONE;
}

int CreateProduct(const char * fctid, char * szRawFile,
		char * szFilenameProduct, long zeroframes)
{
	int  iStatus = 0;
	char szMessage[1024];

	cpl_propertylist * pHeader;
	/*
	cpl_table        * pTable=NULL;
	cpl_propertylist * pTableHeader=NULL;
	int                iTable=1;
	int                iError=CPL_ERROR_NONE;
	 */

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

	pframeProduct = cpl_frame_new();

	if( pframeProduct )
	{
		cpl_frame_set_filename( pframeProduct, szFilenameProduct );
		cpl_frame_set_type( pframeProduct, CPL_FRAME_TYPE_TABLE );
		cpl_frame_set_tag( pframeProduct, "P2VM_REDUCED" );
		cpl_frame_set_group( pframeProduct, CPL_FRAME_GROUP_PRODUCT );
		cpl_frame_set_level( pframeProduct,  CPL_FRAME_LEVEL_INTERMEDIATE );
	}
	else
	{
		cpl_msg_info( fctid, "No memory for product frame." );
		iStatus = 15;
	}

	/*
	 * 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
	 */
	pHeader = cpl_propertylist_load(  szRawFile, 0 );


	sprintf( szMessage, "Extracting Header from file %s.",  szRawFile );
	cpl_msg_info( fctid, "%s", szMessage );









	/* 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,
			pframeProduct, /*pFrame,*/
			gframelist,
			gparlist,
			"amber_p2vm", /* const char *  recid,  */
			PACKAGE "/" PACKAGE_VERSION, /* const char *  pipeline_id,  */
			PRODUCT_DID,  /* const char *  dictionary_id */
			NULL
	)  != CPL_ERROR_NONE )
	{
		/* Error */
		sprintf( szMessage, "Error in setting up the product header." );
		cpl_msg_info( fctid, "%s", szMessage );
		cpl_msg_error(cpl_func,"Error in cpl_dfs_setup_product_header: %s %s",
				cpl_error_get_message(),cpl_error_get_where());
		iStatus = 16;
	}
#else 
	if( cpl_dfs_setup_product_header(  pHeader,
			pframeProduct, /*pFrame,*/
			gframelist,
			gparlist,
			"amber_p2vm", /* 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 );
		cpl_msg_error(cpl_func,"Error in cpl_dfs_setup_product_header: %s %s",
				cpl_error_get_message(),cpl_error_get_where());
		iStatus = 16;
	}

#endif   


	/* Write the product including proper DFS header*/
	/*
	cpl_table        * pTable=NULL;
	cpl_propertylist * pTableHeader=NULL;
	int                iTable=1;
	int                iError=CPL_ERROR_NONE;

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

	sprintf( szMessage, "Creating Product %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
	 */
	/*	FILE *         pFITSFile = NULL;*/

	/*----------------------------------------------------------------*/
	/* Update the Spectral Calibration to header DET.P1.OFFSETY a.s.o */

	/*In case it has not been calculated then take it from the DET1 P1 OFFSETY*/
	if(  off[0] != amdlibOFFSETY_NOT_CALIBRATED )
	{
		cpl_propertylist_update_double( pHeader, "ESO DET1 P1 OFFSETY", off[0]);
		cpl_propertylist_update_double( pHeader, "ESO DET1 P2 OFFSETY", off[1]);
		cpl_propertylist_update_double( pHeader, "ESO DET1 P3 OFFSETY", off[2]);
	}
	else
	{
		off[0] = cpl_propertylist_get_double( pHeader, "ESO DET1 P1 OFFSETY" );
		off[1] = cpl_propertylist_get_double( pHeader, "ESO DET1 P2 OFFSETY" );
		off[2] = cpl_propertylist_get_double( pHeader, "ESO DET1 P3 OFFSETY" );
	}

	cpl_propertylist_append_double( pHeader, "ESO QC P1 OFFSETY", off[0] );
	cpl_propertylist_append_double( pHeader, "ESO QC P2 OFFSETY", off[1] );
	cpl_propertylist_append_double( pHeader, "ESO QC P3 OFFSETY", off[2] );

	cpl_propertylist_append_long( pHeader, "ESO QC ZEROFRAMES", zeroframes );

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


	sprintf( szMessage, "Writing Product Header..." );
	cpl_msg_info( fctid, "%s", szMessage );

	iStatus = 0;
	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");
		iStatus = 666;
	}

	cpl_propertylist_delete(pHeader);



	return iStatus;
} 

amdlibCOMPL_STAT amdlibComputeP2vm3T_ESO(const char  * badPixelFile,
		const char  * flatFieldFile,
		const char  * biasFile,
		const char  * inputFile1,
		const char  * inputFile2,
		const char  * inputFile3,
		const char  * inputFile4,
		const char  * inputFile5,
		const char  * inputFile6,
		const char  * inputFile7,
		const char  * inputFile8,
		const char  * inputFile9,
		const char  * p2vmFile,
		amdlibDOUBLE *newSpectralOffsets,
		const char  * fctid)
{

	char szMessage[1024];
	const  char           * inputFiles[10];
	int              i;
	amdlibBOOLEAN waveDataLoaded = amdlibFALSE;

	/* Init list of input files */
	inputFiles[0] = biasFile;
	inputFiles[1] = inputFile1;
	inputFiles[2] = inputFile2;
	inputFiles[3] = inputFile3;
	inputFiles[4] = inputFile4;
	inputFiles[5] = inputFile5;
	inputFiles[6] = inputFile6;
	inputFiles[7] = inputFile7;
	inputFiles[8] = inputFile8;
	inputFiles[9] = inputFile9;

	/* If a bad pixel file has been specified */
	sprintf( szMessage, "Loading BAD PIXEL MAP %s ...", badPixelFile );
	cpl_msg_info( fctid, "%s", szMessage );
	if ((badPixelFile != NULL) && (strlen(badPixelFile) != 0))
	{
		/* Load it */
		if( amdlibLoadBadPixelMap(badPixelFile, errMsg) != amdlibSUCCESS)
		{
			printf ("Could not load bad pixel map '%s'\n", badPixelFile);
			printf ("%s\n", errMsg);
			return (amdlibFAILURE);
		}
	}

	/* Load flat field map */
	sprintf( szMessage, "Loading FLAT FIELD %s ...", flatFieldFile );
	cpl_msg_info( fctid, "%s", szMessage );

	if( (flatFieldFile != NULL) && (strlen(flatFieldFile) != 0))
	{
		if( amdlibLoadFlatFieldMap( flatFieldFile, errMsg) != amdlibSUCCESS)
		{
			printf ("Could not load Flat Field map '%s'\n", flatFieldFile);
			printf ("%s\n", errMsg);
			return (amdlibFAILURE);
		}
	}

	/* Check P2VM file name */
	if ((p2vmFile == NULL) || (strlen(p2vmFile) == 0))
	{
		printf ("Invalid name for P2VM file\n");
		return (amdlibFAILURE);
	}



	/* For each input files */
	for (i = 0; i < 10; i++)
	{
		if ((inputFiles[i] == NULL) || (strlen(inputFiles[i]) == 0))
		{
			printf ("Invalid name for %dth input file\n", i+1);
			return (amdlibFAILURE);
		}

		/* Load raw data */
		sprintf( szMessage, "Loading P2VM file %s ...", inputFiles[i] );
		cpl_msg_info( fctid, "%s", szMessage );
		if (amdlibLoadRawData(inputFiles[i], &rawData, errMsg) != amdlibSUCCESS)
		{
			printf ("Could not load raw data file '%s'\n", inputFiles[i]);
			printf ("%s\n", errMsg);
			amdlibReleaseRawData(&rawData);
			return (amdlibFAILURE);
		}

		if (rawData.frameType == amdlibUNKNOWN_FRAME)
		{
			printf ("Invalid frame type '%d'\n", amdlibUNKNOWN_FRAME);
			amdlibReleaseRawData(&rawData);
			return (amdlibFAILURE);
		}
		else if (rawData.frameType == amdlibDARK_FRAME)
		{
			sprintf( szMessage, "Computing Pixel Bias Map for P2VM file %s ...",
					inputFiles[i] );
			cpl_msg_info( fctid, "%s", szMessage );

			/* Compute pixel bias map */
			if (amdlibGenerateDarkData(&rawData, &dark, errMsg)
					!= amdlibSUCCESS)
			{
				printf ("Could not generate pixel bias map\n");
				amdlibReleaseDarkData(&dark);
				amdlibReleaseRawData(&rawData);
				return (amdlibFAILURE);
			}
		}
		else
		{
			int p;

			sprintf( szMessage, "Equalizing P2VM file %s ...", inputFiles[i] );
			cpl_msg_info( fctid, "%s", szMessage );

			/* Equalize raw data */
			if( amdlibCalibrateRawData( &dark, &rawData, errMsg)
					!=amdlibSUCCESS)
			{
				printf ("%s\n", errMsg);
				amdlibReleaseDarkData(&dark);
				amdlibReleaseRawData(&rawData);
				return (amdlibFAILURE);
			}

			/* Test if some pixel is saturated */
			if (rawData.dataIsSaturated)
			{
				sprintf( szMessage, "Data in file %s is saturated!",
						inputFiles[i] );
				cpl_msg_info( fctid, "%s", szMessage );
			}

			/* Get wave data from the first given file ??? check this -
			 * see declaration above !!! */
			if( waveDataLoaded == amdlibFALSE )
			{
				if( amdlibGetWaveDataFromRawData( &rawData, &waveData, errMsg)
						== amdlibFAILURE)
				{
					printf ("%s\n", errMsg);
					amdlibReleaseDarkData(&dark);
					amdlibReleaseRawData(&rawData);
					amdlibReleaseP2vmData(&p2vmData);
					return (amdlibFAILURE);
				}

				/* And set new offsets (if given) */
				for (p = 0; p < 3; p++)
				{
					if (newSpectralOffsets[p] != amdlibOFFSETY_NOT_CALIBRATED)
					{
						waveData.photoOffset[p] = newSpectralOffsets[p];
					}
				}

				sprintf( szMessage, "Set new spectral Calibration." );
				cpl_msg_info( fctid, "%s", szMessage );

				waveDataLoaded = amdlibTRUE;
			}

			/* Store calibrated data into P2VM data structure */
			sprintf( szMessage, "Adding P2VM file %s ...", inputFiles[i] );
			cpl_msg_info( fctid, "%s", szMessage );
			if( amdlibAddToP2vmData( &rawData, &waveData, &p2vmData, errMsg)
					!= amdlibSUCCESS)
			{
				printf ("%s\n", errMsg);
				amdlibReleaseDarkData(&dark);
				amdlibReleaseRawData(&rawData);
				amdlibReleaseP2vmData(&p2vmData);
				return (amdlibFAILURE);
			}
		}
		amdlibReleaseRawData(&rawData);
	}
	/* End for */

	amdlibReleaseDarkData(&dark);


	/* Compute P2VM */
	sprintf( szMessage, "Computing P2VM ..." );
	cpl_msg_info( fctid, "%s", szMessage );

	if( amdlibComputeP2VM( &p2vmData, amdlibP2VM_3T, &waveData, &p2vm, errMsg)
			== amdlibFAILURE)
	{
		printf ("%s\n", errMsg);
		amdlibReleaseP2vmData(&p2vmData);
		amdlibReleaseP2VM(&p2vm);
		return (amdlibFAILURE);
	}

	amdlibReleaseP2vmData(&p2vmData);

	sprintf( szMessage, "Saving P2VM %s...", p2vmFile );
	cpl_msg_info( fctid, "%s", szMessage );

	if( amdlibSaveP2VM( p2vmFile, &p2vm, amdlibP2VM_UNKNOWN_ACC, errMsg)
			== amdlibFAILURE )
	{
		printf ("%s\n", errMsg);
		amdlibReleaseP2VM(&p2vm);
		return (amdlibFAILURE);
	}

	amdlibReleaseP2VM(&p2vm);


	return (amdlibSUCCESS);
}

amdlibCOMPL_STAT amdlibComputeP2vm2T_ESO(const char  * badPixelFile,
		const char  * flatFieldFile,
		const char  * biasFile,
		const char  * inputFile1,
		const char  * inputFile2,
		const char  * inputFile3,
		const char  * inputFile4,
		const char  * p2vmFile,
		amdlibDOUBLE *newSpectralOffsets,
		const char  * fctid)
{

	char szMessage[1024];
	const  char           * inputFiles[10];
	int              i;
	amdlibBOOLEAN waveDataLoaded = amdlibFALSE;

	/* Init list of input files */
	inputFiles[0] = biasFile;
	inputFiles[1] = inputFile1;
	inputFiles[2] = inputFile2;
	inputFiles[3] = inputFile3;
	inputFiles[4] = inputFile4;


	/* If a bad pixel file has been specified */
	sprintf( szMessage, "Loading BAD PIXEL MAP %s ...", badPixelFile );
	cpl_msg_info( fctid, "%s", szMessage );
	if ((badPixelFile != NULL) && (strlen(badPixelFile) != 0))
	{
		/* Load it */
		if( amdlibLoadBadPixelMap(badPixelFile, errMsg) != amdlibSUCCESS)
		{
			printf ("Could not load bad pixel map '%s'\n", badPixelFile);
			printf ("%s\n", errMsg);
			return (amdlibFAILURE);
		}
	}

	/* Load flat field map */
	sprintf( szMessage, "Loading FLAT FIELD %s ...", flatFieldFile );
	cpl_msg_info( fctid, "%s", szMessage );

	if( (flatFieldFile != NULL) && (strlen(flatFieldFile) != 0))
	{
		if( amdlibLoadFlatFieldMap( flatFieldFile, errMsg) != amdlibSUCCESS)
		{
			printf ("Could not load Flat Field map '%s'\n", flatFieldFile);
			printf ("%s\n", errMsg);
			return (amdlibFAILURE);
		}
	}

	/* Check P2VM file name */
	if ((p2vmFile == NULL) || (strlen(p2vmFile) == 0))
	{
		printf ("Invalid name for P2VM file\n");
		return (amdlibFAILURE);
	}



	/* For each input files */
	for (i = 0; i < 5; i++)
	{
		if ((inputFiles[i] == NULL) || (strlen(inputFiles[i]) == 0))
		{
			printf ("Invalid name for %dth input file\n", i+1);
			return (amdlibFAILURE);
		}

		/* Load raw data */
		sprintf( szMessage, "Loading P2VM file %s ...", inputFiles[i] );
		cpl_msg_info( fctid, "%s", szMessage );
		if (amdlibLoadRawData(inputFiles[i], &rawData, errMsg) != amdlibSUCCESS)
		{
			printf ("Could not load raw data file '%s'\n", inputFiles[i]);
			printf ("%s\n", errMsg);
			amdlibReleaseRawData(&rawData);
			return (amdlibFAILURE);
		}

		if (rawData.frameType == amdlibUNKNOWN_FRAME)
		{
			printf ("Invalid frame type '%d'\n", amdlibUNKNOWN_FRAME);
			amdlibReleaseRawData(&rawData);
			return (amdlibFAILURE);
		}
		else if (rawData.frameType == amdlibDARK_FRAME)
		{
			sprintf( szMessage, "Computing Pixel Bias Map for P2VM file %s ...",
					inputFiles[i] );
			cpl_msg_info( fctid, "%s", szMessage );

			/* Compute pixel bias map */
			if (amdlibGenerateDarkData(&rawData, &dark, errMsg)
					!= amdlibSUCCESS)
			{
				printf ("Could not generate pixel bias map\n");
				amdlibReleaseDarkData(&dark);
				amdlibReleaseRawData(&rawData);
				return (amdlibFAILURE);
			}
		}
		else
		{
			int p;

			sprintf( szMessage, "Equalizing P2VM file %s ...", inputFiles[i] );
			cpl_msg_info( fctid, "%s", szMessage );

			/* Equalize raw data */
			if( amdlibCalibrateRawData( &dark, &rawData, errMsg)
					!=amdlibSUCCESS)
			{
				printf ("%s\n", errMsg);
				amdlibReleaseDarkData(&dark);
				amdlibReleaseRawData(&rawData);
				return (amdlibFAILURE);
			}

			/* Test if some pixel is saturated */
			if (rawData.dataIsSaturated)
			{
				sprintf( szMessage, "Data in file %s is saturated!",
						inputFiles[i] );
				cpl_msg_info( fctid, "%s", szMessage );
			}

			/* Get wave data from the first given file ??? check this
			 *  - see declaration above !!! */
			if( waveDataLoaded == amdlibFALSE )
			{
				if( amdlibGetWaveDataFromRawData( &rawData, &waveData, errMsg)
						== amdlibFAILURE)
				{
					printf ("%s\n", errMsg);
					amdlibReleaseDarkData(&dark);
					amdlibReleaseRawData(&rawData);
					amdlibReleaseP2vmData(&p2vmData);
					return (amdlibFAILURE);
				}

				/* And set new offsets (if given) */
				for (p = 0; p < 3; p++)
				{
					if (newSpectralOffsets[p] != amdlibOFFSETY_NOT_CALIBRATED)
					{
						waveData.photoOffset[p] = newSpectralOffsets[p];
					}
				}

				sprintf( szMessage, "Set new spectral Calibration." );
				cpl_msg_info( fctid, "%s", szMessage );

				waveDataLoaded = amdlibTRUE;
			}

			/* Store calibrated data into P2VM data structure */
			sprintf( szMessage, "Adding P2VM file %s ...", inputFiles[i] );
			cpl_msg_info( fctid, "%s", szMessage );
			if( amdlibAddToP2vmData( &rawData, &waveData, &p2vmData, errMsg)
					!= amdlibSUCCESS)
			{
				printf ("%s\n", errMsg);
				amdlibReleaseDarkData(&dark);
				amdlibReleaseRawData(&rawData);
				amdlibReleaseP2vmData(&p2vmData);
				return (amdlibFAILURE);
			}
		}
		amdlibReleaseRawData(&rawData);
	}
	/* End for */

	amdlibReleaseDarkData(&dark);


	/* Compute P2VM */
	sprintf( szMessage, "Computing P2VM ..." );
	cpl_msg_info( fctid, "%s", szMessage );

	if( amdlibComputeP2VM( &p2vmData, amdlibP2VM_2T, &waveData, &p2vm, errMsg)
			== amdlibFAILURE)
	{
		printf ("%s\n", errMsg);
		amdlibReleaseP2vmData(&p2vmData);
		amdlibReleaseP2VM(&p2vm);
		return (amdlibFAILURE);
	}

	amdlibReleaseP2vmData(&p2vmData);

	sprintf( szMessage, "Saving P2VM %s...", p2vmFile );
	cpl_msg_info( fctid, "%s", szMessage );

	if( amdlibSaveP2VM( p2vmFile, &p2vm, amdlibP2VM_UNKNOWN_ACC, errMsg)
			== amdlibFAILURE )
	{
		printf ("%s\n", errMsg);
		amdlibReleaseP2VM(&p2vm);
		return (amdlibFAILURE);
	}

	amdlibReleaseP2VM(&p2vm);


	return (amdlibSUCCESS);
}



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

/*
static struct {
	 Inputs
	int         bool_option ;
	char        str_option[512] ;

	 Outputs
	double      qc_param ;
} amber_p2vm_config ;
 */

static char amber_p2vm_man[] =
		"This recipe creates a Pixel-To-Visibility-Matrix "
		"for 2- and 3-Telescopes Mode\n"
		"\n";

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

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

  This function is exported.
 */
/*----------------------------------------------------------------------------*/
int cpl_plugin_get_info(cpl_pluginlist * list)
{
	cpl_recipe  *   recipe = cpl_calloc(1, sizeof(*recipe)) ;
	cpl_plugin  *   plugin = &recipe->interface ;

	cpl_plugin_init(plugin,
			CPL_PLUGIN_API,
			AMBER_BINARY_VERSION,
			CPL_PLUGIN_TYPE_RECIPE,
			"amber_p2vm",
			"AMBER Pixel-To-Visibility-Matrix for 2- and 3-Telescopes Mode",
			amber_p2vm_man,
			"Tom Licha",
			PACKAGE_BUGREPORT,
			"GPL",
			amber_p2vm_create,
			amber_p2vm_exec,
			amber_p2vm_destroy) ;

	cpl_pluginlist_append(list, plugin) ;

	return 0;
}

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

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

	/*
	 * Check that the plugin is part of a valid recipe
	 */

	if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
		recipe = (cpl_recipe *)plugin;
	else
		return -1;

	/*
	 * Create the parameters list in the cpl_recipe object
	 */

	recipe->parameters = cpl_parameterlist_new();


	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_BEAUTIFY_PISTON", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_BEAUTIFY_PISTON");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_CHISQUARE_LIMIT", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_CHISQUARE_LIMIT");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_MAX_PISTON_ERROR", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_MAX_PISTON_ERROR");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_SHIFT_WLENTABLE", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_SHIFT_WLENTABLE");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_MIN_PHOTOMETRY", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_MIN_PHOTOMETRY");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_CORRECT_OPD0", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_CORRECT_OPD0");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_LINEARIZE_P2VM_PHASE", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_LINEARIZE_P2VM_PHASE");
	//	cpl_parameterlist_append(recipe->parameters, p);

	p = cpl_parameter_new_value("amber.amber_p2vm.activate_NORMALIZE_P2VM", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",TRUE);
	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_NORMALIZE_P2VM");
	cpl_parameterlist_append(recipe->parameters, p);

	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_NO_FUDGE", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_NO_FUDGE");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_NO_BIAS", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_NO_BIAS");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_DROP", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_DROP");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_NORMALIZE_SPECTRUM", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_NORMALIZE_SPECTRUM");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_BOXCARSMOOTH_P2VM_PHASE", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_BOXCARSMOOTH_P2VM_PHASE");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_GAUSSSMOOTH_P2VM_PHASE", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_GAUSSSMOOTH_P2VM_PHASE");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_AUTO_BADPIXEL", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_AUTO_BADPIXEL");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_MAX_PISTON_EXCURSION", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_MAX_PISTON_EXCURSION");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_GLOBAL_PHOTOMETRY", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_GLOBAL_PHOTOMETRY");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_USE_GAIN", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_USE_GAIN");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_ZAP_JHK_DISCONTINUTIES", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_ZAP_JHK_DISCONTINUTIES");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.activate_AUTO_SHIFT_JHK", CPL_TYPE_BOOL, "TBD", "amber.amber_p2vm",FALSE);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"activate_AUTO_SHIFT_JHK");
	//	cpl_parameterlist_append(recipe->parameters, p);


	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_BEAUTIFY_PISTON", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_BEAUTIFY_PISTON");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_CHISQUARE_LIMIT", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_CHISQUARE_LIMIT");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_MAX_PISTON_ERROR", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_MAX_PISTON_ERROR");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_SHIFT_WLENTABLE", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_SHIFT_WLENTABLE");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_MIN_PHOTOMETRY", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_MIN_PHOTOMETRY");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_CORRECT_OPD0", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_CORRECT_OPD0");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_LINEARIZE_P2VM_PHASE", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_LINEARIZE_P2VM_PHASE");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_NORMALIZE_P2VM", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_NORMALIZE_P2VM");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_NO_FUDGE", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_NO_FUDGE");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_NO_BIAS", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_NO_BIAS");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_DROP", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_DROP");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_NORMALIZE_SPECTRUM", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_NORMALIZE_SPECTRUM");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_BOXCARSMOOTH_P2VM_PHASE", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_BOXCARSMOOTH_P2VM_PHASE");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_GAUSSSMOOTH_P2VM_PHASE", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_GAUSSSMOOTH_P2VM_PHASE");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_AUTO_BADPIXEL", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_AUTO_BADPIXEL");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_MAX_PISTON_EXCURSION", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_MAX_PISTON_EXCURSION");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_GLOBAL_PHOTOMETRY", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_GLOBAL_PHOTOMETRY");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_USE_GAIN", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",0.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_USE_GAIN");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_ZAP_JHK_DISCONTINUTIES", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_ZAP_JHK_DISCONTINUTIES");
	//	cpl_parameterlist_append(recipe->parameters, p);
	//	p = cpl_parameter_new_value("amber.amber_p2vm.value_AUTO_SHIFT_JHK", CPL_TYPE_DOUBLE, "TBD", "amber.amber_p2vm",1.0);
	//	cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,"value_AUTO_SHIFT_JHK");
	//	cpl_parameterlist_append(recipe->parameters, p);


	return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int amber_p2vm_exec(cpl_plugin * plugin)
{
	/*char szMessage[1024];	*/

	cpl_recipe * recipe = (cpl_recipe *)plugin ;

	/*sprintf( szMessage, "Now calling recipe at [%d] with Frames [%d] and
	 * Parameters [%x].", recipe,  recipe->frames, recipe->parameters );
    cpl_msg_info("amber_p2vm_exec", szMessage );*/

	return amber_p2vm(recipe->parameters, recipe->frames) ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Destroy what has been created by the 'create' function
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int amber_p2vm_destroy(cpl_plugin * plugin)
{
	cpl_recipe  *   recipe = (cpl_recipe *)plugin ;
	cpl_parameterlist_delete(recipe->parameters) ;
	return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Get the command line options and execute the data reduction
  @param    parlist     the parameters list
  @param    framelist   the frames list
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int amber_p2vm(
		cpl_parameterlist     *   parlist,
		cpl_frameset    *   framelist)
{


	/* CPL structures */
	cpl_frameset * cur_set;
	cpl_frame    * cur_frame;
	cpl_frameset_iterator * it = NULL;
	/*	FILE         * fpWave;*/
	long zeroframes = 0;
	char   szMessage[1024];
	/*	char   szBuffer[1024];*/
	char   szFilenameP2VM[1024];
	char   szFilenameWave[1024];
	char   szCommandline[2048];
	char * pszFilename;
	char * pszFileTag;
	const char * pszARCFILE;

	int  iStatus      = 0;

	int  i2TelFrames  = 0;
	int  i3TelFrames  = 0;
	int  iWriteQC     = 0;

	char  szFilenameBadPix[1024];
	char  szFilenameFlat[1024];
	const char * fctid = "amber_p2vm";
	//FILE * fp = NULL;
	/*	int   s, iPhot;*/
	cpl_errorstate   prestate=0;
	cpl_propertylist * applist=NULL;

	amdlibDOUBLE spectralOffsets[3]= {amdlibOFFSETY_NOT_CALIBRATED,
			amdlibOFFSETY_NOT_CALIBRATED,
			amdlibOFFSETY_NOT_CALIBRATED};
	AmPaf            * qc            = NULL;
	cpl_propertylist * qclog         = NULL;
	cpl_propertylist * qcFromProduct = NULL;
	cpl_propertylist * qcFromRawfile = NULL;


	/*Activate and deactivate amdlib parameters here*/
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_BEAUTIFY_PISTON")))          {amdlibSetUserPref(amdlibBEAUTIFY_PISTON, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_BEAUTIFY_PISTON")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_CHISQUARE_LIMIT")))          {amdlibSetUserPref(amdlibCHISQUARE_LIMIT, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_CHISQUARE_LIMIT")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_SHIFT_WLENTABLE")))          {amdlibSetUserPref(amdlibSHIFT_WLENTABLE, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_SHIFT_WLENTABLE")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_MIN_PHOTOMETRY")))           {amdlibSetUserPref(amdlibMIN_PHOTOMETRY, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_MIN_PHOTOMETRY")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_CORRECT_OPD0")))             {amdlibSetUserPref(amdlibCORRECT_OPD0, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_CORRECT_OPD0")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_LINEARIZE_P2VM_PHASE")))     {amdlibSetUserPref(amdlibLINEARIZE_P2VM_PHASE, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_LINEARIZE_P2VM_PHASE")));}

	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_NORMALIZE_P2VM")))           {amdlibSetUserPref(amdlibNORMALIZE_P2VM, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_NORMALIZE_P2VM")));}
	if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_NORMALIZE_P2VM")))           {amdlibSetUserPref(amdlibNORMALIZE_P2VM, 1.0);}


	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_NO_FUDGE")))                 {amdlibSetUserPref(amdlibNO_FUDGE, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_NO_FUDGE")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_NO_BIAS")))                  {amdlibSetUserPref(amdlibNO_BIAS, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_NO_BIAS")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_DROP")))                     {amdlibSetUserPref(amdlibDROP, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_DROP")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_NORMALIZE_SPECTRUM")))       {amdlibSetUserPref(amdlibNORMALIZE_SPECTRUM, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_NORMALIZE_SPECTRUM")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_BOXCARSMOOTH_P2VM_PHASE")))  {amdlibSetUserPref(amdlibBOXCARSMOOTH_P2VM_PHASE, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_BOXCARSMOOTH_P2VM_PHASE")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_GAUSSSMOOTH_P2VM_PHASE")))   {amdlibSetUserPref(amdlibGAUSSSMOOTH_P2VM_PHASE, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_GAUSSSMOOTH_P2VM_PHASE")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_AUTO_BADPIXEL")))            {amdlibSetUserPref(amdlibAUTO_BADPIXEL, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_AUTO_BADPIXEL")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_GLOBAL_PHOTOMETRY")))        {amdlibSetUserPref(amdlibGLOBAL_PHOTOMETRY, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_GLOBAL_PHOTOMETRY")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_USE_GAIN")))                 {amdlibSetUserPref(amdlibUSE_GAIN, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_USE_GAIN")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_ZAP_JHK_DISCONTINUTIES")))   {amdlibSetUserPref(amdlibZAP_JHK_DISCONTINUTIES, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_ZAP_JHK_DISCONTINUTIES")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_AUTO_SHIFT_JHK")))           {amdlibSetUserPref(amdlibAUTO_SHIFT_JHK, cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_AUTO_SHIFT_JHK")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_MAX_PISTON_ERROR")))         {amdlibSetUserPref(amdlibMAX_PISTON_ERROR, 1000*cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_MAX_PISTON_ERROR")));}
	//if (cpl_parameter_get_bool(cpl_parameterlist_find(parlist, "amber.amber_p2vm.activate_MAX_PISTON_EXCURSION")))     {amdlibSetUserPref(amdlibMAX_PISTON_EXCURSION, 1000*cpl_parameter_get_double(cpl_parameterlist_find(parlist, "amber.amber_p2vm.value_MAX_PISTON_EXCURSION")));};



	/* save for CreateProduct function */
	gframelist = framelist;
	gparlist   = parlist;

	/*Set the cpl group*/
	amber_dfs_set_groups(framelist);

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

	cpl_msg_info( fctid, "Start of DataReduction");

	/* log of the fprint of the amdlib */

	//fp = freopen( "amdlib_p2vm.log", "w", stdout );

	cpl_msg_info(cpl_func,"Counting bad frames: ...");
	/*Count the number of frames with timeTag of zero*/
	zeroframes=amber_check_zeroframes(parlist, framelist);
	cpl_msg_info(cpl_func,"Zeroframes found: %ld\n", zeroframes);



	/* Product Name (faked) */
	strcpy( szFilenameP2VM, "p2vm_produced_by_amber_p2vm.fits" );
	strcpy( szFilenameWave, "temp_spec_calib.asc" );

	strcpy( szFilenameBadPix, "" );
	strcpy( szFilenameFlat  , "" );
	memset( szFilenameInP2VM, 0, sizeof( szFilenameInP2VM ) );
	memset( szFilenameInWAVE, 0, sizeof( szFilenameInWAVE ) );

	/* Find BadPix and FlatField first Frames */
	cur_frame = cpl_frameset_find( framelist, "AMBER_FLATFIELD" );

	if( cur_frame )
	{
		/* Get Filename and Classification Tag */
		pszFilename = (char *)cpl_frame_get_filename( cur_frame );
		pszFileTag  = (char *)cpl_frame_get_tag( cur_frame );

		strcpy( szFilenameFlat, pszFilename );

		sprintf ( szMessage, "FLAT FIELD identified %s", pszFilename );
		cpl_msg_info( fctid, "%s", szMessage );
	}

	cur_frame = cpl_frameset_find( framelist, "AMBER_BADPIX" );

	if( cur_frame )
	{
		/* Get Filename and Classification Tag */
		pszFilename = (char *)cpl_frame_get_filename( cur_frame );
		pszFileTag  = (char *)cpl_frame_get_tag( cur_frame );

		strcpy( szFilenameBadPix, pszFilename );

		sprintf ( szMessage, "BAD PIXEL MAP identified %s", pszFilename );
		cpl_msg_info( fctid, "%s", szMessage );
	}

	/* Do not continue without BADPIX and FALTFIELD */
	if( strlen(szFilenameBadPix)==0 || strlen(szFilenameFlat)==0 )
	{
		iStatus = 2;
		sprintf ( szMessage, "Support Frames needed: AMBER_BADPIX and "
				"AMBER_FLAT." );
		cpl_msg_info( fctid, "%s", szMessage );

		/* Close the amdlib logfile */
		//if( fp )
		//	fclose( fp );

		return iStatus;
	}

	/* TEST if 2P2V or 3P2V is present */
	cur_frame = cpl_frameset_find( framelist, "AMBER_3P2V" );

	if( !cur_frame )
	{
		cur_frame = cpl_frameset_find( framelist, "AMBER_2P2V" );

		if( !cur_frame )
		{
			sprintf (szMessage, "Error: 10 Frames of type AMBER_3P2V are needed"
					" or 5 Frames of type AMBER_2P2V are needed", pszFilename );
			cpl_msg_info( fctid, "%s", szMessage );

			iStatus = 666;
		}
	}



	/*

    Walk through the whole Set of Frames SOF and search for AMBER_WAVE files

    If theses files are present, we will perform a spectral calibration, to
    calculate the pixelshift of the CCD channels.
	 */
	sprintf ( szMessage, "Searching for Spectral Calibration (WAVE) Files... "
			"(current status = %d)", iStatus );
	cpl_msg_info( fctid, "%s", szMessage );

	it = cpl_frameset_iterator_new(framelist);
	cur_frame = cpl_frameset_iterator_get(it);

	while( cur_frame && !iStatus )
	{
		/* Get Filename and Classification Tag */
		pszFilename = (char *)cpl_frame_get_filename( cur_frame );
		pszFileTag  = (char *)cpl_frame_get_tag( cur_frame );

		/* Check Tag and Filename */
		if( pszFilename && pszFileTag )
		{
			/* 3 WAVE (4 needed) */
			if( !strcmp( pszFileTag, "AMBER_3WAVE" ) && i3TelFrames<4 )
			{
				/* Add to Framelist */
				sprintf ( szMessage, "%d. 3WAVE identified %s", i3TelFrames+1,
						pszFilename );
				cpl_msg_info( fctid, "%s", szMessage );
				strcpy( szFilenameInWAVE[i3TelFrames], pszFilename );
				i3TelFrames++;
			}

			/* Only use 2-Telescopes files when no 3-Telecopes file
			 * arrived yet */
			if( !i3TelFrames )
			{
				/* 2 WAVE (3 needed) */
				if( !strcmp( pszFileTag, "AMBER_2WAVE" ) && i2TelFrames<4 )
				{
					/* Add to Framelist */
					sprintf ( szMessage, "%d. 2WAVE identified %s",
							i2TelFrames+1, pszFilename );
					cpl_msg_info( fctid, "%s", szMessage );
					strcpy( szFilenameInWAVE[i2TelFrames], pszFilename );
					i2TelFrames++;
				}
			}
		}

		/* Proceed with next frame */
		  cpl_frameset_iterator_advance(it, 1);
		  cur_frame = cpl_frameset_iterator_get(it);
	} /* while more frames */


	/* Check, if we can perform the calibration */
	if( i3TelFrames == 4 && !iStatus )
	{
		sprintf (szMessage, "Computing new Spectral Calibration for 3 Telecopes");
		cpl_msg_info( fctid, "%s", szMessage );

		/*sprintf ( szMessage, "[%s][%s][%s][%s][%s][%s]",
		 * szFilenameBadPix, szFilenameFlat,  szFilenameInWAVE[0],
		 * szFilenameInWAVE[1], szFilenameInWAVE[2], szFilenameInWAVE[3]  );
    cpl_msg_info( fctid, "%s", szMessage ); */


		if (amdlibComputeSpectralCalibration3T(szFilenameBadPix, szFilenameFlat,
				szFilenameInWAVE[0], szFilenameInWAVE[1], szFilenameInWAVE[2],
				szFilenameInWAVE[3], spectralOffsets)
				!= amdlibSUCCESS)
		{
			iStatus = 5;
			sprintf ( szMessage, "Could not compute Spectral Calibration" );
			cpl_msg_info( fctid, "%s", szMessage );
		}
		else
		{

			cpl_msg_info(cpl_func,"Computed spectral offsets: ");
			cpl_msg_info(cpl_func,"Photometric 1 = %f, Photometric 2 = %f,"
					" Photometric 3 = %f", spectralOffsets[0],
					spectralOffsets[1], spectralOffsets[2]);

			off[0]=floor(spectralOffsets[0]+0.5);
			off[1]=floor(spectralOffsets[1]+0.5);
			off[2]=floor(spectralOffsets[2]+0.5);

			cpl_msg_info(cpl_func,"Used (rounded) spectral offsets: ");
			cpl_msg_info(cpl_func,"Photometric 1 = %f, Photometric 2 = %f,"
					" Photometric 3 = %f", off[0], off[1], off[2]);

		}
	}
	else if( i2TelFrames == 3 && !iStatus )
	{
		sprintf ( szMessage, "Computing new Spectral Calibration for 2 "
				"Telecopes (%s)...", szFilenameWave );
		cpl_msg_info( fctid, "%s", szMessage );

		if (amdlibComputeSpectralCalibration2T(szFilenameBadPix, szFilenameFlat,
				szFilenameInWAVE[0], szFilenameInWAVE[1], szFilenameInWAVE[2],
				spectralOffsets)
				!= amdlibSUCCESS)
		{
			iStatus = 5;
		}
		else
		{

			cpl_msg_info(cpl_func,"Computed spectral offsets: ");
			cpl_msg_info(cpl_func,"Photometric 1 = %f, Photometric 2 = %f,"
					" Photometric 3 = %f", spectralOffsets[0],
					spectralOffsets[1], spectralOffsets[2]);

			off[0]=floor(spectralOffsets[0]+0.5);
			off[1]=floor(spectralOffsets[1]+0.5);
			off[2]=floor(spectralOffsets[2]+0.5);

			cpl_msg_info(cpl_func,"Used (rounded) spectral offsets: ");
			cpl_msg_info(cpl_func,"Photometric 1 = %f, Photometric 2 = %f,"
					" Photometric 3 = %f", off[0], off[1], off[2]);

		}
	}
	else
	{
		sprintf ( szMessage, "No new Spectral Calibration computed "
				"(no 4 AMBER_WAVE files!). "
				"Using Calibration from DET1-Key in Header." );
		cpl_msg_info( fctid, "%s", szMessage );
	}

	/*

    Walk through the whole Set of Frames SOF and search for 3P2V or 2P2V files

	 */

	sprintf ( szMessage, "Searching for 3P2V and 2P2V Files..." );
	cpl_msg_info( fctid, "%s", szMessage );

	i3TelFrames = 0;
	i2TelFrames = 0;

	cpl_frameset_iterator_reset(it);
	cur_frame = cpl_frameset_iterator_get(it);

	while( cur_frame )
	{
		/* Get Filename and Classification Tag */
		pszFilename = (char *)cpl_frame_get_filename( cur_frame );
		pszFileTag  = (char *)cpl_frame_get_tag( cur_frame );

		/* Check Tag and Filename */
		if( pszFilename && pszFileTag )
		{
			/* 3P2V (10 needed) */
			if( !strcmp( pszFileTag, "AMBER_3P2V" ) && i3TelFrames<10 )
			{
				/* Add to Framelist */
				sprintf ( szMessage, "3P2V identified %s", pszFilename );
				cpl_msg_info( fctid, "%s", szMessage );
				strcpy( szFilenameInP2VM[i3TelFrames], pszFilename );
				i3TelFrames++;
			}

			/* Only use 2-Telescopes files when no
			 * 3-Telecopes file arrived yet */
			if( !i3TelFrames )
			{
				/* 2P2V (5 needed) */
				if( !strcmp( pszFileTag, "AMBER_2P2V" ) && i2TelFrames<5 )
				{
					/* Add to Framelist */
					sprintf ( szMessage, "2P2V identified %s", pszFilename );
					cpl_msg_info( fctid, "%s", szMessage );
					strcpy( szFilenameInP2VM[i2TelFrames], pszFilename );
					i2TelFrames++;
				}
			}
		}

		/* Proceed with next frame */
		cpl_frameset_iterator_advance(it, 1);
		cur_frame = cpl_frameset_iterator_get(it);

	} /* while more frames */
	cpl_frameset_iterator_delete(it);

	/* Test Number of Frames and call Data Reduction */
	if( strlen(szFilenameBadPix) && strlen(szFilenameFlat) )
	{
		if( i3TelFrames == 10 )
		{
			strcpy( szFilenameP2VM, "p2vm.fits" );

			/* Now make a DMD Product */
			sprintf ( szMessage, "Create file and write header..." );
			cpl_msg_info( fctid, "%s", szMessage );
			CreateProduct(fctid,szFilenameInP2VM[0],szFilenameP2VM,zeroframes);

			sprintf ( szMessage, "Now computing P2VM for 3 Telescopes..." );
			cpl_msg_info( fctid, "%s", szMessage );

			if(amdlibComputeP2vm3T_ESO(szFilenameBadPix, szFilenameFlat,
					szFilenameInP2VM[0],szFilenameInP2VM[1],szFilenameInP2VM[2],
					szFilenameInP2VM[3],szFilenameInP2VM[4],szFilenameInP2VM[5],
					szFilenameInP2VM[6],szFilenameInP2VM[7],szFilenameInP2VM[8],
					szFilenameInP2VM[9],szFilenameP2VM, off, fctid
			)
			!= amdlibSUCCESS
			)
			{
				sprintf ( szMessage, "P2VM not computed due to errors." );
				cpl_msg_info( fctid, "%s", szMessage );
				iStatus = 4;
			}
			else
			{
				sprintf ( szMessage, "P2VM produced: %s.", szFilenameP2VM );
				cpl_msg_info( fctid, "%s", szMessage );
				iStatus = 0;

				iWriteQC = 1;

				cpl_frameset_insert( framelist, pframeProduct );
			}
		}
		else if( i2TelFrames != 5 )
		{
			iStatus = 3;
			sprintf ( szMessage, "10 Frames of type AMBER_3P2V are needed." );
			cpl_msg_info( fctid, "%s", szMessage );
		}

		if( i2TelFrames == 5 )
		{
			strcpy( szFilenameP2VM, "p2vm.fits" );

			/* Now make a DMD Product */
			sprintf ( szMessage, "Create file and write header..." );
			cpl_msg_info( fctid, "%s", szMessage );
			CreateProduct(fctid,szFilenameInP2VM[0],szFilenameP2VM,zeroframes);

			sprintf ( szMessage, "Now computing P2VM for 2 Telescopes..." );
			cpl_msg_info( fctid, "%s", szMessage );
			if( amdlibComputeP2vm2T_ESO( szFilenameBadPix, szFilenameFlat,
					szFilenameInP2VM[0],szFilenameInP2VM[1],szFilenameInP2VM[2],
					szFilenameInP2VM[3],szFilenameInP2VM[4],
					szFilenameP2VM, off, fctid
			)
			!= amdlibSUCCESS
			)
			{
				sprintf ( szMessage, "P2VM not computed due to errors." );
				cpl_msg_info( fctid, "%s", szMessage );
				iStatus = 4;
			}
			else
			{
				sprintf ( szMessage, "P2VM produced: %s.", szFilenameP2VM );
				cpl_msg_info( fctid, "%s", szMessage );
				iStatus = 0;

				iWriteQC = 1;

				cpl_frameset_insert( framelist, pframeProduct );
			}

		}
		else if( i3TelFrames != 10 )
		{
			iStatus = 3;
			sprintf ( szMessage, "5 Frames of type AMBER_2P2V are needed." );
			cpl_msg_info( fctid, "%s", szMessage );
		}

	}
	else
	{
		iStatus = 2;
		sprintf (szMessage,"Support Frames needed: "
				"AMBER_BADPIX and AMBER_FLAT." );
		cpl_msg_info( fctid, "%s", szMessage );
	} /* No Flat of BadPix */

	/* Close the amdlib logfile */
	//if( fp )
	//	fclose( fp );

	prestate = cpl_errorstate_get();
	applist=cpl_propertylist_new();

	//	Write QC parameter in additional product
	if(iStatus==0){
		if (!amber_p2vm_qc(szFilenameP2VM, applist)) {
			cpl_propertylist_update_string(applist,CPL_DFS_PRO_CATG,"PVM_QC");
			cpl_dfs_save_propertylist(framelist, NULL, parlist, framelist, NULL,
					"amber_p2vm", applist, NULL, PACKAGE "/" PACKAGE_VERSION,
					"amber_p2vm_qc.fits");
		}

	}

	cpl_propertylist_delete(applist); applist=NULL;
	cpl_errorstate_set(prestate);

	if( iWriteQC )
	{
		sprintf ( szMessage, "Writing QC log file." );
		cpl_msg_info( fctid, "%s", szMessage );

		/* Reload the file to get the header */


		qcFromRawfile= cpl_propertylist_load(szFilenameInP2VM[0], 0 );



		qc = amber_qclog_open( 0 );

		if( qc )
		{
			qclog = amber_paf_get_properties( qc );

			/* Read original ARCFILE entry and copy to PAF-File */
			pszARCFILE = cpl_propertylist_get_string( qcFromRawfile, "ARCFILE");

			/* Add mandatory keys */
			cpl_propertylist_append_string( qclog, "ARCFILE", pszARCFILE );

			/* Take the whole header including QC parameters */
			qcFromProduct = cpl_propertylist_load ( szFilenameP2VM, 0);

			/* copy to PAF */
			amber_propertylist_append( qclog, qcFromProduct);

			/* Finished... */
			amber_qclog_close(qc);
		} /* if qc */


		/* Cleanup */
		cpl_propertylist_delete( qcFromProduct );
		cpl_propertylist_delete( qcFromRawfile);

	}
	else
	{
		sprintf ( szMessage, "Error: Could not write QC log file." );
		cpl_msg_info( fctid, "%s", szMessage );
		iStatus = 1;
	}

	/* Now copy P2VM also to the temp directory for pipeline usage */
	sprintf( szCommandline, "cp %s /tmp/current_P2VM.fits", szFilenameP2VM );
	system( szCommandline );

	cpl_msg_info( fctid, "End of DataReduction");

	cpl_error_reset();

	return iStatus;
}

/*----------------------------------------------------------------------------*/
/**
  @brief Derives the minimum and maximum valid wavelength in the p2vm
  @param szFilenameP2VM    Name of p2vm file.
  @param qclist            cpl_propertylist where the qc values are written
  @return   appropriate cpl_error_code
 */
/*----------------------------------------------------------------------------*/
cpl_error_code amber_p2vm_qc(const char * szFilenameP2VM,
		cpl_propertylist * qclist){
	cpl_size              p2vm_extension=0;
	cpl_table           * table=NULL;
	const cpl_array     * flag=NULL;
	const cpl_array     * eff_wave=NULL;
	cpl_size              i=0;
	cpl_propertylist    * plist=NULL;

	p2vm_extension=cpl_fits_find_extension(szFilenameP2VM, "P2VM");

	/* Load extension  */
	if((table = cpl_table_load(szFilenameP2VM, p2vm_extension, 1))==NULL)
	{
		cpl_msg_warning(cpl_func, "No suitable table found in file: %s",
				szFilenameP2VM);
		cpl_msg_warning(cpl_func, "Can not write additional QC parameter file");

		/* Propagate error, if any */
		return cpl_error_set(cpl_func, cpl_error_get_code());
	}


	if (cpl_table_has_column(table, "EFF_WAVE") &&
			cpl_table_has_column(table, "FLAG"))
	{
		//extract the arrays
		flag=cpl_table_get_array(table,"FLAG",0);
		eff_wave=cpl_table_get_array(table,"EFF_WAVE",0);

		//remove invalid elements
		for(i=0; i<cpl_array_get_size(eff_wave); i++){
			if(cpl_array_get_int(flag, i, NULL) != 1){
				cpl_array_set_invalid((cpl_array *)eff_wave, i);
				cpl_array_set_invalid((cpl_array *)flag, i);
			}
		}

		cpl_msg_debug(cpl_func," eff_wave_min: %g eff_wave_max: %g",
				cpl_array_get_min(eff_wave),
				cpl_array_get_max(eff_wave));
	}
	else
	{
		cpl_msg_warning(cpl_func, "No suitable table found in file: %s",
				szFilenameP2VM);
		cpl_table_delete(table); table=NULL;
		/* Propagate error, if any */
		return cpl_error_set(cpl_func, cpl_error_get_code());

	}

	//Read and add all the other QC parameters written by amdlib

	plist=cpl_propertylist_load(szFilenameP2VM, 0);
	cpl_propertylist_copy_property_regexp(qclist, plist, "^ESO QC", 0);
	cpl_propertylist_delete(plist);

	cpl_propertylist_update_double(qclist,  "ESO QC P2VM LAMBDA MIN",
			cpl_array_get_min(eff_wave));

	cpl_propertylist_update_double(qclist,  "ESO QC P2VM LAMBDA MAX",
			cpl_array_get_max(eff_wave));

	cpl_table_delete(table); table=NULL;
	/* Propagate error, if any */
	return cpl_error_set(cpl_func, cpl_error_get_code());
}



/*----------------------------------------------------------------------------*/
/**
  @brief    Count the number of frames with no data inside
  @param    parlist     the parameters list
  @param    framelist   the frames list
  @return   number of frames with no data inside (only values of zero)
 */
/*----------------------------------------------------------------------------*/

static long amber_check_zeroframes(cpl_parameterlist * parlist,
		cpl_frameset * framelist)
{
	const cpl_frame *   cur_frame= NULL;
	int                 nbframes= 0;
	int                 i=0, j=0 ;
	amdlibRAW_DATA      rawData_tmp= {NULL};
	long                zeroframes=0;
	long                zeroframes_per_file=0;

	/* Test entries */
	if (parlist == NULL) return -1 ;
	if (framelist == NULL) return -1 ;

	/* Initialise */
	nbframes = cpl_frameset_get_size(framelist) ;
	zeroframes=0;
	zeroframes_per_file=0;

	/* Loop on the frames and test for zeroframes */
	for (i=0 ; i<nbframes ; i++) {
		cur_frame = cpl_frameset_get_position_const(framelist, i) ;

		if (!strcmp(cpl_frame_get_tag(cur_frame), "AMBER_3P2V") ||
				!strcmp(cpl_frame_get_tag(cur_frame), "AMBER_2P2V"))
		{
			if (amdlibLoadRawData(cpl_frame_get_filename(cur_frame),
					&rawData_tmp, errMsg) != amdlibSUCCESS)
			{
				cpl_msg_warning(cpl_func,"Could not load raw data file '%s'\n",
						cpl_frame_get_filename(cur_frame));
				amdlibReleaseRawData(&rawData_tmp);
				return -1;
			}
			else{

				for (j=0 ; j<rawData_tmp.nbFrames ; j++) {
					if(rawData_tmp.timeTag[j]<FLT_EPSILON){
						zeroframes_per_file++;
					}
				}
			}
			zeroframes+=zeroframes_per_file;
			cpl_msg_indent(1);
			cpl_msg_info(cpl_func,"File %s has %ld Zeroframes",
					cpl_frame_get_filename(cur_frame), zeroframes_per_file);
			cpl_msg_indent(-1);
			zeroframes_per_file=0;

			amdlibReleaseRawData(&rawData_tmp);
		}
	}
	return zeroframes;

}
