/* Fitsy FITS header creation routines.

   EXAMPLES
+
		FITSHead	fits;

	fits = #ft_headinit(NULL, 0);	/-* Create an empty header	*-/

	/-* Build a FITS file header by hand.
	 *-/
	#ft_headappl(fits, "SIMPLE", 0,   1, "Primary FITS image array");
	#ft_headappi(fits, "BITPIX", 0, -32, "Single floats");
	#ft_headappi(fits, "NAXIS" , 0,   2, "2d image");
	#ft_headappi(fits, "NAXIS" , 1, 256, "X axis is 256");
	#ft_headappi(fits, "NAXIS" , 2, 256, "Y axis is 256");

+
 */

#include "xos.h"
#include "fitsy.h"

/* Create or import a FITS header to Fitsy.

   This is the primary way in which FITSHead structures are created.

   If #cards is not NULL then then #acard cards are scanned from the memory
   in search of the END card.  When a memory block is passed in to #ft_headinit
   then it is considered ~unmanaged~ and will not be freed by #ft_headfree.

   If #cards is NULL, then #acards cards will be allocated for use as a FITSHead.
   If #acards is 0 the #FT_CARDS will be allocated (36).

	
 */
FITSHead ft_headinit(data, bytes)
	void		*data;	/* Pointer to FITS cards.	*/
	int		 bytes;		/* Number of bytes in buffer.	*/
{
		int		match;
		FITSHead	fits;
		FITSCard	end = NULL;
		FITSBuff	*cards;

	/* cast the namby-pamby void into a real pointer 
	 */
	cards = (FITSCard) data;

	Calloc(fits, sizeof(struct _FITSHead));
	fits->mem = FT_MALLOC;
	fits->primary = NULL;
	fits->filename = NULL;
	fits->rfcount = 1;

	if ( cards ) {
	    fits->cards = fits->last = cards;
	    fits->acard = fits->ncard = Abs(bytes/FT_CARDLEN);
	    if ( bytes > 0 )    fits->mem = FT_EXTERN;

	    if ( bytes > 0 && (end=ft_headfindseq(fits, "END", 0, &match)) ) {
		fits->ncard = end - fits->cards + 1;
		fits->acard = ((fits->ncard+FT_CARDS-1)/FT_CARDS) * FT_CARDS;
		fits->data  = fits->acard * FT_CARDLEN;
	    }
	} else {
	    if ( bytes == 0 ) bytes = FT_CARDS * FT_CARDLEN;

	    fits->acard = bytes/FT_CARDLEN ? bytes/FT_CARDLEN : FT_CARDS;
	    (void)Calloc(fits->cards, sizeof(FITSBuff) * fits->acard + 1);
	    /* make sure there is a null at the end (EGM) */
	    ((char *)fits->cards)[sizeof(FITSBuff) * fits->acard] = '\0';
	    ft_cardclr(fits->cards, fits->acard);
	    fits->ncard = 0;
	    fits->data  = fits->acard * FT_CARDLEN;
	}
	fits->index = NULL;
	fits->last  = fits->cards;

	if ( !cards || (bytes > 0 && !end) ) {
	    fits->ncard = 1;
	    ft_cardkey(fits->cards, "END", 0);
	} else {
	    if ( end ) {
		ft_syncdata(fits);
	    }
	}

	fits->sync  = 0;
	fits->cache = NULL;

	return fits;
}

/* Make a copy of a #FITSHead data structure.
 */
FITSHead ft_headcopy(fits)
	FITSHead	fits;
{
		FITSHead	head;

	if ( fits == NULL ) return ft_headinit(NULL, 0);

	Calloc(head, sizeof(struct _FITSHead));
	head->primary = NULL;
	head->rfcount = 1;

	if ( fits->primary )
	    ft_primlink(fits->primary, head);

	memmove(head, fits, sizeof(struct _FITSHead));

	(void)Calloc (head->cards, head->acard * FT_CARDLEN + 1);
	/* make sure there is a null at the end (EGM) */
	((char *)head->cards)[head->acard * FT_CARDLEN] = '\0';
	memmove(head->cards, fits->cards, head->acard * FT_CARDLEN);

	head->index = NULL;
	head->basic = NULL;
	head->image = NULL;
	head->table = NULL;
	head->mem   = FT_MALLOC;
	head->mmap_here  = 0;
	head->mmap_size  = 0;

	/* realloc filename string for the copy record */
	if( head->filename ) head->filename = strdup(head->filename);

	ft_syncdata(head);

	if( head->index && !fits->index ){
	  Free(head->index);
	  head->index = NULL;
	}

	return head;
}

