/*
 * esolibCalibVis.c
 *
 *  Created on: Oct 26, 2009
 *      Author: agabasch
 */
#include <math.h>
#include "esolibCalibVis.h"
#include <libgen.h>
#include <string.h>
#include "amdrs.h"
#include "amber_dfs.h"
#include "amber_qc.h"
#include "config.h"
#include "esolibamdlibOi.h"


static cpl_error_code amber_CalibVis_calc_save(const char * recipename,
		cpl_frame * science_frame,  cpl_frame * trf_frame,
		cpl_parameterlist * parlist, cpl_frameset * frameset);
static cpl_error_code amber_check_consitency_trf_science(
		cpl_frame *science_frame, cpl_frame * trf_frame);


static cpl_error_code get_common_channels(amdlibWAVELENGTH  trf_wlen,
		amdlibWAVELENGTH  science_wlen, int  * trf_lmin, int * trf_lmax,
		int  * science_lmin, int * science_lmax);



cpl_error_code amber_CalibVis(const char * recipename, const char * filename,
		cpl_parameterlist *parlist, cpl_frameset * frameset)
{
	cpl_frame         * trf_frame;
	cpl_frame         * science_frame;
	cpl_propertylist  * plist=NULL;
	char              * tag_needed=NULL;
    int                 nframes=0 ;
    int                 i=0 ;

	/*cpl_frameset_dump(frameset, NULL);
exit(0);*/
	/* Extract the FILTER J,H,K */
	plist = cpl_propertylist_load(filename, 0);
	if (plist==NULL){
		cpl_msg_warning(cpl_func,"Error loading the header of %s",filename);
		/* Propagate error, if any */
		return cpl_error_set(cpl_func, cpl_error_get_code());
	}
	if (cpl_propertylist_has(plist, "ESO QC BAND") == 1){
		tag_needed=cpl_sprintf("AMBER_TRF_%s",
				cpl_propertylist_get_string(plist, "ESO QC BAND"));
		cpl_msg_info(cpl_func, "Searching for the transfer function tagged %s"
				, tag_needed);
	}
	else{
		cpl_propertylist_delete(plist);
		return cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
				"Can not determine the Band from the header! ");
	}
	cpl_propertylist_delete(plist);


	/*Find the right frame*/

	trf_frame=cpl_frameset_find(frameset,tag_needed);
	if (trf_frame == NULL) {
		cpl_free(tag_needed);
		cpl_msg_warning(cpl_func, "No Transfer function frame found in the SOF");
		/* Propagate error, if any */
		return cpl_error_set_message(cpl_func, CPL_ERROR_FILE_NOT_FOUND,
				"No transfer function file in the SOF! "
				"Visibilities can not be calibrated!");

	}
	cpl_free(tag_needed);

	/* Initialize */
    nframes = cpl_frameset_get_size(frameset) ;

	for (i=0 ; i<nframes ; i++)
	{
        science_frame = cpl_frameset_get_position(frameset, i) ;
		if(!strcmp(cpl_frame_get_filename(science_frame),filename))
		{
			break;
		}
	}
	if (science_frame == NULL) {
		cpl_msg_error(cpl_func, "No science frame found in the SOF");
		/* Propagate error, if any */
		return cpl_error_set_message(cpl_func, CPL_ERROR_FILE_NOT_FOUND,
				"No science frame in the SOF!");

	}

	if (amber_check_consitency_trf_science(science_frame, trf_frame))
	{
		cpl_msg_error(cpl_func, "Mode of science frame and transfer function "
				"frame differ!! Aborting the visibility calibration!");
		return cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
				"Transfer function is of wrong instrument mode!");
	}

	amber_CalibVis_calc_save(recipename, science_frame,  trf_frame, parlist,
			frameset);

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


}

