/*                                             -*- c-file-style: "Ellemtel" -*-

`fh.h' - FITS Handling Routines for Standard FITS and MEF

This file is part of version 1 of the FITS Handling Library.
Read the `License' file for terms of use and distribution.
Copyright 2001, Canada-France-Hawaii Telescope, daprog@cfht.hawaii.edu.

___This header automatically generated from `Index'. Do not edit it here!___ */
/* $Id: fh.h,v 1.5 2003/05/28 06:45:17 isani Exp isani $
 * Created: 2001-4-2
 * Documentation: See http://software.cfht.hawaii.edu/libfh/
 */
#ifndef _INCLUDED_fh
#define _INCLUDED_fh 1
/* ------------------------------------------------
 * Definitions, Types, and Structures used by libfh
 * ------------------------------------------------
 */
#define FH_MAX_STRLEN	68  	/* Longest possible FITS string value */
#define FH_NAME_SIZE	8   	/* Size in bytes of a FITS card name */
#define FH_CARD_SIZE	80  	/* Size in bytes of a FITS card */
#define FH_BLOCK_SIZE	2880	/* FITS file block size in bytes (36 cards) */
#define FH_AUTO		0.0 	/* Don't need `idx' numbers?  Use this everywhere. */
#define FH_TYPESIZE_RAW	0 	/* Never swap the image data === Document! */
#define FH_TYPESIZE_8	1 	/* 8-bit data (swapping not applicable) */
#define FH_TYPESIZE_16	2 	/* 16-bit data, auto-swap if needed */
#define FH_TYPESIZE_16U 3	/* 16-bit unsign, offset 32768 when writing */
#define FH_TYPESIZE_16US -3	/* 16-bit unsign, offset 32768, force swap */
#define FH_TYPESIZE_16URAW 5	/* 16-bit unsign, offset 32768, no swap */
#define FH_TYPESIZE_16S	-2	/* 16-bit data, force swapping */
#define FH_TYPESIZE_32	4 	/* 32-bit data, auto-swap if needed */
#define FH_TYPESIZE_32S	-4	/* 32-bit data, force swapping */
#define FH_TYPESIZE_64	8 	/* 64-bit data, auto-swap if needed */
#define FH_TYPESIZE_64S	-8	/* 64-bit data, force swapping */
#define FH_COUNT_CARDS "@FH_COUNT_CARDS@"  /* See also: fh_count_cards() */

/* Size of buffer in fhTableCol structure for format string. */
#define FH_TABLE_FORMAT_STR_LEN 20

typedef enum /* fh_result -- Result Code for most functions in this library: */
{  FH_SUCCESS = 0,
   FH_IN_ERRNO = -1,	/* A system call failed; check value of errno */
   FH_END_OF_FILE = -2,	/* EOF condition */
   FH_NOT_FOUND = -3,	/* No such card or header unit found */
   FH_INVALID = -4,	/* Invalid arguments passed (NULL pointer, etc.) */
   FH_BAD_VALUE = -5,	/* Wrong type, error in conversion, string too long */
   FH_BAD_SIZE = -6,	/* Size argument doesn't match BITPIX and NAXIS */
   FH_BAD_PADDING = -7,	/* Problem with header padding or \0's after data */
   FH_NO_SPACE = -8,	/* No space for header (file must be re-created.) */
   FH_NO_MEMORY = -9,	/* A call to malloc failed somewhere */
   FH_IS_TTY = -10,	/* This call doesn't work on tty devices */
   FH_IS_DIR = -11,	/* This call requires a file, not a directory */
   FH_INTERNAL_ERROR=-99, /* Internal error with the library */
} fh_result;

typedef enum
{  FH_FILE_RDONLY,		/* Open, get non-exclusive lock, read header, unlock */
   FH_FILE_RDWR,		/* Open, get exclusive lock, read, unlock on rewrite */
   FH_FILE_RDONLY_NOLOCK,	/* Same as RDONLY but without advisory lock */
   FH_FILE_RDWR_NOLOCK 		/* Same as RDWR but without advisory lock */
} fh_mode;

typedef enum
{  FH_FALSE=0,			/* FITS card contains 'F' in column 30 */
   FH_TRUE=1			/* FITS card contains 'T' in column 30 */
} fh_bool;			/* Value for "logical" FITS cards */

/* The possible field formats in an ASCII table extension. */
typedef enum
{
   FH_TABLE_FORMAT_CHAR = 'A',   /* Designates a string field. */
   FH_TABLE_FORMAT_INT = 'I',    /* Integer field. */
   FH_TABLE_FORMAT_FLOAT = 'F',  /* Float field. */
   FH_TABLE_FORMAT_DOUBLE = 'D'  /* Double field. */
} fhTableFormat;