/* Free a Fitsy FITS header data structure.
 */
void ft_headfree(fits, freecards)
	FITSHead	fits;
	int		freecards;	/* Free the FITS cards?		*/
{
	if ( fits == NULL )	return;

	if ( --fits->rfcount ) return;

	if ( fits->index )    Free(fits->index);
	if ( fits->filename ) Free(fits->filename);

	if ( fits->basic ) ft_basicfree(fits->basic);
	if ( fits->image ) ft_imagefree(fits->image);
	if ( fits->table ) ft_tablefree(fits->table);

	if ( fits->mem == FT_MALLOC && freecards ) Free(fits->cards);

#ifdef HAVE_SYS_MMAN_H
        if ( fits->mem == FT_MMAP  ) ft_munmap(fits);
#endif
#ifdef HAVE_SYS_SHM_H
        if ( fits->mem == FT_SHMAT ) ft_shmdt(fits);
#endif

	if ( fits->primary ) ft_headfree(fits->primary, 1);

	Free(fits);
}

void ft_primlink(prim, fits)
	FITSHead	prim;
	FITSHead	fits;
{
	if ( prim == NULL ) return;
	if ( fits == NULL ) return;

	fits->primary = prim;
	prim->rfcount++;
}

/* Set the card buffer
 */
void ft_setcards(fits, cards)
	FITSHead	 fits;
	void		*cards;
{
	fits->cards = (FITSCard) cards;
}

/* Make a copy of the header and force it to be a simple primary header.
 */
FITSHead ft_primary(fits)
	FITSHead	fits;
{
	FITSHead prim;
        int             one = 1;

    prim = ft_headcopy(fits);

    ft_cardfmt(ft_cardnth(prim, 1)
		, "SIMPLE", 0, FT_LOGICAL, &one, 0, FT_Comment);
 
    ft_headdel(prim, "EXTVER", 0);
    ft_headdel(prim, "GCOUNT", 0);
    ft_headdel(prim, "PCOUNT", 0);
    ft_headdel(prim, "EXTEND", 0);
    ft_headdel(prim, "NEXTEND", 0);
    ft_headdel(prim, "INHERIT", 0);

    return prim;
}

/* Merge cards from fits1 to fits2
 */
FITSHead ft_headmerge(fits1, fits2, copy)
	FITSHead	fits1;
	FITSHead	fits2;
	int		copy;
{
	FITSHead	merge;
	FITSCard	card;
	int		i, match;

    merge = ft_headcopy(fits1);

    if ( fits2 != NULL ) {
        i = 1;
	card = ft_cardnth(fits2, 1);
	if( !strcmp(card->c, "SIMPLE") || !strcmp(card->c, "XTENSION") )
	  i = 2;
	for ( ; i < ft_ncards(fits2); i++ ) {
	    /* The card is not in the merge header
	     */
	    if ( !(card = ft_cardfindseq(merge
				, ft_cardnth(fits2, i), &match)) ) {
		ft_cardapp(merge, ft_cardnth(fits2, i));
	    } else {
		if ( !match ) {
		    ft_cardins(merge, ft_cardnth(fits2, i), card);
		} else {
		    /* special cards always are inserted */
		    if( !strncmp(card->c, "HISTORY ",  8)  ||
			!strncmp(card->c, "CONTINUE ", 9)  ||
			!strncmp(card->c, "COMMENT ",  8)  ||
			!strncmp(card->c, "        ",  8)  )
		      ft_cardapp(merge, ft_cardnth(fits2, i));
		    else if( copy ) ft_cardcpy(card , ft_cardnth(fits2, i));
		}
	    }
	}

    }

    ft_syncdata(merge);

    return merge;
}