static cpl_error_code amber_CalibVis_calc_save(const char * recipename,
		cpl_frame * science_frame,  cpl_frame * trf_frame,
		cpl_parameterlist * parlist, cpl_frameset * frameset)
{
	int iFrame=0;
	int iWave=0;
	int trf_lmin=0;
	int trf_lmax=0;
	int science_lmin=0;
	int science_lmax=0;
	int waveconsistency=0;
	int dummy_cp_phi_counter=0;
	int dummy_cp_phi_error_counter=0;
	int dummy_vis2_counter_b1=0.;
	int dummy_vis2_counter_b2=0.;
	int dummy_vis2_counter_b3=0.;
	cpl_propertylist * pHeader=NULL;
	cpl_propertylist * qclist=NULL;
	char * outname_calibrated=NULL;
	const char * outname_calibrated_paf=NULL;

	double dummy_vis2_b1=0.;
	double dummy_vis2_error_b1=0.;
	double dummy_vis2_b2=0.;
	double dummy_vis2_error_b2=0.;
	double dummy_vis2_b3=0.;
	double dummy_vis2_error_b3=0.;
	double dummy_cp_phi=0.;
	double dummy_cp_phi_error=0.;

	cpl_vector * trf_vis2_average_b1=NULL;
	cpl_vector * trf_vis2_average_error_b1=NULL;
	cpl_vector * trf_vis2_average_b2=NULL;
	cpl_vector * trf_vis2_average_error_b2=NULL;
	cpl_vector * trf_vis2_average_b3=NULL;
	cpl_vector * trf_vis2_average_error_b3=NULL;
	cpl_vector * trf_cpphi_average=NULL;
	cpl_vector * trf_cpphi_average_error=NULL;


	amdlibERROR_MSG  errMsg;
	amdlibPHOTOMETRY science_photometry = {NULL};
	amdlibWAVELENGTH science_wave = {NULL};
	amdlibPISTON     science_opd = {NULL};
	amdlibOI_TARGET  science_target = {NULL};
	amdlibOI_ARRAY   science_array = {NULL};
	amdlibVIS        science_vis = {NULL};
	amdlibVIS2       science_vis2 = {NULL};
	amdlibVIS3       science_vis3 = {NULL};
	amdlibINS_CFG    science_insCfg;
	amdlibSPECTRUM   science_spectrum={NULL};


	amdlibPHOTOMETRY trf_photometry = {NULL};
	amdlibWAVELENGTH trf_wave = {NULL};
	amdlibPISTON     trf_opd = {NULL};
	amdlibOI_TARGET  trf_target = {NULL};
	amdlibOI_ARRAY   trf_array = {NULL};
	amdlibVIS        trf_vis = {NULL};
	amdlibVIS2       trf_vis2 = {NULL};
	amdlibVIS3       trf_vis3 = {NULL};
	amdlibINS_CFG    trf_insCfg;
	amdlibSPECTRUM   trf_spectrum={NULL};

	amdlibClearInsCfg(&science_insCfg);

	if (amdlibLoadOiFile(cpl_frame_get_filename(science_frame), &science_insCfg,
			&science_array, &science_target, &science_photometry, &science_vis,
			&science_vis2, &science_vis3, &science_wave, &science_opd,
			&science_spectrum, errMsg) != amdlibSUCCESS)
	{
		amdlibLogError("Could not load OI-FITS file '%s'",
				cpl_frame_get_filename(science_frame));
		amdlibLogErrorDetail(errMsg);
		return amdlibFAILURE;
	}


	amdlibClearInsCfg(&trf_insCfg);

	if (amdlibLoadOiFile(cpl_frame_get_filename(trf_frame), &trf_insCfg,
			&trf_array, &trf_target, &trf_photometry, &trf_vis, &trf_vis2,
			&trf_vis3, &trf_wave, &trf_opd,
			&trf_spectrum, errMsg) != amdlibSUCCESS)
	{
		amdlibLogError("Could not load OI-FITS file '%s'",
				cpl_frame_get_filename(trf_frame));
		amdlibLogErrorDetail(errMsg);
		return amdlibFAILURE;
	}

	/*Check if the wavelength agree or are shifted*/
	waveconsistency=1;
	if(trf_wave.nbWlen!=science_wave.nbWlen)
	{
		waveconsistency=0;
	}

	if(trf_wave.nbWlen==science_wave.nbWlen)
	{
		int i=0;
		for(i=0; i<trf_wave.nbWlen;i++)
		{
			if(trf_wave.wlen[i]!=science_wave.wlen[i])
			{
				waveconsistency=0;
				break;
			}
		}
	}

	if(!waveconsistency){
		cpl_msg_info(cpl_func,"Trying to find the wavelength values common "
				"to calibrator and science frame ...");
		get_common_channels(trf_wave, science_wave, &trf_lmin, &trf_lmax,
				&science_lmin, &science_lmax);

		pHeader = cpl_propertylist_load(cpl_frame_get_filename(science_frame), 0 );
		cpl_image_save(NULL,"_science_waveselected.fits", CPL_BPP_16_SIGNED,
				pHeader, CPL_IO_DEFAULT );
		cpl_propertylist_delete(pHeader);

		amdlibSaveOiFile_waveselected("_science_waveselected.fits", &science_insCfg, &science_array,
				&science_target, &science_photometry, &science_vis, &science_vis2,
				&science_vis3, &science_wave, &science_opd, &science_spectrum,
				errMsg,science_lmin,science_lmax);


		pHeader = cpl_propertylist_load(cpl_frame_get_filename(trf_frame), 0 );
		cpl_image_save(NULL,"_trf_waveselected.fits", CPL_BPP_16_SIGNED,
				pHeader, CPL_IO_DEFAULT );
		cpl_propertylist_delete(pHeader);
		amdlibSaveOiFile_waveselected("_trf_waveselected.fits", &trf_insCfg, &trf_array,
				&trf_target, &trf_photometry, &trf_vis, &trf_vis2,
				&trf_vis3, &trf_wave, &trf_opd, &trf_spectrum,
				errMsg,trf_lmin,trf_lmax);
		/*Free the memory*/
		amdlibReleaseOiArray(&science_array);
		amdlibReleaseOiTarget(&science_target);
		amdlibReleasePhotometry(&science_photometry);
		amdlibReleaseVis(&science_vis);
		amdlibReleaseVis2(&science_vis2);
		amdlibReleaseVis3(&science_vis3);
		amdlibReleaseWavelength(&science_wave);
		amdlibReleaseSpectrum(&science_spectrum);
		amdlibReleasePiston(&science_opd);

		amdlibReleaseOiArray(&trf_array);
		amdlibReleaseOiTarget(&trf_target);
		amdlibReleasePhotometry(&trf_photometry);
		amdlibReleaseVis(&trf_vis);
		amdlibReleaseVis2(&trf_vis2);
		amdlibReleaseVis3(&trf_vis3);
		amdlibReleaseWavelength(&trf_wave);
		amdlibReleaseSpectrum(&trf_spectrum);
		amdlibReleasePiston(&trf_opd);


		amdlibClearInsCfg(&science_insCfg);

		if (amdlibLoadOiFile("_science_waveselected.fits", &science_insCfg,
				&science_array, &science_target, &science_photometry, &science_vis,
				&science_vis2, &science_vis3, &science_wave, &science_opd,
				&science_spectrum, errMsg) != amdlibSUCCESS)
		{
			amdlibLogError("Could not load OI-FITS file _science_waveselected.fits");
			amdlibLogErrorDetail(errMsg);
			return amdlibFAILURE;
		}


		amdlibClearInsCfg(&trf_insCfg);

		if (amdlibLoadOiFile("_trf_waveselected.fits", &trf_insCfg,
				&trf_array, &trf_target, &trf_photometry, &trf_vis, &trf_vis2,
				&trf_vis3, &trf_wave, &trf_opd,
				&trf_spectrum, errMsg) != amdlibSUCCESS)
		{
			amdlibLogError("Could not load OI-FITS file _trf_waveselected.fits");
			amdlibLogErrorDetail(errMsg);
			return amdlibFAILURE;
		}

	}
	/*Check again if the wavelengths agree*/
	waveconsistency=1;
	if(trf_wave.nbWlen!=science_wave.nbWlen)
	{
		waveconsistency=0;
	}

	if(trf_wave.nbWlen==science_wave.nbWlen)
	{
		int i=0;
		for(i=0; i<trf_wave.nbWlen;i++)
		{
			if(trf_wave.wlen[i]!=science_wave.wlen[i])
			{
				waveconsistency=0;
				break;
			}
		}
	}

	if(waveconsistency==0)
	{
		/*Free the memory*/
		amdlibReleaseOiArray(&science_array);
		amdlibReleaseOiTarget(&science_target);
		amdlibReleasePhotometry(&science_photometry);
		amdlibReleaseVis(&science_vis);
		amdlibReleaseVis2(&science_vis2);
		amdlibReleaseVis3(&science_vis3);
		amdlibReleaseWavelength(&science_wave);
		amdlibReleaseSpectrum(&science_spectrum);
		amdlibReleasePiston(&science_opd);

		amdlibReleaseOiArray(&trf_array);
		amdlibReleaseOiTarget(&trf_target);
		amdlibReleasePhotometry(&trf_photometry);
		amdlibReleaseVis(&trf_vis);
		amdlibReleaseVis2(&trf_vis2);
		amdlibReleaseVis3(&trf_vis3);
		amdlibReleaseWavelength(&trf_wave);
		amdlibReleaseSpectrum(&trf_spectrum);
		amdlibReleasePiston(&trf_opd);

		cpl_msg_error(cpl_func, "Wavelength vector of science frame and "
				"transfer function frame differ!! Aborting the visibility "
				"calibration for this file!");
		return cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
				"Transfer function is not fully compatible with science frame!");

	}
	else
	{
		cpl_msg_info(cpl_func,"Wavelength vector of calibrator and science frame "
				"are (now) identical...");
	}

	trf_vis2_average_b1=cpl_vector_new(trf_wave.nbWlen);
	trf_vis2_average_error_b1=cpl_vector_new(trf_wave.nbWlen);
	trf_vis2_average_b2=cpl_vector_new(trf_wave.nbWlen);
	trf_vis2_average_error_b2=cpl_vector_new(trf_wave.nbWlen);
	trf_vis2_average_b3=cpl_vector_new(trf_wave.nbWlen);
	trf_vis2_average_error_b3=cpl_vector_new(trf_wave.nbWlen);

	trf_cpphi_average=cpl_vector_new(trf_wave.nbWlen);
	trf_cpphi_average_error=cpl_vector_new(trf_wave.nbWlen);


	cpl_vector_fill(trf_vis2_average_b1, 0.);
	cpl_vector_fill(trf_vis2_average_b2, 0.);
	cpl_vector_fill(trf_vis2_average_b3, 0.);
	cpl_vector_fill(trf_vis2_average_error_b1, 0.);
	cpl_vector_fill(trf_vis2_average_error_b2, 0.);
	cpl_vector_fill(trf_vis2_average_error_b3, 0.);
	cpl_vector_fill(trf_cpphi_average, 0.);
	cpl_vector_fill(trf_cpphi_average_error, 0.);


	/* Averaging the Transfer function for Visibility2 and its error*/
	for(iWave = 0; iWave <trf_wave.nbWlen; iWave++){
		dummy_vis2_b1=0.;
		dummy_vis2_error_b1=0.;
		dummy_vis2_counter_b1=0.;

		dummy_vis2_b2=0.;
		dummy_vis2_error_b2=0.;
		dummy_vis2_counter_b2=0.;

		dummy_vis2_b3=0.;
		dummy_vis2_error_b3=0.;
		dummy_vis2_counter_b3=0.;

		for(iFrame = 0; iFrame < (trf_vis.nbFrames * trf_vis.nbBases); iFrame++){
			/*Vis2*/
			if(trf_vis2.table[iFrame].flag[iWave]==amdlibFALSE){
				dummy_vis2_b1+=trf_vis2.table[iFrame].vis2[iWave];
				dummy_vis2_error_b1+=pow(trf_vis2.table[iFrame].vis2Error[iWave],2.);
				dummy_vis2_counter_b1++;
			}
			if(science_vis.nbBases>1){
				iFrame++;
				if(trf_vis2.table[iFrame].flag[iWave]==amdlibFALSE){
					dummy_vis2_b2+=trf_vis2.table[iFrame].vis2[iWave];
					dummy_vis2_error_b2+=pow(trf_vis2.table[iFrame].vis2Error[iWave],2.);
					dummy_vis2_counter_b2++;
				}
				iFrame++;
				if(trf_vis2.table[iFrame].flag[iWave]==amdlibFALSE){
					dummy_vis2_b3+=trf_vis2.table[iFrame].vis2[iWave];
					dummy_vis2_error_b3+=pow(trf_vis2.table[iFrame].vis2Error[iWave],2.);
					dummy_vis2_counter_b3++;
				}
			}
		}
		if(dummy_vis2_counter_b1>0 && dummy_vis2_counter_b2>0 &&
				dummy_vis2_counter_b3>0)
		{
			dummy_vis2_b1=dummy_vis2_b1/dummy_vis2_counter_b1;
			dummy_vis2_error_b1=sqrt(dummy_vis2_error_b1)/dummy_vis2_counter_b1;
			dummy_vis2_b2=dummy_vis2_b2/dummy_vis2_counter_b2;
			dummy_vis2_error_b2=sqrt(dummy_vis2_error_b2)/dummy_vis2_counter_b2;
			dummy_vis2_b3=dummy_vis2_b3/dummy_vis2_counter_b3;
			dummy_vis2_error_b3=sqrt(dummy_vis2_error_b3)/dummy_vis2_counter_b3;

			cpl_vector_set(trf_vis2_average_b1,iWave,dummy_vis2_b1);
			cpl_vector_set(trf_vis2_average_error_b1,iWave,dummy_vis2_error_b1);
			cpl_vector_set(trf_vis2_average_b2,iWave,dummy_vis2_b2);
			cpl_vector_set(trf_vis2_average_error_b2,iWave,dummy_vis2_error_b2);
			cpl_vector_set(trf_vis2_average_b3,iWave,dummy_vis2_b3);
			cpl_vector_set(trf_vis2_average_error_b3,iWave,dummy_vis2_error_b3);
		}
	}

	/* Averaging the closure phase and its error*/
	if(trf_vis.nbBases>1){
		for(iWave = 0; iWave <trf_wave.nbWlen; iWave++){
			dummy_cp_phi=0.;
			dummy_cp_phi_error=0.;
			dummy_cp_phi_counter=0;
			dummy_cp_phi_error_counter=0;
			for(iFrame = 0; iFrame < trf_vis.nbFrames; iFrame++){

				if (trf_vis3.table[iFrame].flag[iWave]==amdlibFALSE)
				{
					if(!amber_isnan(trf_vis3.table[iFrame].vis3Phi[iWave])){
						dummy_cp_phi+=
								trf_vis3.table[iFrame].vis3Phi[iWave];
						dummy_cp_phi_counter++;
					}
					if(!amber_isnan(trf_vis3.table[iFrame].vis3PhiError[iWave])){
						dummy_cp_phi_error+=
								pow(trf_vis3.table[iFrame].vis3PhiError[iWave],2.);
						dummy_cp_phi_error_counter++;
					}
				}
			}
			/*
			dummy_cp_phi=dummy_cp_phi/
					(dummy_cp_phi_counter)*(180. / M_PI);
			dummy_cp_phi_error=sqrt(dummy_cp_phi_error)/
					(dummy_cp_phi_error_counter)*(180. / M_PI);
			 */
			if(dummy_cp_phi_counter>0)
			{

				dummy_cp_phi=dummy_cp_phi/
						(dummy_cp_phi_counter);
				dummy_cp_phi_error=sqrt(dummy_cp_phi_error)/
						(dummy_cp_phi_error_counter);

				cpl_vector_set(trf_cpphi_average,iWave,dummy_cp_phi);
				cpl_vector_set(trf_cpphi_average_error,iWave,dummy_cp_phi_error);
			}
		}
	}
	/*
	cpl_vector_dump(trf_cpphi_average, NULL);
	cpl_vector_dump(trf_cpphi_average_error, NULL);
	exit(0);
	 */

	if(dummy_cp_phi_counter<1 || dummy_vis2_counter_b1<1 ||
			dummy_vis2_counter_b2<1 || dummy_vis2_counter_b3<1)
	{

		cpl_vector_delete(trf_vis2_average_b1);
		cpl_vector_delete(trf_vis2_average_b2);
		cpl_vector_delete(trf_vis2_average_b3);
		cpl_vector_delete(trf_vis2_average_error_b1);
		cpl_vector_delete(trf_vis2_average_error_b2);
		cpl_vector_delete(trf_vis2_average_error_b3);
		cpl_vector_delete(trf_cpphi_average);
		cpl_vector_delete(trf_cpphi_average_error);

		cpl_msg_error(cpl_func, "No good visibilitiy values found!! "
				"Aborting visibility calibration for this file!");
		return cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
				"Averaged transfer function can not be computed!");

	}
	/* Calibrate Visibility2 and propagate the error*/

	cpl_msg_info(cpl_func,"Calibrating visibilities squared and propagating "
			"the errors ...");

	for(iFrame = 0; iFrame < (science_vis.nbFrames * science_vis.nbBases); iFrame++){
		for(iWave = 0; iWave <science_wave.nbWlen; iWave++){
			/*Vis2*/
			science_vis2.table[iFrame].vis2[iWave] =
					science_vis2.table[iFrame].vis2[iWave]/
					cpl_vector_get(trf_vis2_average_b1,iWave);
			/*Vis2 Error propagation*/

			science_vis2.table[iFrame].vis2Error[iWave] =
					science_vis2.table[iFrame].vis2[iWave]*
					sqrt(
							(cpl_vector_get(trf_vis2_average_error_b1, iWave) *
									cpl_vector_get(trf_vis2_average_error_b1, iWave))
									/
									(cpl_vector_get(trf_vis2_average_b1, iWave) *
											cpl_vector_get(trf_vis2_average_b1, iWave))
											+
											((science_vis2.table[iFrame].vis2Error[iWave]) *
													(science_vis2.table[iFrame].vis2Error[iWave]))
													/
													((science_vis2.table[iFrame].vis2[iWave]) *
															(science_vis2.table[iFrame].vis2[iWave]))
					);
		}
		if(science_vis.nbBases>1){
			iFrame++;
			for(iWave = 0; iWave <science_wave.nbWlen; iWave++){
				/*Vis2*/
				science_vis2.table[iFrame].vis2[iWave] =
						science_vis2.table[iFrame].vis2[iWave]/
						cpl_vector_get(trf_vis2_average_b2,iWave);
				/*Vis2 Error propagation*/
				science_vis2.table[iFrame].vis2Error[iWave] =
						science_vis2.table[iFrame].vis2[iWave]*
						sqrt(
								(cpl_vector_get(trf_vis2_average_error_b2, iWave) *
										cpl_vector_get(trf_vis2_average_error_b2, iWave))
										/
										(cpl_vector_get(trf_vis2_average_b2, iWave) *
												cpl_vector_get(trf_vis2_average_b2, iWave))
												+
												((science_vis2.table[iFrame].vis2Error[iWave]) *
														(science_vis2.table[iFrame].vis2Error[iWave]))
														/
														((science_vis2.table[iFrame].vis2[iWave]) *
																(science_vis2.table[iFrame].vis2[iWave]))
						);
			}
			iFrame++;
			for(iWave = 0; iWave <science_wave.nbWlen; iWave++){
				/*Vis2*/
				science_vis2.table[iFrame].vis2[iWave] =
						science_vis2.table[iFrame].vis2[iWave]/
						cpl_vector_get(trf_vis2_average_b3,iWave);
				/*Vis2 Error propagation*/
				science_vis2.table[iFrame].vis2Error[iWave] =
						science_vis2.table[iFrame].vis2[iWave]*
						sqrt(
								(cpl_vector_get(trf_vis2_average_error_b3, iWave) *
										cpl_vector_get(trf_vis2_average_error_b3, iWave))
										/
										(cpl_vector_get(trf_vis2_average_b3, iWave) *
												cpl_vector_get(trf_vis2_average_b3, iWave))
												+
												((science_vis2.table[iFrame].vis2Error[iWave]) *
														(science_vis2.table[iFrame].vis2Error[iWave]))
														/
														((science_vis2.table[iFrame].vis2[iWave]) *
																(science_vis2.table[iFrame].vis2[iWave]))
						);
			}
		}
	}

	/* Calibrating the closure phase and propagate the error*/
	if(science_vis.nbBases>1){
		cpl_msg_info(cpl_func,"Calibrating closure phase and propagate "
				"the errors ...");
		/*closure phase*/
		for(iFrame = 0; iFrame < science_vis.nbFrames; iFrame++){
			for(iWave = 0; iWave <science_wave.nbWlen; iWave++){

				science_vis3.table[iFrame].vis3Phi[iWave]=
						science_vis3.table[iFrame].vis3Phi[iWave]-
						cpl_vector_get(trf_cpphi_average, iWave);
				/*closure phase error*/
				science_vis3.table[iFrame].vis3PhiError[iWave]=sqrt(
						science_vis3.table[iFrame].vis3PhiError[iWave]*
						science_vis3.table[iFrame].vis3PhiError[iWave] +
						cpl_vector_get(trf_cpphi_average_error, iWave)*
						cpl_vector_get(trf_cpphi_average_error, iWave));

			}
		}


	}


	pHeader = cpl_propertylist_load(cpl_frame_get_filename(science_frame), 0 );
	qclist=cpl_propertylist_new();
	cpl_propertylist_update_string(qclist, CPL_DFS_PRO_CATG,
			"SCIENCE_CALIBRATED");
	cpl_propertylist_update_string(qclist, CPL_DFS_PRO_TECH,
			"INTERFEROMETRY");
	cpl_propertylist_update_bool(qclist, CPL_DFS_PRO_SCIENCE,
			TRUE);


	cpl_propertylist_copy_property_regexp(qclist, pHeader, "^ESO QC", 0);
	amber_qc(&science_wave, &science_vis, &science_vis2, &science_vis3, NULL,
			qclist, "cal");


	outname_calibrated = cpl_sprintf("cal_%s",
			basename((char *)cpl_frame_get_filename(science_frame)));
	cpl_msg_info(cpl_func,"Calibrated Frame :"
			" %s",outname_calibrated);


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


	cpl_dfs_save_propertylist(frameset,
			pHeader,
			parlist,
			frameset,
			science_frame,
			recipename,
			qclist,
			NULL,
			PACKAGE "/" PACKAGE_VERSION,
			outname_calibrated);

	amdlibSaveOiFile(outname_calibrated, &science_insCfg, &science_array,
			&science_target, &science_photometry, &science_vis, &science_vis2,
			&science_vis3, &science_wave, &science_opd, &science_spectrum,
			errMsg);

	/*
	amdlibSaveOiFile_waveselected(outname_calibrated, &science_insCfg, &science_array,
			&science_target, &science_photometry, &science_vis, &science_vis2,
			&science_vis3, &science_wave, &science_opd, &science_spectrum,
			errMsg,2,3);
	 */

	/*... In order to create a complete paf file :-( */
	amber_qc(&science_wave, &science_vis, &science_vis2, &science_vis3, NULL,
			pHeader, "cal");

	outname_calibrated_paf=cpl_sprintf("qc_cal_%s.paf",
			basename(cpl_frame_get_filename(science_frame)));

	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_calibrated_paf);
		cpl_propertylist_erase(pHeader,"ARCFILE");
	}
	cpl_free((void *)outname_calibrated_paf);





	/*Free the memory*/
	amdlibReleaseOiArray(&science_array);
	amdlibReleaseOiTarget(&science_target);
	amdlibReleasePhotometry(&science_photometry);
	amdlibReleaseVis(&science_vis);
	amdlibReleaseVis2(&science_vis2);
	amdlibReleaseVis3(&science_vis3);
	amdlibReleaseWavelength(&science_wave);
	amdlibReleaseSpectrum(&science_spectrum);
	amdlibReleasePiston(&science_opd);

	amdlibReleaseOiArray(&trf_array);
	amdlibReleaseOiTarget(&trf_target);
	amdlibReleasePhotometry(&trf_photometry);
	amdlibReleaseVis(&trf_vis);
	amdlibReleaseVis2(&trf_vis2);
	amdlibReleaseVis3(&trf_vis3);
	amdlibReleaseWavelength(&trf_wave);
	amdlibReleaseSpectrum(&trf_spectrum);
	amdlibReleasePiston(&trf_opd);

	cpl_vector_delete(trf_vis2_average_b1);
	cpl_vector_delete(trf_vis2_average_error_b1);
	cpl_vector_delete(trf_vis2_average_b2);
	cpl_vector_delete(trf_vis2_average_error_b2);
	cpl_vector_delete(trf_vis2_average_b3);
	cpl_vector_delete(trf_vis2_average_error_b3);
	cpl_vector_delete(trf_cpphi_average);
	cpl_vector_delete(trf_cpphi_average_error);

	cpl_free(outname_calibrated);
	cpl_propertylist_delete(pHeader);
	cpl_propertylist_delete(qclist);
	/* Propagate error, if any */
	return cpl_error_set(cpl_func, cpl_error_get_code());

}
static cpl_error_code amber_check_consitency_trf_science(
		cpl_frame *science_frame, cpl_frame * trf_frame)
{
	cpl_propertylist * trf_header=NULL;
	cpl_propertylist * science_header=NULL;

	trf_header = cpl_propertylist_load(cpl_frame_get_filename(trf_frame), 0);
	science_header = cpl_propertylist_load(
			cpl_frame_get_filename(science_frame), 0);

	if(trf_header==NULL || science_header==NULL)

	{
		cpl_msg_error(cpl_func,"Transfer function file or science "
				"file missing!");
		cpl_error_set_message(cpl_func, CPL_ERROR_FILE_NOT_FOUND,
				"Transfer function file or science file missing!");
		return cpl_error_set(cpl_func, cpl_error_get_code());
	}

	if(strcmp(cpl_propertylist_get_string(science_header, "ESO INS MODE"),
			cpl_propertylist_get_string(trf_header, "ESO INS MODE")))
	{
		cpl_propertylist_delete(science_header);
		cpl_propertylist_delete(trf_header);

		cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
				"Transfer function is of wrong instrument mode!");
		return cpl_error_set(cpl_func, cpl_error_get_code());
	}
	cpl_propertylist_delete(science_header);
	cpl_propertylist_delete(trf_header);

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




}
static cpl_error_code get_common_channels(amdlibWAVELENGTH  trf_wlen,
		amdlibWAVELENGTH  science_wlen, int  * trf_lmin, int * trf_lmax,
		int  * science_lmin, int * science_lmax)
{

	int science_nbWlen=0;
	int trf_nbWlen=0;
	int i=0;
	int trf_min_index=INT_MAX;
	int science_min_index=INT_MAX;
	int trf_max_index=0.;
	int science_max_index=0.;

	double use_max_value=0.;
	double use_min_value=FLT_MAX;

	science_nbWlen=science_wlen.nbWlen;
	trf_nbWlen=trf_wlen.nbWlen;

	if(trf_nbWlen>1 && trf_wlen.wlen[0]<trf_wlen.wlen[1]){
		/*ascending vector*/
		use_min_value =	(trf_wlen.wlen[0] >= science_wlen.wlen[0]) ?
				trf_wlen.wlen[0] : science_wlen.wlen[0];

		use_max_value =
				(trf_wlen.wlen[trf_nbWlen-1] >= science_wlen.wlen[science_nbWlen-1])
				? science_wlen.wlen[science_nbWlen-1] : trf_wlen.wlen[trf_nbWlen-1];
	}
	else{
		/*descending vector*/
		use_min_value =	(trf_wlen.wlen[0] <= science_wlen.wlen[0]) ?
				trf_wlen.wlen[0] : science_wlen.wlen[0];

		use_max_value =
				(trf_wlen.wlen[trf_nbWlen-1] <= science_wlen.wlen[science_nbWlen-1])
				? science_wlen.wlen[science_nbWlen-1] : trf_wlen.wlen[trf_nbWlen-1];
	}


	cpl_msg_info(cpl_func,"use_min:%g use_max: %g",use_min_value,use_max_value);



	if(trf_nbWlen>1 && trf_wlen.wlen[0]<trf_wlen.wlen[1]){
		/*ascending vector*/
		cpl_msg_info(cpl_func,"ascending wave-vector found");
		for (i=0; i<trf_nbWlen; i++)
		{
			if(trf_wlen.wlen[i]<=use_min_value)
			{
				trf_min_index=i;
			}
			if(trf_wlen.wlen[i]<=use_max_value)
			{
				trf_max_index=i;
			}
		}

		for (i=0; i<science_nbWlen; i++)
		{
			/*cpl_msg_info(cpl_func,"ARMIN1:%g",science_wlen.wlen[i]);*/
			if(science_wlen.wlen[i]<=use_min_value)
			{
				science_min_index=i;
			}
			if(science_wlen.wlen[i]<=use_max_value)
			{
				science_max_index=i;
			}
		}
	}
	else
	{
		/*descending vector*/
		cpl_msg_info(cpl_func,"descending wave-vector found" );
		for (i=0; i<trf_nbWlen; i++)
		{
			if(trf_wlen.wlen[i]>=use_min_value)
			{
				trf_min_index=i;
			}
			if(trf_wlen.wlen[i]>=use_max_value)
			{
				trf_max_index=i;
			}
		}
		for (i=0; i<science_nbWlen; i++)
		{
			/*cpl_msg_info(cpl_func,"ARMIN1:%g",science_wlen.wlen[i]);*/
			if(science_wlen.wlen[i]>=use_min_value)
			{
				science_min_index=i;
			}
			if(science_wlen.wlen[i]>=use_max_value)
			{
				science_max_index=i;
			}
		}
	}

	cpl_msg_info(cpl_func,"trf_min_index:%d trf_max_index: %d",
			trf_min_index,trf_max_index);
	cpl_msg_info(cpl_func,"science_min_index:%d science_max_index: %d",
			science_min_index,science_max_index);

	cpl_msg_info(cpl_func,"trf_min_value:%.6g trf_max_value: %.6g",
			trf_wlen.wlen[trf_min_index],
			trf_wlen.wlen[trf_max_index]);
	cpl_msg_info(cpl_func,"science_min_value:%.6g science_max_value: %.6g",
			science_wlen.wlen[science_min_index],
			science_wlen.wlen[science_max_index]);


	*science_lmin =  science_min_index;
	*trf_lmin     =  trf_min_index;


	*science_lmax = (science_nbWlen-science_max_index-1);
	*trf_lmax     = (trf_nbWlen-trf_max_index-1);

	cpl_msg_info(cpl_func,"science_lmin: %d science_lmax: %d",
			*science_lmin, *science_lmax);
	cpl_msg_info(cpl_func,"trf_lmin: %d trf_lmax: %d",
			*trf_lmin, *trf_lmax);



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

}
/*----------------------------------------------------------------------------*/
/**
   @brief   portable isnan
 */
/*----------------------------------------------------------------------------*/
int amber_isnan(double value)
{
#if defined HAVE_ISNAN && HAVE_ISNAN
	return isnan(value);
#else
	return value != value;
#endif
}