typedef struct
{
  char * name;    /* Short string identifying this column's data. */ 
  char * comment; /* Longer blurb about this column's data. */
  char * units;   /* Units for column's data. */
  fhTableFormat format; /* Type of data held in this column. */
  int width;      /* Width of this column's data, in characters. */
  int dec_places; /* For doubles, etc, the number of decimal places to write. */
  /* Everything below this line is generated by fh_table_init(). These 
   * do not need to be filled out by hand. */
  int first_char_pos; /* Position of first character in this column. Calculated on the fly. */
  char format_string[FH_TABLE_FORMAT_STR_LEN]; /* Format string for printf. */
} fhTableCol;

/* Describes the layout of an ASCII table. */
typedef struct
{
  char * extname;    /* Name of table extension. */
  int num_cols;      /* Number of rows in table. */
  int num_rows;      /* Number of columns in table. */
  fhTableCol * cols; /* Array of cols columns. */
  /* Everything below this line is generated by fh_table_init(). These 
   * do not need to be filled out by hand. */
  int widest_col;    /* Width of widest column. */
  char * strbuf;     /* Will point to buf of widest_col + 1 chars. */
  int row_width;     /* Number of characters in a table tow. */
  int table_size;    /* Total size of table data, in bytes. */
} fhTable;

typedef void* HeaderUnit; /* Handle to a list of FITS cards allocated by fh_create. */

typedef void (*fh_logger_t) (const char* message);
void fh_log_error(fh_logger_t logger);	/* These are used only if you do not */
void fh_log_perror(fh_logger_t logger);	/* want messages going to stderr. */
void fh_log_warning(fh_logger_t logger); /* See manual for more information. */

/* ---------------------------------------------------------
 * Open, Close, Lock, or create new FITS files/header blocks
 * ---------------------------------------------------------
 */
HeaderUnit	fh_create(void);
fh_result	fh_destroy(HeaderUnit hu);
/*
 * These are the first and last functions called to allocate and free
 * space for a HeaderUnit. After fh_create, use fh_file, fh_read, fh_set...
 * NOT to be used with HeaderUnit's returned by the fh_ehu() functions!
 */
fh_result	fh_file(HeaderUnit hu, const char* filespec, fh_mode mode);
/*
 * Open file and read headers from the PHU or [extname] if given in filespec.
 * File is left at start of image data in the file or 1st extension.
 */
int		fh_file_desc(HeaderUnit hu);
/*
 * Returns the file descriptor associated with HeaderUnit (or -1, if 
 * if no file is open.)  This is only needed if you used fh_file() to
 * open a file and you need to read or write its file descriptor directly.
 */
fh_result	fh_read(HeaderUnit hu, int fd, double idx);
/*
 * Alternative to fh_file() if you opened the file descriptor yourself.
 * fh_read() reads cards from a file descriptor and inserts them into
 * the HeaderUnit cards starting at "idx" (use FH_AUTO to append to the end.)
 * `fd' is left at start of image data in the file or 1st extension.
 */

fh_result fh_read_keyword_buffer(HeaderUnit hu, const char* fitsblock, double idx, int fast);
/*
 * Use this function if keywords are already in memory instead of a file.
 *
 * Inputs:
 *    fitsblock    - Exactly 2880 bytes of FITS keyword data in memory.
 *    idx          - Starting sorting number (or use FH_AUTO).
 *    fast         - If non-zero, skip all integrity checks in order to
 *                   make parsing the keywords as fast as possible.
 *
 * Outputs:
 *    hu           - Keyword header unit with parsed keywords added.
 *
 * Returns:
 *    FH_INVALID   - If hu or fitsblock is NULL.
 *    FH_NO_MEMORY - Memory allocation error
 *    FH_BAD_VALUE - Illegal characters in keyword value (only if !fast)
 *    FH_SUCCESS     - OK.
 *    FH_END_OF_FILE - OK, and END Keyword found.
 */

fh_result fh_count_cards(HeaderUnit hu);
/*
 * Any following fh_[re]write() call will print the number of cards
 * needed instead of actually updating a file.  This mode is also
 * selected when fh_file() is called with filedesc = FH_COUNT_CARDS.
 */
fh_result fh_reserve(HeaderUnit hu, int n);
/*
 * Set the number of COMMENT cards to reserve in the output when fh_write()
 * is used to create a new FITS header.  NOT for use with fh_rewrite().
 */
