/*******************************************************************************
 * JMMC project ( http://www.jmmc.fr ) - Copyright (C) CNRS.
 ******************************************************************************/

#ifndef amdlibProtected_H
#define amdlibProtected_H
/**
 * @file
 * This file contains protected functions and structures of the AMBER data
 * reduction software; ie which can be only used inside library itself.
 */ 

/* The following piece of code alternates the linkage type to C for all
 * functions declared within the braces, which is necessary to use the
 * functions in C++-code.
 */
#ifdef __cplusplus
extern "C" { 
#endif

/* Include the generated file which  define amdlibVERSION using svn revision or AMDLIB_VERSION env var :
   #define amdlibVERSION "SVN_YYYYMMDD_$SVNVERSIONCODE" 
*/
#include "amdlibVersion.h"

/** System functions which are not defined on all platform */
long int lround(double x);
#ifndef isnan
int isnan (double value);
#endif
int getopt (int argc, char * const argv[],
            const char * optstring);

/** Macro to compute square; more efficient than pow() function */
#define amdlibPow2(a) ((a)*(a))

/** Constant for program debug */
#if defined(DEBUG)
#define amdlibDEBUG 1
#endif

/** Macro to trace global information */
#define amdlibInfo(format, arg...) printf(format, ##arg)

/** Useful max macro */
#define amdlibMax(A,B) ((A)>(B)?(A):(B))
/** Useful min macro */
#define amdlibMin(A,B) ((A)<(B)?(A):(B))

/** Macro for conditional memory freeing */
#define amdlibFree(ptr)  \
    if (ptr != NULL)     \
    {                    \
        free(ptr);       \
        ptr=NULL;        \
    }
/** Macro to Divide two complex values */
#define amdlibCpxDiv(p,q,r) \
    r.re = p.re * q.re / (q.re*q.re + q.im*q.im) + \
           p.im * q.im / (q.re*q.re + q.im*q.im);  \
    r.im = p.im * q.re / (q.re*q.re + q.im*q.im) - \
           p.re * q.im / (q.re*q.re + q.im*q.im);
/** Macro to Multiply two complex values */
#define amdlibCpxMul(p,q,r) r.re = p.re*q.re - p.im*q.im; \
                            r.im = p.re*q.im + p.im*q.re;
                                     
/** Values used for bad pixels in bad pixel map */
#define amdlibBAD_PIXEL_FLAG  0
/** Values used for good pixels in bad pixel map */
#define amdlibGOOD_PIXEL_FLAG 1
/** When a pixel is marked as bad, its value is set to the following
 * value when generating calibrated images.
 * Using a NAN will show the influence of any bad pixel in the process */
/* #define amdlibBAD_PIXEL_VALUE sqrt(-1.0) */
#define amdlibBAD_PIXEL_VALUE 0.0

/** Threshold for detector saturation detection */
#define amdlibDETECTOR_SATURATION 8000.0

/** conversion nanometers to meters */
#define amdlibNM_TO_M   1e-9 

/** Blanking value used in amdlib at various places
 ** WARNING: MUST BE A HUGE NEGATIVE VALUE to permit use in
 ** weighted means as in w = 1.0 / amdlibPow2(amdlibBLANKING_VALUE) */
#define amdlibBLANKING_VALUE -1.0e10

/** Precision for floating point comparison */
#define amdlibPRECISION 1.0e-8

#ifndef M_PI
/** pi */
#define M_PI 3.14159265358979323846
#endif
/** 2 pi */
#ifndef M_2PI
#define M_2PI           2*M_PI 
#endif

/** Usefull macro to handle error when reading/writing IO-FITS file */ 
#define amdlibGetFitsError(msg)                                    \
    fits_get_errstatus(status, (char*)fitsioMsg);                  \
    sprintf(errMsg, "%s: %s - %s", __FILE_LINE__, msg, fitsioMsg); \
    status = 0;
#define amdlibReturnFitsError(msg)                                 \
    fits_get_errstatus(status, (char*)fitsioMsg);                  \
    sprintf(errMsg, "%s: %s - %s", __FILE_LINE__, msg, fitsioMsg); \
    status = 0;                                                    \
    return amdlibFAILURE;

/** Usefull macro to set error message including file name and line number */
#define amdlibSetErrMsg(format, arg...)                              \
    sprintf(errMsg, "%s: " format, __FILE_LINE__, ##arg);

/**
 * Logging level
 */
typedef enum
{
    amdlibLOG_ERROR = -1, /**< Error messages */
    amdlibLOG_QUIET,      /**< No echo */
    amdlibLOG_WARNING,    /**< Abnormal events for application */
    amdlibLOG_INFO,       /**< Major events (e.g when command is received).*/
    amdlibLOG_TEST,       /**< Software test activities */
    amdlibLOG_TRACE       /**< Use to trace subroutine calls */
} amdlibLOG_LEVEL;

/**
 * Configuration parameters for logging service
 */
typedef struct 
{
    amdlibBOOLEAN   enabled;       /**< Flag indicating whether log is enabled
                                     or not */
    amdlibLOG_LEVEL level;         /**< Lowest level of logged message */
    amdlibBOOLEAN   printDate;     /**< Flag indicating whether date is
                                     printed out or not */
    amdlibBOOLEAN   printFileLine; /**< Flag indicating whether file/line
                                     information is printed out or not */
} amdlibLOG_CONFIG;

/* Time stamp */
void amdlibGetTimeStamp(char timeStamp[32], unsigned int precision);

/* Log handling */
void amdlibLogSet(amdlibBOOLEAN enabled, amdlibLOG_LEVEL level,
                  amdlibBOOLEAN printDateFileLine);
amdlibBOOLEAN amdlibLogIsEnabled(void);
amdlibLOG_LEVEL amdlibLogGetLevel(void);
void amdlibLogPrint(amdlibLOG_LEVEL level, amdlibBOOLEAN isDetail, const char *fileLine,
                    const char *format, ...);

/**
 * Macro to get file name and line number
 */
#ifndef DOXYGEN /* Code fragment ignored by Doxygen */
#ifndef __FILE_LINE__
#define amdlibIToStr(a) #a
#define amdlibIToStr2(a) amdlibIToStr(a)
#define __FILE_LINE__ __FILE__ ":" amdlibIToStr2(__LINE__)
#endif /*!__FILE_LINE__*/
#endif /*!DOXYGEN*/

/**
 * The following convenient macros are provided for logging information. They
 * automaticaly add log level and file/line information parameters.
 *
 * Log errors. The logging level is set to amdlibLOG_ERROR.
 *
 * @sa amdlibLogPrint
 */
#define amdlibLogError(format, arg...) \
        amdlibLogPrint(amdlibLOG_ERROR, amdlibFALSE, __FILE_LINE__, format, ##arg)
#define amdlibLogErrorDetail(format, arg...) \
        amdlibLogPrint(amdlibLOG_ERROR, amdlibTRUE, __FILE_LINE__, format, ##arg)
/**
 * Log information about abnormal events for application. The logging level is
 * set to amdlibLOG_WARNING.
 *
 * @sa amdlibLogPrint
 */
#define amdlibLogWarning(format, arg...) \
        amdlibLogPrint(amdlibLOG_WARNING, amdlibFALSE, __FILE_LINE__, format, ##arg)
#define amdlibLogWarningDetail(format, arg...) \
        amdlibLogPrint(amdlibLOG_WARNING, amdlibTRUE, __FILE_LINE__, format, ##arg)
/**
 * Log information about major events. For example, when command is received.
 * The logging level is fixed to amdlibLOG_INFO.
 *
 * @sa amdlibLogPrint
 */
#define amdlibLogInfo(format, arg...) \
        amdlibLogPrint(amdlibLOG_INFO, amdlibFALSE, __FILE_LINE__, format, ##arg)
#define amdlibLogInfoDetail(format, arg...) \
        amdlibLogPrint(amdlibLOG_INFO, amdlibTRUE, __FILE_LINE__, format, ##arg)
/**
 * Log relevant information used for the software test activities. The
 * logging level is fixed to amdlibLOG_TEST.
 *
 * @sa amdlibLogPrint
 */
#define amdlibLogTest(format, arg...) \
        amdlibLogPrint(amdlibLOG_TEST, amdlibFALSE, __FILE_LINE__, format, ##arg)
#define amdlibLogTestDetail(format, arg...) \
        amdlibLogPrint(amdlibLOG_TEST, amdlibTRUE, __FILE_LINE__, format, ##arg)
/**
 * Log subroutine calls. The logging level is fixed to amdlibLOG_TRACE.
 *
 * @sa amdlibLogPrint
 */
#define amdlibLogTrace(format, arg...) \
        amdlibLogPrint(amdlibLOG_TRACE, amdlibFALSE, __FILE_LINE__, format, ##arg)

/* Error handling */
#define amdlibERR_ALLOC_MEM "Could not allocate memory for"
#define amdlibERR_NB_PARAM "Invalid number of parameters for"

/**
 * Values not passed by normal selection Mechanism. Change in amdlibMisc
 * if number of arguments in below list is > 24. 
 * Also, reflect change in Yorick/amdlibWrapper.i
 */
typedef enum
{
    amdlibBEAUTIFY_PISTON,
    amdlibCHISQUARE_LIMIT,
    amdlibMAX_PISTON_ERROR,
    amdlibSHIFT_WLENTABLE,
    amdlibMIN_PHOTOMETRY,
    amdlibCORRECT_OPD0,
    amdlibLINEARIZE_P2VM_PHASE,
    amdlibNORMALIZE_P2VM,
    amdlibNO_FUDGE,
    amdlibNO_BIAS,
    amdlibDROP,
    amdlibNORMALIZE_SPECTRUM,
    amdlibBOXCARSMOOTH_P2VM_PHASE,
    amdlibGAUSSSMOOTH_P2VM_PHASE,
    amdlibAUTO_BADPIXEL,
    amdlibMAX_PISTON_EXCURSION,
    amdlibGLOBAL_PHOTOMETRY,
    amdlibUSE_GAIN,
    amdlibZAP_JHK_DISCONTINUTIES,
    amdlibAUTO_SHIFT_JHK
} amdlibUSER_PREFERENCES;

/**
 * The corresponding table of values
 */

typedef struct 
{
    amdlibBOOLEAN   set; /* yes : was wet and is thus not default */
    amdlibDOUBLE  value;
} amdlibUSERPREF_VALUES;
/* and the functions associated */
amdlibUSERPREF_VALUES amdlibGetUserPref(int code);
void amdlibSetUserPref(int code, amdlibDOUBLE value);
void amdlibUnsetUserPref(int code);

/* Miscellaneous useful functions */
const char * amdlibGetProgramName(char * argv0);

/* Function prototypes */
unsigned char **amdlibAlloc2DArrayUnsignedChar(const int       firstDim, 
                                               const int       secondDim,
                                               amdlibERROR_MSG errMsg);
double **amdlibAlloc2DArrayDouble(const int       firstDim, 
                                  const int       secondDim,
                                  amdlibERROR_MSG errMsg);
double ***amdlibAlloc3DArrayDouble(const int       firstDim,
                                   const int       secondDim,
                                   const int       thirdDim,
                                   amdlibERROR_MSG errMsg);
float **amdlibAlloc2DArrayFloat(const int       firstDim, 
                                const int       secondDim,
                                amdlibERROR_MSG errMsg);
float ***amdlibAlloc3DArrayFloat(const int       firstDim,
                                 const int       secondDim,
                                 const int       thirdDim,
                                 amdlibERROR_MSG errMsg);
amdlibCOMPLEX **amdlibAlloc2DArrayComplex(const int       firstDim, 
                                          const int       secondDim,
                                          amdlibERROR_MSG errMsg);
amdlibCOMPLEX ***amdlibAlloc3DArrayComplex(const int       firstDim, 
                                           const int       secondDim,
                                           const int       thirdDim,
                                           amdlibERROR_MSG errMsg);
void **amdlibWrap2DArray(void *          array,
                         const int       firstDim, 
                         const int       secondDim,
                         const int       elemSize, 
                         amdlibERROR_MSG errMsg);
void ***amdlibWrap3DArray(void *          array,
                          const int       firstDim,
                          const int       secondDim,
                          const int       thirdDim,
                          const int       elemSize, 
                          amdlibERROR_MSG errMsg);
void ****amdlibWrap4DArray(void *          array,
                          const int       firstDim,
                          const int       secondDim,
                          const int       thirdDim,
                          const int       fourthDim,
                          const int       elemSize, 
                          amdlibERROR_MSG errMsg);
float **amdlibWrap2DArrayFloat(float *         initialArray,
                               const int       firstDim, 
                               const int       secondDim,
                               amdlibERROR_MSG errMsg);
float ***amdlibWrap3DArrayFloat(float *         initialArray,
                                const int       firstDim,
                                const int       secondDim,
                                const int       thirdDim,
                                amdlibERROR_MSG errMsg);
double **amdlibWrap2DArrayDouble(double *        array,
                                 const int       firstDim, 
                                 const int       secondDim,
                                 amdlibERROR_MSG errMsg);
double ***amdlibWrap3DArrayDouble(double *        initialArray,
                                  const int       firstDim,
                                  const int       secondDim,
                                  const int       thirdDim,
                                  amdlibERROR_MSG errMsg);
double ****amdlibWrap4DArrayDouble(double *        initialArray,
                                  const int       firstDim,
                                  const int       secondDim,
                                  const int       thirdDim,
                                  const int       fourthDim,
                                  amdlibERROR_MSG errMsg);
unsigned char **amdlibWrap2DArrayUnsignedChar(unsigned char * initialArray,
                                              const int       firstDim, 
                                              const int       secondDim,
                                              amdlibERROR_MSG errMsg);
void amdlibFree2DArrayUnsignedChar(unsigned char **arrayToFree);
void amdlibFree2DArrayDouble(double **arrayToFree);
void amdlibFree3DArrayDouble(double ***arrayToFree); 
void amdlibFree2DArrayFloat(float **arrayToFree);
void amdlibFree3DArrayFloat(float ***arrayToFree); 
void amdlibFree2DArrayComplex(amdlibCOMPLEX **arrayToFree);
void amdlibFree3DArrayComplex(amdlibCOMPLEX ***arrayToFree);
void amdlibFree2DArrayWrapping(void **wrappingToFree);
void amdlibFree3DArrayWrapping(void ***wrappingToFree);
void amdlibFree2DArrayFloatWrapping(float **wrappingToFree);
void amdlibFree3DArrayFloatWrapping(float ***wrappingToFree);
void amdlibFree2DArrayDoubleWrapping(double **wrappingToFree);
void amdlibFree3DArrayDoubleWrapping(double ***wrappingToFree);
void amdlibFree4DArrayDoubleWrapping(double ****wrappingToFree);
void amdlibFree2DArrayUnsignedCharWrapping(unsigned char **wrappingToFree);

/* Useful functions */
void amdlibStripBlanks(char *str);
void amdlibStripQuotes(char *str);
char *amdlibMJD2ISODate(double mjd);
int amdlibCompareDouble(double x, double y);

/* Instrument configuration keywords */
amdlibCOMPL_STAT amdlibAddInsCfgKeyword(amdlibINS_CFG   *insCfg, 
                                        amdlibKEYW_LINE keywLine,
                                        amdlibERROR_MSG errMsg);
void amdlibRemoveInsCfgKeyword(amdlibINS_CFG   *insCfg, 
                               amdlibKEYW_LINE keyword);
amdlibCOMPL_STAT amdlibSetInsCfgKeyword(amdlibINS_CFG   *insCfg, 
                                        char            *keywName,
                                        char            *keywVal,
                                        char            *keywCmt,
                                        amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibGetInsCfgKeyword(amdlibINS_CFG   *insCfg,
                                        char            *keywName,
                                        char    keywValue[amdlibKEYW_VAL_LEN+1],
                                        amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibClearInsCfg(amdlibINS_CFG   *insCfg);

/* Raw data */
void amdlibInitRawData(amdlibRAW_DATA *rawData);
amdlibCOMPL_STAT amdlibDuplicateRawData(amdlibRAW_DATA  *dstRawData,
                                        amdlibRAW_DATA  *srcRawData,
                                        amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibReadWaveData(fitsfile             *filePtr, 
                                    amdlibWAVEDATA       *waveData, 
                                    amdlibERROR_MSG      errMsg);

amdlibCOMPL_STAT amdlibReadImagingDetectorHdr
                                    (fitsfile               *filePtr, 
                                     amdlibIMAGING_DETECTOR *imagingDetector, 
                                     amdlibERROR_MSG        errMsg);

amdlibCOMPL_STAT amdlibReadImagingDataHdr
                                    (fitsfile           *filePtr, 
                                     amdlibIMAGING_DATA *imagingData, 
                                     amdlibERROR_MSG    errMsg);
amdlibCOMPL_STAT amdlibReadArrayGeometry (fitsfile        *filePtr, 
                                          amdlibOI_ARRAY  *arrayGeometry, 
                                          amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibAllocateRegions(amdlibREGION **regions,
                                       int          nbRegions);
void amdlibFreeRegions(amdlibREGION **regions, int  nbRegions );

amdlibCOMPL_STAT amdlibReadRegionInfo (fitsfile        *filePtr, 
                                       amdlibREGION    **regions, 
                                       int             nbRegions,
                                       int             nbTel,
                                       amdlibERROR_MSG errMsg);

amdlibCOMPL_STAT amdlibReadRegionData (fitsfile        *filePtr, 
                                      amdlibREGION    *regions, 
                                      int             nbRegions,
                                      int             firstFrame,
                                      int             nbFrames,
                                      amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibWriteRegionData (fitsfile        *filePtr, 
                                        amdlibREGION    *regions, 
                                        int             nbRegions,
                                        amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibStoreRawData(const char      *filename,
                                    amdlibRAW_DATA  *rawData,
                                    amdlibERROR_MSG errMsg);

amdlibCOMPL_STAT amdlibCopyRawDataFile(const char      *srcFilename,
                                       const char      *dstFilename,
                                       amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibSaveRawDataToFits(const char      *filename,
                                         amdlibRAW_DATA  *rawData,
                                         amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibSumAndPackData(amdlibRAW_DATA  *rawData,
                                      amdlibBOOLEAN   sumX,
                                      amdlibBOOLEAN   sumY,
                                      amdlibBOOLEAN   sumZ,
                                      int             channel,
                                      amdlibDOUBLE           **result,
                                      amdlibDOUBLE           **sigma2Result,
                                      amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibComputeBandwidth(amdlibWAVEDATA *waveData);

/* Bad pixels */
amdlibBAD_PIXEL_MAP *amdlibGetBadPixelMap(void);
amdlibDOUBLE **amdlibGetBadPixelMapRegion(int             startPixelX, 
                                   int             startPixelY, 
                                   int             nbPixelX, 
                                   int             nbPixelY,
                                   amdlibERROR_MSG errMsg);

amdlibCOMPL_STAT amdlibUpdateBadPixelMap(int             startPixelX, 
                                          int             startPixelY, 
                                          int             nbPixelX, 
                                          int             nbPixelY,
                                          amdlibDOUBLE    **mask,    
                                         amdlibERROR_MSG errMsg);
/* Flat field */
amdlibFLAT_FIELD_MAP *amdlibGetFlatFieldMap(void);
amdlibDOUBLE **amdlibGetFlatFieldMapRegion(int             startPixelX, 
                                    int             startPixelY, 
                                    int             nbPixelX, 
                                    int             nbPixelY,
                                    amdlibERROR_MSG errMsg);


/* Science data */
void amdlibFreeScienceData(amdlibSCIENCE_DATA *scienceData);

/* Spectral calibration */
amdlibCOMPL_STAT amdlibShift(/* INPUT */
                                   int     nbPix,
                                   amdlibDOUBLE *tab_in,
                                   double  shift,
                                   /* OUTPUT */
                                   amdlibDOUBLE *tab_out,
                                   amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibComputeShift(/* INPUT */
                                   int     nbPix,
                                   amdlibDOUBLE *tab1,
                                   amdlibDOUBLE *tab2,
                                   /* OUTPUT */
                                   double *shift,
                                   double *sigma2_shift,
                                   amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibGetRefLowJHKSpectrumForCal
                                (int bandState[amdlibNB_BANDS], 
                                amdlibDOUBLE refSpec[amdlibNB_SPECTRAL_CHANNELS]);
amdlibCOMPL_STAT amdlibGetRefLowJHKSpectralDispersion
                            (amdlibDOUBLE specTable[amdlibNB_SPECTRAL_CHANNELS]);

/* Matrix utilities */
amdlibDOUBLE amdlibMedian9(amdlibDOUBLE *data);
double amdlibQuickSelectDble(double* arr, int n);
float amdlibQuickSelectSngl(float* arr, int n);

amdlibCOMPL_STAT amdlibInvertMatrix(double *matrix, 
                                    int dim);
void amdlibTransposeMatrix(double *matrix, 
                           double *tmatrix, 
                           int nx, 
                           int ny);
amdlibCOMPL_STAT amdlibProductMatrix(double *matrix1, 
                                     double *matrix2, 
                                     double *matprod, 
                                     int dim1, 
                                     int dim2, 
                                     int dim3);
void amdlibComputeMatrixCov(double *x, 
                      double *y, 
                      int lambda, 
                      int nbWlen, 
                      int nbFrames, 
                      int nbBase,
                      double *cov_xy);
double amdlibComputeCov(double *x, 
                      double *y, 
                      int nbFrames);
double amdlibAvgTable(int    nbPix,
                      double *table,
                      double *sigma2);
double amdlibRmsTable(int    nbPix,
                      double *table,
                      double *sigma2);
double amdlibAvgValues(int    nbPix,
                      double *table);
double amdlibRmsValues(int    nbPix,
                      double *table);
double amdlibSignedSqrt(double a);
amdlibCOMPL_STAT amdlibCorrect3DVisTableFromAchromaticPiston(
                                            amdlibCOMPLEX ***cpxVisTable, 
                                            amdlibCOMPLEX ***cNopTable,
                                            int nbFrames, 
                                            int nbBases, 
                                            int nbLVis, 
                                            amdlibDOUBLE *wlen,
                                            amdlibDOUBLE **pst,
                                            amdlibERROR_MSG errMsg);
int amdlibFindIndexOfMinimum(double *data, int N);
double amdlibArrayDoubleMinimum(double *data, int N);
int amdlibFindIndexOfMaximum(double *data, int N);
double amdlibArrayDoubleMaximum(double *data, int N);
double amdlibArrayDoubleSum(double *data, int N);
/* Spectra */
amdlibCOMPL_STAT amdlibAddSpectrum(amdlibSPECTRUM *dstSpectrum, 
                                   amdlibSPECTRUM *srcSpectrum, 
                                   amdlibERROR_MSG   errMsg);
amdlibCOMPL_STAT amdlibWriteAmberSpectrum(fitsfile         *filePtr,
                                          amdlibWAVELENGTH *wave,
                                          amdlibSPECTRUM   *spc,
                                          amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibReadAmberSpectrum(fitsfile         *filePtr,
                                         int              nbTels,
                                         amdlibSPECTRUM   *spc,
                                         amdlibERROR_MSG  errMsg);

/* Visibilities */
amdlibCOMPL_STAT amdlibAppendVis(amdlibVIS       *dstVis, 
                                 amdlibVIS       *srcVis, 
                                 amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibInsertVis(amdlibVIS       *dstVis, 
                                 amdlibVIS       *srcVis,
                                 int insertIndex,
                                 amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibMergeVis(amdlibVIS       *vis1,
                                amdlibVIS       *vis2,
                                amdlibBOOLEAN    isInverted,
                                amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibCopyVisFrom(amdlibVIS       *dstVis,
                                   amdlibVIS       *srcVis,
                                   int             index,
                                   int             nbOfElem,
                                   amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibSplitVis(amdlibVIS        *srcVis,
                                amdlibVIS        *dstVis,
                                int              *idxFirstWlen,
                                int              *nbWlen,
                                amdlibERROR_MSG  errMsg);

amdlibCOMPL_STAT amdlibAppendVis2 (amdlibVIS2      *dstVis2, 
                                   amdlibVIS2      *srcVis2, 
                                   amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibInsertVis2 (amdlibVIS2      *dstVis2, 
                                   amdlibVIS2      *srcVis2, 
                                   int insertIndex,
                                   amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibMergeVis2(amdlibVIS2      *vis1,
                                 amdlibVIS2      *vis2,
                                 amdlibBOOLEAN    isInverted,
                                 amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibCopyVis2From(amdlibVIS2       *dstVis2,
                                    amdlibVIS2       *srcVis2,
                                    int              index,
                                    int              nbOfElem,
                                    amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibSplitVis2(amdlibVIS2       *srcVis2,
                                 amdlibVIS2       *dstVis2,
                                 int              *idxFirstWlen,
                                 int              *nbWlen,
                                 amdlibERROR_MSG  errMsg);

amdlibCOMPL_STAT amdlibAppendVis3(amdlibVIS3      *dstVis3, 
                                  amdlibVIS3      *srcVis3, 
                                  amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibInsertVis3(amdlibVIS3      *dstVis3, 
                                  amdlibVIS3      *srcVis3, 
                                  int insertIndex,
                                  amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibMergeVis3(amdlibVIS3      *vis1,
                                 amdlibVIS3      *vis2,
                                 amdlibBOOLEAN    isInverted,
                                 amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibCopyVis3From(amdlibVIS3       *dstVis3,
                                    amdlibVIS3       *srcVis3,
                                    int              index,
                                    int              nbOfElem,
                                    amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibSplitVis3(amdlibVIS3       *srcVis3,
                                 amdlibVIS3       *dstVis3,
                                 int              *idxFirstWlen,
                                 int              *nbWlen,
                                 amdlibERROR_MSG  errMsg);

amdlibBOOLEAN amdlibCompareWavelengths(amdlibWAVELENGTH *wave1,
                                       amdlibWAVELENGTH *wave2,
                                       amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibMergeWavelengths(amdlibWAVELENGTH *wave1,
                                        amdlibWAVELENGTH *wave2,
                                        amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibSplitWavelength(amdlibWAVELENGTH *srcWave,
                                       amdlibWAVELENGTH *dstWave,
                                       int              *idxFirstWlen,
                                       int              *nbWlen,
                                       amdlibERROR_MSG  errMsg);

amdlibCOMPL_STAT amdlibAppendPhotometry(amdlibPHOTOMETRY *dstPhotometry, 
                                        amdlibPHOTOMETRY *srcPhotometry, 
                                        amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibInsertPhotometry(amdlibPHOTOMETRY *dstPhotometry, 
                                        amdlibPHOTOMETRY *srcPhotometry, 
                                        int insertIndex,
                                        amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibMergePhotometry(amdlibPHOTOMETRY *phot1,
                                       amdlibPHOTOMETRY *phot2,
                                       amdlibBOOLEAN    isInverted,
                                       amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibCopyPhotFrom(amdlibPHOTOMETRY *dstPhot,
                                    amdlibPHOTOMETRY *srcPhot,
                                    int              index,
                                    int              nbOfElem,
                                    amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibSplitPhot(amdlibPHOTOMETRY *srcPhot,
                                 amdlibPHOTOMETRY *dstPhot,
                                 int              *idxFirstWlen,
                                 int              *nbWlen,
                                 amdlibERROR_MSG  errMsg);


amdlibCOMPL_STAT amdlibSelectFrames(amdlibVIS             *vis,
                                    amdlibPHOTOMETRY      *phot,
                                    amdlibPISTON          *opd,
                                    amdlibFRAME_SELECTION frameSelectionType,
                                    double                frameSelectionRatio,
                                    amdlibSELECTION       *selectedFrames,
                                    amdlibBAND            band,
                                    amdlibERROR_MSG       errMsg);
amdlibCOMPL_STAT amdlibAllocateSelection(amdlibSELECTION *selection,
                                         int              nbFrames,
                                         int              nbBases,
                                         amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibSetSelection(amdlibSELECTION *selection,
                                    amdlibBAND    band,
                                    amdlibBOOLEAN isSelected);
amdlibCOMPL_STAT amdlibUpdateSelection(amdlibSELECTION *selection);

/* Pistons */
amdlibCOMPL_STAT amdlibAppendPiston(amdlibPISTON    *dstOpd, 
                                    amdlibPISTON    *srcOpd, 
                                    amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibInsertPiston(amdlibPISTON    *dstOpd, 
                                    amdlibPISTON    *srcOpd,
                                    int insertIndex,
                                    amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibMergePiston(amdlibPISTON    *opd1, 
                                   amdlibPISTON    *opd2, 
                                   amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibComputePiston(amdlibVIS              *instantCorrFlux,
                                     amdlibBAND             band,
                                     amdlibWAVELENGTH       *wave,
                                     amdlibPISTON           *instantOpd,
                                     amdlibERROR_MSG        errMsg);
amdlibCOMPL_STAT amdlibBinPiston(amdlibPISTON    *instantOpd,
                                 amdlibBAND      band,
                                 int             firstFrame,
                                 int             nbFrames,
                                 int             iBin,
                                 amdlibPISTON    *opd);
amdlibCOMPL_STAT amdlibMeanPiston(amdlibPISTON    *instantOpd,
                                  amdlibBAND      band,
                                  int             iBin,
                                  amdlibSELECTION *selectedFrames,
                                  amdlibPISTON    *opd);
amdlibCOMPL_STAT amdlibSplitPiston(amdlibPISTON    *srcOpd,
                                   amdlibPISTON    *dstOpd,
                                   int             *nbWlen,
                                   amdlibERROR_MSG errMsg);

/* Closure phases */
amdlibCOMPL_STAT amdlibComputeClosurePhases(/* Input */
                                            amdlibVIS       *instantCorrFlux,
                                            int             iBin,
                                            amdlibBAND      band,
                                            amdlibSELECTION *selectedFrames,
					    amdlibERROR_TYPE   errorType,
                                            /* Output */
                                            amdlibVIS3      *vis3,
                                            amdlibERROR_MSG errMsg);
amdlibCOMPL_STAT amdlibBinClosurePhases(/* Input */
                                            amdlibVIS       *instantCorrFlux,
                                            int             firstFrame,
                                            int             nbFrames,
                                            int             iBin,
                                            amdlibBAND      band,
					    amdlibERROR_TYPE   errorType,
                                            /* Output */
                                            amdlibVIS3      *vis3,
                                            amdlibERROR_MSG errMsg);
void amdlibAverageClosurePhases(amdlibVIS3 *vis3);


/* Instrument configuration */
amdlibCOMPL_STAT amdlibCheckInsConfig(/* Input */
                                      amdlibSCIENCE_DATA     *data,
                                      amdlibP2VM_MATRIX      *mv2p,
                                      amdlibBOOLEAN          noCheckP2vmId,
                                      /* Output */
                                      amdlibERROR_MSG        errMsg);
/* OI-FITS */
amdlibCOMPL_STAT amdlibFillInVisTableHeader(/* Input */
                                            amdlibSCIENCE_DATA     *data,
                                            /* Output */
                                            amdlibVIS              *vis,
                                            amdlibERROR_MSG        errMsg);

amdlibCOMPL_STAT amdlibFillInVis2TableHeader(/* Input */
                                             amdlibSCIENCE_DATA     *data,
                                             /* Output */
                                             amdlibVIS2             *vis2,
                                             amdlibERROR_MSG        errMsg);

amdlibCOMPL_STAT amdlibFillInVis3TableHeader(/* Input */
                                             amdlibSCIENCE_DATA     *data,
                                             /* Output */
                                             amdlibVIS3             *vis3,
                                             amdlibERROR_MSG        errMsg);

/* High level functions */
amdlibCOMPL_STAT amdlibGenerateCalImage(const char *badPixelFile,
                                        const char *flatFieldFile,
                                        const char *darkFile,
                                        const char *inputFile,
                                        const char *outputFile);
amdlibCOMPL_STAT amdlibBtbl2Fits(const char *inputFile,
                                 const char *outputFile);

amdlibCOMPL_STAT  amdlibComputeP2vm2T(const char *badPixelFile,
                                      const char *flatFieldFile,
                                      const char *darkFile,
                                      const char *inputFile1,
                                      const char *inputFile2,
                                      const char *inputFile3,
                                      const char *inputFile4,
                                      const char *p2vmFile,
                                      amdlibDOUBLE *newSpectralOffsets,
                                      const amdlibBOOLEAN verbose);

amdlibCOMPL_STAT amdlibComputeP2vm3T(const char *badPixelFile,
                                     const char *flatFieldFile,
                                     const char *darkFile,
                                     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 amdlibBOOLEAN verbose);
amdlibCOMPL_STAT amdlibComputeSpectralCalibration2T(const char *badPixelFile,
                                                    const char *flatFieldFile,
                                                    const char *darkFile,
                                                    const char *inputFile1,
                                                    const char *inputFile2,
                                                    amdlibDOUBLE spectralOffsets[]);
amdlibCOMPL_STAT amdlibComputeSpectralCalibration3T(const char *badPixelFile,
                                                    const char *flatFieldFile,
                                                    const char *darkFile,
                                                    const char *inputFile1,
                                                    const char *inputFile2,
                                                    const char *inputFile3,
                                                    amdlibDOUBLE spectralOffsets[]);
amdlibCOMPL_STAT amdlibComputeOiData(const char    *badPixelFile,
                                     const char    *flatFieldFile,
                                     const char    *p2vmFile,
                                     const char    *darkFile,
                                     const char    *inputFile, 
                                     const char    *outputFile,
                                     const int      nbBinning,
                                     const amdlibERROR_TYPE errorType,
                                     const amdlibPISTON_ALGORITHM pistonType,
                                     const amdlibBOOLEAN noCheckP2vmId,
                                     amdlibBOOLEAN mergeOutputFiles,
                                     const amdlibFRAME_SELECTION selectionType,
                                     const double selectionRatio,
                                     const amdlibBAND bands);
amdlibCOMPL_STAT amdlibPerformSelection(const char *inputFile, 
                                    const char *inputSelFileName,
                                    const char *outputFile,
                                    const char *outputSelFileName,
                                    const amdlibFRAME_SELECTION selectionType,
                                    const double selectionRatio,
                                    const amdlibBOOLEAN useSelFile,
                                    const amdlibBOOLEAN saveSelFile,
                                    const amdlibBOOLEAN averageData);
amdlibCOMPL_STAT amdlibMergeP2vmFromFiles(const int  nbFiles,
                                          const char **p2vmFile,
                                          const char *outputFile);
amdlibCOMPL_STAT amdlibAppendOiFitsFiles(const int  nbFiles,
                                         const char **oiFitsFile,
                                         const char *outputFile);

amdlibCOMPL_STAT amdlibMergeOiStructures(amdlibWAVELENGTH *wave, 
                                         amdlibWAVELENGTH *imdWave,
                                         amdlibPHOTOMETRY *photometry, 
                                         amdlibPHOTOMETRY *imdPhot,
                                         amdlibVIS        *vis, 
                                         amdlibVIS        *imdVis,
                                         amdlibVIS2       *vis2, 
                                         amdlibVIS2       *imdVis2,
                                         amdlibVIS3       *vis3, 
                                         amdlibVIS3       *imdVis3,
                                         amdlibPISTON     *opd,
                                         amdlibPISTON     *imdOpd,
                                         amdlibERROR_MSG  errMsg);
amdlibCOMPL_STAT amdlibSplitOiStructures(amdlibWAVELENGTH *wave, 
                                         amdlibWAVELENGTH *imdWave,
                                         amdlibPHOTOMETRY *phot, 
                                         amdlibPHOTOMETRY *imdPhot,
                                         amdlibVIS        *vis, 
                                         amdlibVIS        *imdVis,
                                         amdlibVIS2       *vis2, 
                                         amdlibVIS2       *imdVis2,
                                         amdlibVIS3       *vis3, 
                                         amdlibVIS3       *imdVis3,
                                         amdlibPISTON     *opd,
                                         amdlibPISTON     *imdOpd,
                                         amdlibERROR_MSG  errMsg);

void amdlibDisplayP2vm(amdlibP2VM_MATRIX *p2vm);

void amdlibDisplayScienceData(amdlibSCIENCE_DATA *scienceData);

void amdlibDisplayWavelength(amdlibWAVELENGTH *wavelength);

void amdlibDisplayPhotometry(amdlibPHOTOMETRY *photometry);

void amdlibDisplayVis(amdlibVIS *vis);

void amdlibDisplayVis2(amdlibVIS2 *vis2);

void amdlibDisplayVis3(amdlibVIS3 *vis3);

void amdlibDisplayPiston(amdlibPISTON *opd);

void amdlibGaussSmooth(int n, double *y, double w);

#ifdef __cplusplus
}
#endif

#endif /*!amdlibProtected_H*/
