/*******************************************************************************
 * JMMC project ( http://www.jmmc.fr ) - Copyright (C) CNRS.
 ******************************************************************************/
/**
 * @file
 * Function to handle the array geometry binary table. 
 *
 * The ARRAY_GEOMETRY binary table gives the positions and characteristics of
 * the telescopes used in the observation.
 */

#define _POSIX_SOURCE 1

/* 
 * System Headers
 */
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fitsio.h"

/* 
 * Local Headers
 */
#include "amdlib.h"
#include "amdlibProtected.h"

/* 
 * Local function definition
 */
static void amdlibFreeOiArray(amdlibOI_ARRAY *array);

/**
 * Read information from ARRAY_GEOMETRY binary table and store it into the
 * amdlibOI_ARRAY structure.
 *
 * This function reads, from ARRAY_GEOMETRY binary table, positions and
 * characteristics of the telescopes used in observation. This mainly concerns
 * information related to :
 *      \li the array center coordinates
 *      \li the number of stations used
 *      \li the telescope names
 *      \li the telescope diameters
 *      \li the station names
 *      \li the station positions
 *
 * @param filePtr name of the FITS file containing IMAGING_DATA binary table.
 * @param arrayGeometry structure where array geometry information is stored.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibReadArrayGeometry (fitsfile        *filePtr, 
                                          amdlibOI_ARRAY  *arrayGeometry, 
                                          amdlibERROR_MSG errMsg)

{
    int  status = 0;
    char fitsioMsg[256];
    long nbRows;
    int  station;
    int  colnum;
    int  anynull = 0;

    amdlibLogTrace("amdlibReadArrayGeometry()"); 

    /* Reset error message */
    memset(errMsg, '\0', sizeof(amdlibERROR_MSG));
 
    /* Clear 'ARRAY_GEOMETRY' data structure */
    amdlibReleaseOiArray(arrayGeometry);
           
    /* Go to the ARRAY_GEOMETRY binary table */
    if (fits_movnam_hdu(filePtr, BINARY_TBL, "ARRAY_GEOMETRY", 0, &status))
    {
        /* If ARRAY_GEOMETRY is not found, return. This means that VLTI was
         * ignored during this exposition. */
        /* amdlibLogWarning("No ARRAY_GEOMETRY table !"); */
        return amdlibSUCCESS;
    }

    /* Get number of rows */
    if (fits_get_num_rows(filePtr, &nbRows, &status))
    {
        amdlibReturnFitsError("nbStations");
    }

    /* Allocate structure array */
    if (amdlibAllocateOiArray(arrayGeometry, (int)nbRows, errMsg) != 
                                amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }

    /* Get the array name */
    if(fits_read_key (filePtr, TSTRING, "ARRNAME", arrayGeometry->arrayName,
                      NULL, &status))
    {
        amdlibReturnFitsError("ARRNAME");
    }

    if(fits_read_key (filePtr, TSTRING, "FRAME", arrayGeometry->coordinateFrame,
                      NULL, &status))
    {
        amdlibReturnFitsError("FRAME");
    }

    /* Get the array position */
    if(fits_read_key (filePtr, TDOUBLE, "ARRAYX", 
                      &arrayGeometry->arrayCenterCoordinates[0],
                      NULL, &status))
    {
        amdlibReturnFitsError("ARRAYX");
    }

    if(fits_read_key (filePtr, TDOUBLE, "ARRAYY",
                      &arrayGeometry->arrayCenterCoordinates[1], NULL, &status))
    {
        amdlibReturnFitsError("ARRAYY");
    }
    /* correct ISS error if present (was present on Mars 2007) */
    if (arrayGeometry->arrayCenterCoordinates[1] > 
        arrayGeometry->arrayCenterCoordinates[0]) 
    {
        arrayGeometry->arrayCenterCoordinates[2] = 
            arrayGeometry->arrayCenterCoordinates[1];
        arrayGeometry->arrayCenterCoordinates[1] = 
            arrayGeometry->arrayCenterCoordinates[0];
        arrayGeometry->arrayCenterCoordinates[0] = 
            arrayGeometry->arrayCenterCoordinates[2];
        arrayGeometry->arrayCenterCoordinates[2] = 0.0;
    }
    
    if(fits_read_key (filePtr, TDOUBLE, "ARRAYZ", 
                      &arrayGeometry->arrayCenterCoordinates[2], NULL, &status))
    {
        amdlibReturnFitsError("ARRAYZ");
    }

    /* For each station */
    for (station=1; station<=arrayGeometry->nbStations; station++) 
    {
        char *p;

        /* Get the telescope name */
        if(fits_get_colnum (filePtr, CASEINSEN, "TEL_NAME", &colnum,
                            &status))
        {
            amdlibReturnFitsError("TEL_NAME");
        }

        p = arrayGeometry->element[station-1].telescopeName;

        if(fits_read_col (filePtr, TSTRING, colnum, station, 1, 1,
                          NULL, &p, &anynull, &status))
        {
            amdlibReturnFitsError("TEL_NAME");
        }

        /* Get the station name */
        if(fits_get_colnum (filePtr, CASEINSEN, "STA_NAME", &colnum,
                            &status))
        {
            amdlibReturnFitsError("STA_NAME");
        }

        p = arrayGeometry->element[station-1].stationName;

        if(fits_read_col (filePtr, TSTRING, colnum, station, 1, 1,
                          NULL, &p, &anynull, &status))
        {
            amdlibReturnFitsError("STA_NAME");
        }

        /* Get the station index */
        if(fits_get_colnum (filePtr, CASEINSEN, "STA_INDEX", &colnum,
                            &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }

        if(fits_read_col (filePtr, TINT, colnum, station, 1, 1, NULL,
                          &arrayGeometry->element[station-1].stationIndex, 
                          &anynull, &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }

        /* Get the telescope diameter */
        if(fits_get_colnum (filePtr, CASEINSEN, "DIAMETER", &colnum,
                            &status))
        {
            amdlibReturnFitsError("DIAMETER");
        }

        if(fits_read_col (filePtr, TDOUBLE, colnum, station, 1, 1, NULL,
                          &arrayGeometry->element[station-1].elementDiameter,
                          &anynull, &status))
        {
            amdlibReturnFitsError("DIAMETER");
        }

        /* Get the station position */
        if(fits_get_colnum (filePtr, CASEINSEN, "STAXYZ", &colnum,
                            &status))
        {
            amdlibReturnFitsError("STAXYZ");
        }

        if(fits_read_col (filePtr, TDOUBLE, colnum, station, 1, 3, NULL,
                          &arrayGeometry->element[station-1].stationCoordinates,
                          &anynull, &status)) 
        {
            amdlibReturnFitsError("STAXYZ");
        }

    }

    return amdlibSUCCESS;
}

/**
 * Retrieve OI array structure from the specified raw data 
 *
 * @param rawData input raw data 
 * @param array pointer to OI array  
 * @param errMsg error description message returned if function fails.
 *
 * @return 
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibGetOiArrayFromRawData(amdlibRAW_DATA  *rawData,
                                             amdlibOI_ARRAY  *array,
                                             amdlibERROR_MSG errMsg)
{
    int station;
    
    amdlibLogTrace("amdlibGetOiArrayFromRawData()");
    
    /* Allocate structure array */
    if (amdlibAllocateOiArray
        (array, rawData->arrayGeometry.nbStations, errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }

    /* Copy 'amdlibOI_ARRAY' data structure */
    strcpy(array->arrayName, rawData->arrayGeometry.arrayName);
    strcpy(array->coordinateFrame, rawData->arrayGeometry.coordinateFrame);
    array->arrayCenterCoordinates[0] =
        rawData->arrayGeometry.arrayCenterCoordinates[0];
    array->arrayCenterCoordinates[1] =
        rawData->arrayGeometry.arrayCenterCoordinates[1];
    array->arrayCenterCoordinates[2] =
        rawData->arrayGeometry.arrayCenterCoordinates[2];

    for (station = 0; station < array->nbStations; station++)
    {
        strcpy(array->element[station].telescopeName, 
               rawData->arrayGeometry.element[station].telescopeName);
        strcpy(array->element[station].stationName, 
               rawData->arrayGeometry.element[station].stationName);
        array->element[station].stationIndex =
            rawData->arrayGeometry.element[station].stationIndex;
        array->element[station].elementDiameter =
            rawData->arrayGeometry.element[station].elementDiameter;
        array->element[station].stationCoordinates[0] =
            rawData->arrayGeometry.element[station].stationCoordinates[0];
        array->element[station].stationCoordinates[1] =
            rawData->arrayGeometry.element[station].stationCoordinates[1];
        array->element[station].stationCoordinates[2] =
            rawData->arrayGeometry.element[station].stationCoordinates[2];
    }

    return amdlibSUCCESS;
}

/* 
 * Protected functions
 */
/**
 * Allocate memory for storing information of OI array. 
 *
 * This function allocates memory for storing information of N stations. 
 *
 * @param array pointer to amdlibOI_ARRAY structure.
 * @param nbElements number of stations 
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibAllocateOiArray(amdlibOI_ARRAY *array,
                                       const int      nbElements,
                                       amdlibERROR_MSG  errMsg)
{
    amdlibLogTrace("amdlibAllocateOiArray()");
    
    /* First free previous allocated memory */
    if (array->thisPtr == array)
    {
        amdlibFreeOiArray(array);
    }

    /* Init data structure */
    array->thisPtr = memset(array, '\0', sizeof(*array));
    /* Set array size */
    array->nbStations = nbElements;

    /* Allocate table pointer list  */
    array->element = calloc(nbElements, sizeof(*(array->element)));
    if (array->element == NULL)
    {
        amdlibFreeOiArray(array);
        amdlibSetErrMsg("%s OI array : %ld required", amdlibERR_ALLOC_MEM, 
                        (long)(nbElements * sizeof(*(array->element))));
        return amdlibFAILURE;
    }
    /* Return */
    return amdlibSUCCESS;
}

/**
 * Release memory allocated to store OI array, and reset the structure members
 * to zero.
 *
 * @param array pointer to amdlibOI_ARRAY structure.
 */
void amdlibReleaseOiArray(amdlibOI_ARRAY *array)
{
    amdlibLogTrace("amdlibReleaseOiArray()");
    
    amdlibFreeOiArray(array);
    memset (array, '\0', sizeof(amdlibOI_ARRAY));
}

/*
 * Local functions
 */
/**
 * Free memory allocated for OI array. 
 *
 * This function frees memory allocated to store array information.
 * 
 * @param array pointer to amdlibOI_ARRAY structure.
 */
void amdlibFreeOiArray(amdlibOI_ARRAY *array)
{
    amdlibLogTrace("amdlibFreeOiArray()");
    
    /* Check thisPtr and do nothing if unitialized*/
    if (array->thisPtr != array)
    {
        return;
    }
    if (array->element != NULL)
    {
        free(array->element);
    }
    memset (array, '\0', sizeof(*array));

}

/*___oOo___*/