int fh_get_reserve(HeaderUnit hu);
/*
 * Get the current setting or the number of reserve cards found in
 * a header unit.  Returns -1 if hu is invalid.
 */
fh_result fh_validate(HeaderUnit hu); /* fh_validate.c; for use by fhtool.c */

fh_result fh_write(HeaderUnit hu, int fd);
/*
 * Writes cards in 2880 byte **PADDED** blocks to a file descriptor.
 * This adds `END' line also, and file pointer `fd' is left at start of
 * image data, just like it is after a call to fh_file().
 */

fh_result fh_write_keyword_buffer(HeaderUnit hu, char* buffer, int* fits_blocks_inout);
/*
 * Use this function to get formatted keywords into a memory buffer.
 *
 * Inputs:
 *    hu                - Header unit structure with some keywords in it.
 *    buffer            - Pointer to user allocated memory buffer
 *    fits_blocks_inout - Pointer to buffer size / 2880 (FH_BLOCK_SIZE)
 *
 * Outputs:
 *    fits_blocks_inout - Returns with the number of 2880-byte blocks copied.
 *
 * Returns:
 *    FH_INVALID   - An argument was null or invalid.
 *    FH_NO_SPACE  - Partial header copied, but there was insufficient space.
 *    FH_NO_MEMORY - Memory allocation error occurred previously (on fh_set)
 *    FH_SUCCESS   - OK.
 */

fh_result fh_map_raw_image(HeaderUnit hu, void** data, int size);
fh_result fh_munmap_raw_image(HeaderUnit hu);
/*
 * These calls give the calling program access to a memory mapped pointer
 * to the data in the FITS file.  The data format is equivalent to what
 * would be produced by fh_read_image() with typesize=FH_TYPESIZE_RAW.
 * Whether or not the file can be modified through the memory-mapped
 * pointer depends on whether fh_mode=FH_FILE_RDONLY or FH_FILE_RDWR
 * in the call to fh_file() that opened the file.  Be sure to use
 * fh_munmap_raw_image() when done.
 *
 * `size' must match the exact number bytes of data which should be in
 * the file, according to its NAXIS and BITPIX values, excluding padding.
 *
 * %%% TODO: FH_FILE_RDWR is completely unimplemented!
 *
 * %%% TODO: File locking is not handled properly yet.  For now, use
 * FH_FILE_RDONLY_NOLOCK and FH_FILE_RDWR_NOLOCK, especially on MEF
 * files where the locks are left in place until the file is closed
 * otherwise!
 */

fh_result fh_write_image(HeaderUnit hu, int fd, void* data, int size, int typesize);
fh_result fh_write_padding(HeaderUnit hu, int fd);
fh_result fh_write_padded_image(HeaderUnit hu, int fd, void* data, int size, int typesize);
/*
 * fh_write_image and fh_write_padded_image writes exactly `size' bytes of
 * `data' at the current file position of `fd'.  In the case of
 * fh_write_padded_image, it is followed by enough padding to reach an
 * FH_BLOCK_SIZE boundary, and `size' must match what the library
 * calculates from the NAXIS and BITPIX values in `hu', excluding padding.
 *
 * fh_write_padding writes padding meant to go AFTER THE DATA.  (The header
 * is automatically padded.)  This routine is used if you write the data
 * to fd yourself, or in segments using fh_write_image().
 */
fh_result fh_read_image(HeaderUnit hu, int fd, void* data, int size, int typesize);
fh_result fh_read_padding(HeaderUnit hu, int fd);
fh_result fh_read_padded_image(HeaderUnit hu, int fd, void* data, int size, int typesize);
/*
 * Read image data into buffer.  Padding is not copied into *data.
 * To read data in segments, use fh_read_image() followed by a final
 * call to fh_read_padding().  Calling fh_read_padding() and ensuring
 * that it returns FH_SUCCESS helps ensure that the entire file was
 * read correctly.
 *
 * Use the final form, fh_read_padded_image() to read the entire data
 * in one call.  In this case, as with fh_write_padded_image(), `size'
 * must match the exact number bytes of data which should be in the
 * file, according to its NAXIS and BITPIX values, excluding padding.
 */
fh_result fh_copy_padded_image(HeaderUnit hu, int fd_out, int fd_in);
/*
 * Read image data for `fd_out' from `fd_in'.
 */
fh_result fh_copy_and_verify_padded_image(HeaderUnit hu, int fd_out, int fd_in);
/*
 * Just like fh_copy_padded_image, but with an additional check for blocks
 * of data with zero pixels.
 */

