/** @file  psFits.h
 *
 *  @brief Contains Fits I/O routines
 *
 *  @ingroup FileIO
 *
 *  @author Robert DeSonia, MHPCC
 *
 *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
 *  @date $Date: 2005-01-18 03:15:03 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 */

#ifndef PS_FITS_H
#define PS_FITS_H

#include<fitsio.h>

#include "psType.h"
#include "psArray.h"
#include "psVector.h"
#include "psMetadata.h"
#include "psHash.h"
#include "psImage.h"

/// @addtogroup FileIO
/// @{

typedef enum {
    PS_FITS_TYPE_NONE = -1,
    PS_FITS_TYPE_IMAGE = IMAGE_HDU,
    PS_FITS_TYPE_BINARY_TABLE = BINARY_TBL,
    PS_FITS_TYPE_ASCII_TABLE = ASCII_TBL,
    PS_FITS_TYPE_ANY = ANY_HDU
} psFitsType;

/** FITS file object.
 *
 *  This object should be considered opaque to the user; no item in this
 *  struct should be accessed directly.
 *
 */
typedef struct
{
    fitsfile* p_fd;                    ///< the CFITSIO fits files handle.
    const char* filename;              ///< the filename of the fits file
}
psFits;

/** Opens a FITS file and allocates the associated psFits object.
 *
 *  @return psFits*    new psFits object for the FITS files specified or
 *                     NULL if the open of the FITS file failed
 */
psFits* psFitsAlloc(
    const char* name                   ///< the FITS file name
);

/** Moves the FITS HDU to the specified extension name.
 *
 *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
 */
bool psFitsMoveExtName(
    psFits* fits,                      ///< the psFits object to move
    const char* extname                ///< the extension name
);

/** Moves the FITS HDU to the specified extension number
 *
 *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
 */
bool psFitsMoveExtNum(
    psFits* fits,                      ///< the psFits object to move
    int extnum,                        ///< the extension number to move to (zero is primary HDU)
    bool relative                      ///< if true, extnum is a relative number to the current position
);

/** Get the current extension number, where 0 is the primary HDU.
 *
 *  @return int        Current HDU number of the psFits file or < 0 if an error
 *                     occurred.
 */
int psFitsGetExtNum(
    psFits* fits                       ///< the psFits object
);

/** Get the current extension name.
 *
 *  @return int        Current HDU name of the psFits file or NULL if an
 *                     error occurred.
 */
char* psFitsGetExtName(
    psFits* fits                       ///< the psFits object
);

/** Set the current extension's name
 *
 *  @return bool       TRUE if the extension was successfully set, otherwise FALSE.
 */
bool psFitsSetExtName(
    psFits* fits,                      ///< the psFits object
    const char* name                   ///< the extension name
);

/** Get the total number of HDUs in the FITS file.
 *
 *  @return int        The total number of HDUs in the FITS file or < 0 if an
 *                     error occurred.
 */
int psFitsGetSize(
    psFits* fits                       ///< the psFits object
);

/** Get the extension type of the current HDU.
 *
 *  @return psFitsType The type of the current HDU.  If PS_FITS_TYPE_UNKNOWN,
 *                     the type could not be determined.
 */
psFitsType psFitsGetExtType(
    psFits* fits                       ///< the psFits object
);

/** Reads the header of the current HDU.
 *
 *  @return psMetadata*   the header data
 */
psMetadata* psFitsReadHeader(
    psMetadata* out,
    ///< The psMetadata to add the header data.  If null, a new psMetadata is created.

    const psFits* fits                 ///< the psFits object
);

/** Reads the header of all HDUs.  The current HDU is not changed.
 *
 *  @return psHash*      the header data
 */
psHash* psFitsReadHeaderSet(
    psHash* out,
    ///< The psHash to add the header data via psMetadata items.  If null, a
    ///< new psHash is created.  The keys of the psHash are the extension names
    ///< of the cooresponding HDUs.

    psFits* fits                       ///< the psFits object
);

/** Writes the values of the metadata to the current HDU header.
 *
 *  @return bool        if TRUE, the write was successful, otherwise FALSE.
 */
bool psFitsWriteHeader(
    const psMetadata* header,          ///< the psMetadata data in which to write
    psFits* fits                       ///< the psFits object
);

/** Reads an image, given the desired region and z-plane.
 *
 *  @return psImage*     the read image or NULL if there was an error.
 */
psImage* psFitsReadImage(
    psImage* out,                      ///< a psImage to recycle.
    psFits* fits,                      ///< the psFits object
    psRegion region,                   ///< the region in the FITS image to read
    int z                              ///< the z-plane in the FITS image cube to read
);

/** Writes an image, given the desired region and z-plane.
 *
 *  @return bool        TRUE is the write was successful, otherwise FALSE.
 */
bool psFitsWriteImage(
    psFits* fits,                      ///< the psFits object
    const psMetadata* header,          ///< header items for the new HDU.  Can be NULL.
    const psImage* input,              ///< the image to output
    int depth,                         ///< the number of z-planes of the FITS image data cube
    char* extname                      ///< extension name
);

/** Updates the FITS file image, given the desired region and z-plane.
 *
 *  @return bool        TRUE is the write was successful, otherwise FALSE.
 */
bool psFitsUpdateImage(
    psFits* fits,                      ///< the psFits object
    const psImage* input,              ///< the image to output
    psRegion region,                   ///< the region in the FITS image to write
    int z                              ///< the z-planes of the FITS image data cube to write
);

/** Reads a table row.  The current HDU type must be either
 *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
 *
 *  @return psMetadata*    The table row's data.  The keys are the column names.
 */
psMetadata* psFitsReadTableRow(
    psFits* fits,                      ///< the psFits object
    int row                            ///< row number to read
);

/** Reads a table column.  The current HDU type must be either
 *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
 *
 *  @return psArray*    Array of data items for the specified column or NULL
 *                      if an error occurred.
 */
psArray* psFitsReadTableColumn(
    psFits* fits,
    const char* colname
);

/** Reads a table column of numbers.  The current HDU type must be either
 *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
 *
 *  @return psVector*    Vector of data for the specified column or NULL
 *                       if an error occurred.
 */
psVector* psFitsReadTableColumnNum(
    psFits* fits,                      ///< the psFits object
    const char* colname                ///< the column name
);


/** Reads a whole FITS table.  The current HDU type must be either
 *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
 *
 *  @return psArray*     Array of psMetadata items, which contains the output
 *                       data items of each row.
 *
 *  @see psFitsReadTableRow
 */
psArray* psFitsReadTable(
    psFits* fits                       ///< the psFits object
);

/** Writes a whole FITS table.  The current HDU type must be either
 *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
 *
 *  @return bool        TRUE if the write was successful, otherwise FALSE
 *
 *  @see psFitsReadTableRow
 */
bool psFitsWriteTable(
    psFits* fits,                      ///< the psFits object
    psMetadata* header,                ///< header items for the new HDU.  Can be NULL.
    psArray* table,
    ///< Array of psMetadata items, which contains the output data items of each row.
    char* extname                      ///< extension name
);

/** Updates a FITS table.  The current HDU type must be either
 *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
 *
 *  @return bool        TRUE if the write was successful, otherwise FALSE
 *
 *  @see psFitsWriteTable
 */
bool psFitsUpdateTable(
    psFits* fits,                      ///< the psFits object
    psArray* table
    ///< Array of psMetadata items, which contains the output data items of each row.
);

/// @}

#endif