/* WARNING: If you intend to make a utility that can read from pipes
 * (stdin) then you should avoid the following functions.  The are the
 * only routines which need to be able to seek in a file.  If you are
 * always reading from a file, this routines are ok.
 */
/* ------------- BEGIN ROUTINES WHICH USE lseek() ....  ------------ */
fh_result fh_reserve_padded_image(HeaderUnit hu, int fd);
/*
 * This is like fh_write_padded_image(), except that it quickly seeks
 * to the location where the last byte of actual image data will
 * eventually go, writes a NUL character, and then writes all of the
 * padding.  This sets things up for a future write by mmap.
 */
fh_result fh_rewrite(HeaderUnit hu);
/*
 * This call is only valid if fh_read() has been called to create `hu' in the
 * first place.  Headers are written back to the same file from which they
 * were read.  Your program should check for FH_NO_SPACE (and any other non-
 * FH_SUCCESS returns).  FH_NO_SPACE means there was not enough room to
 * rewrite the modified header unit back to the same file.
 */
void fh_link_ehu_to_phu(HeaderUnit ehu, HeaderUnit phu);

HeaderUnit fh_ehu(HeaderUnit phu, int number);
HeaderUnit fh_ehu_by_imageid(HeaderUnit phu, int imageid);
HeaderUnit fh_ehu_by_extname(HeaderUnit phu, const char* extname);
/*
 * `phu' must be first be filled from a file by fh_read().
 * `number' = 0 returns the phu and seeks the file to end of the phu cards.
 * `number' = 1,2,3 returns the extension 1,2,3 and seeks to the start of data.
 * Last two functions search for matching IMAGEID or EXTNAME cards.
 */
/* ------------- END OF ROUTINES WHICH USE lseek() ...  ------------ */

int fh_extensions(HeaderUnit hu); /* Count extensions (0 if EXTEND=F) */

int fh_image_bytes(HeaderUnit hu); /* Proper # of image bytes for hu */
int fh_image_blocks(HeaderUnit hu); /* Same value, div 2880, rounded up */
int fh_header_blocks(HeaderUnit hu); /* # of header bytes, div 2880, rnd. up */
/*
 * The number of image_blocks is calculated from hu's BITPIX and NAXIS cards.
 * The image_blocks functions return 0 if BITPIX or an NAXIS card is missing.
 */
/* ----------------------
 * Getting Keyword Values
 * ----------------------
 */
fh_result fh_get_bool(HeaderUnit hu, const char* name, fh_bool* value);
fh_result fh_get_int(HeaderUnit hu, const char* name, int* value);
fh_result fh_get_flt(HeaderUnit hu, const char* name, double* value);
fh_result fh_get_str(HeaderUnit hu, const char* name, char* value, int maxlen);
/*
 * Get FITS card values from `hu'.
 * For fh_get_str(), `maxlen' is the size of the buffer, including
 * space for a '\0' termination character which will be added by
 * this routine.  If it doesn't fit, a FH_BAD_VALUE will be returned.
 * (Pass buffers of FH_MAX_STRLEN + 1 to be sure this doesn't happen.)
 */
double fh_idx_after(HeaderUnit hu, const char* name);
double fh_idx_before(HeaderUnit hu, const char* name);
/*
 * Get a value for a new `idx' which will cause a new card to be inserted
 * just after (or before) an existing card `name'.  If `name' does not
 * exist, this function returns FH_AUTO, which causes the new card to be
 * added to the end of the header.
 */
fh_result fh_search(HeaderUnit hu, const char* name, double* idx);
/*
 * Search for a particular card, and (if idx!=0), save a copy of its
 * internal sorting number.  The `idx' is useful if you need to insert
 * cards just before or after those read from a file.  (Increment or
 * decrement the idx value by .000001)
 */
fh_result fh_show(HeaderUnit hu); /* Print `hu' to screen for debugging */

/* ----------------------
 * Setting Keyword Values
 * ----------------------
 */
fh_result fh_remove(HeaderUnit hu, const char* name);
/*
 * Removes a card `name' from `hu'.  COMMENTS cannot be removed.
 */
fh_result fh_set_all_units(HeaderUnit hu);
/*
 * By default, if fh_set*() operations are performed on a PHU read
 * from a file, they are not applied to extension units.  Call this
 * function BEFORE calling any other fh_set* functions on a PHU when
 * you also want the extensions (which you'd get with fh_ehu()) to
 * be changed, otherwise fh_set() only applies to the current HeaderUnit.
 */
void fh_set_str(HeaderUnit hu, double idx, const char* name,
		const char* value, const char* comment);
void fh_set_com(HeaderUnit hu, double idx, const char* name,
		const char* comment);
void fh_set_int(HeaderUnit hu, double idx, const char* name,
		int value, const char* comment);
void fh_set_flt(HeaderUnit hu, double idx, const char* name,
		double value, int significant_digits, const char* comment);
void fh_set_pfl(HeaderUnit hu, double idx, const char* name,
		double value, int decimal_places, const char* comments);
void fh_set_bool(HeaderUnit hu, double idx, const char* name,
		 fh_bool value, const char* comment);
/*
 * Change/Create FITS card in table of type string, integer, or floating point.
 * The comment field may get silently truncated if it doesn't fit in 80 cols.
 */
void fh_set_val(HeaderUnit hu, double idx, const char* name,
		const char* value, const char* comment);
/*
 * This final form of fh_set is used by neodatah to insert pre-formatted
 * FITS cards (80 bytes) obtained from the status server staging area.
 */
void fh_set_card(HeaderUnit hu, double idx, const char* card);
/*
 * This one is used by the nheader command in DetCom to set a value with
 * "auto-detected" type of float, int, boolean, or string.  Use typed
 * functions instead, when the value type is known.
 */
const char* fh_first(HeaderUnit hu);	/* See examples.  These are */
const char* fh_next(HeaderUnit hu);	/* used to iterate through a list */
double fh_idx(HeaderUnit hu); /* idx of the last card returned by fh_next */
fh_result fh_merge(HeaderUnit hu, const HeaderUnit source); /* source unchanged */
fh_result fh_copy(HeaderUnit hu, const HeaderUnit source); /* fh_merge+keep reserve */

/* ---------------------------------------------------------
 * Reading and writing tables
 * ---------------------------------------------------------
 */

fh_result
fh_table_init(fhTable * table);
/* Call once to initialise information about a given table. This will
 * walk through all of the columns and fill out starting character
 * positions and so forth, and will calculate the size of each row
 * and the size of the table as a whole, and so forth. */

fh_result
fh_table_populate_header(HeaderUnit hu, fhTable * table);
/* Sets up keywords for a table extension header, including all 
 * of the column information, etc. This will initialise the table
 * structure if necessary. This does not include any fh_write... 
 * calls to write the header to the file. */

fh_result
fh_reserve_padded_table(HeaderUnit hu, int fd);
/* Analogous to fh_reserve_padded_image() for tables. This reserves 
 * space for the table based on the information in the passed extension
 * header at the current location in the file. The header must therefore
 * be populated prior to calling this function (use 
 * fh_table_populate_header() to build a header from a table structure.
 */

fh_result
fh_map_table(HeaderUnit hu, void** data, int size);
fh_result
fh_munmap_table(HeaderUnit hu);
/* Analogous to fh_map_raw_image() and fh_munmap_image() for tables. 
 * These calls give the calling program access to a memory mapped pointer
 * to the data in the FITS file.  The data format is equivalent to what
 * would be produced by fh_read_image() with typesize=FH_TYPESIZE_RAW.
 * Whether or not the file can be modified through the memory-mapped
 * pointer depends on whether fh_mode=FH_FILE_RDONLY or FH_FILE_RDWR
 * in the call to fh_file() that opened the file.  Be sure to use
 * fh_munmap_table() when done.
 *
 * `size' must match the exact number bytes of data which should be in
 * the file, according to its NAXIS and BITPIX values, excluding padding.
 *
 * %%% TODO: FH_FILE_RDWR is completely unimplemented!
 *
 * %%% TODO: File locking is not handled properly yet.  For now, use
 * FH_FILE_RDONLY_NOLOCK and FH_FILE_RDWR_NOLOCK, especially on MEF
 * files where the locks are left in place until the file is closed
 * otherwise!
 */

fh_result
fh_table_read_value(fhTable * table, void * data,
                    int row, int col, void * value);
/* Reads a single value from an existing table at (row, column). 
 * The table field is interpreted based on the format of that
 * column in the table structure, and is passed back via the
 * 'value' pointer. It is the caller's responsibility to ensure
 * that 'value' points to a location with sufficient space for
 * the data in the field, and that any casting to & from a void
 * pointer is done correctly.
 */

fh_result
fh_table_write_value(fhTable * table, void * data, 
                     int row, int col, void * value);
/* Similar to read_value() above, this function overwrites a field
 * in a table at the given location.  The caller is repsonsible 
 * for casting the location of the stored data to a void pointer. 
 */

#endif /* _INCLUDED_fh */
