/* hehdf.c -- interface to hdf for HDFedit */
#include "he.h"

int he_rdonly = 0;
int he_currDesc;
int he_numDesc;
int he_numGrp;
int he_backup;
char *he_file;
DFdesc he_desc[HE_DESC_SZ];
HE_GROUP he_grp[HE_DESC_SZ];
	
int32 getElement(desc, pdata)
    int desc;
    char **pdata;
{
    int32 length;
    DF *df;

    length = he_desc[desc].length;

    /* alloc memory to read the element in */
    *pdata = (char *) malloc(length);
    if (*pdata == NULL) return HE_FAIL;

    /* read in the element and check for error */
    if ((df = DFopen(he_file, DFACC_READ, 0)) == NULL)
    {
	hdfErr();
	return HE_FAIL;
    }
    if (DFgetelement(df, he_desc[desc].tag, he_desc[desc].ref, *pdata) < 0)
    {
	free(*pdata);
	fprintf(stderr, "Cannot read element.\n");
	return HE_FAIL;
    }
    DFclose(df);
    return length;
}

int od(format, file)
    char *format;
    char *file;
{
    /* fork a child and let the child run od */
    if (fork() == 0)
    {
	/* this is the child */
	if (execl("/bin/od", "od", format, file, 0) == -1)
	    fprintf(stderr, "Error while executing od.\n");

	/* return control to the parent */
	exit();
    }

    /* the parent waits for the child to die */
    wait(0);

    /* this is a bug because it always returns OK, will expand this as
       soon as the status return mechanism from wait is understood */
    return HE_OK;
}

/* the tmp directory, currently set for unix */
#define TDIR "/tmp/"

int getTmpName(pname)
    char **pname;
{
    int length;
    static int count = 0;
    char s[32];

    (void) sprintf(s, "%she%d.%d", TDIR, getpid(), count);
    count++;

    length = strlen(s);
    if (length <= 0) return HE_FAIL;

    *pname = (char *) malloc(length+1);
    strcpy(*pname, s);

    return length;
}

int writeToFile(file, data, length)
    char *file;
    char *data;
    int length;
{
    FILE *fd;
    int written;

    fd = fopen(file, "w");
    if (fd == NULL) return HE_FAIL;

    written = fwrite(data, sizeof(char), length, fd);
    if (written != length)
    {
	fprintf(stderr,"Error in write.\n");
	return HE_FAIL;
    }
    fclose(fd);

    return HE_OK;
}

int removeFile(file)
    char *file;
{
    return unlink(file);
}

/* is a file currently opened */
int fileOpen()
{
    return (he_file != NULL);
}

char *backupName(file)
    char *file;
{
    return catStr("$",file);
}

int backupFile(file)
    char *file;
{
    char *back;			/* backup file name */

    back = backupName(file);
    return copyFile(file, back);
}

int copyFile(from, to)
    char *from, *to;
{
    int read;
    char buf[HE_BUF_SZ];	/* copying buffer */
    FILE *fp;
    FILE *bfp;

    /* open the hdf file for backing up */
    if ((fp = fopen(from, "r")) == NULL)
    {
	fprintf(stderr,"Unable to open file: %s.\n",from);
	return HE_FAIL;
    }
    if ((bfp = fopen(to, "w")) == NULL)
    {
	fclose(fp);
	fprintf(stderr,"Unable to open backup file.\n");
	return HE_FAIL;
    }
    /* copy the contents from hdf file to backup file */
    while ((read = fread(buf, 1, HE_BUF_SZ, fp)) > 0)
	fwrite(buf, 1, read, bfp);

    fclose(fp);
    fclose(bfp);

    return HE_OK;
}

int updateDesc()
{
    DF *df;
    register int i, j;

    if (((df = DFopen(he_file, DFACC_READ, 0)) == NULL) ||
	((he_numDesc = DFdescriptors(df, he_desc, 0, HE_DESC_SZ)) < 0))
    {
	hdfErr();
	return HE_FAIL;
    }

    /* get informations about the groups */
    he_numGrp = 0;
    for (i = 0; i < he_numDesc; i++)
    {
	if (isGrp(he_desc[i].tag))
	{
	    he_grp[he_numGrp].desc = i;
	    he_grp[he_numGrp].size = he_desc[i].length / sizeof(DFdi);

	    he_grp[he_numGrp].ddList = (DFdi *) malloc(he_desc[i].length);
	    if (!he_grp[he_numGrp].ddList)
	    {
		fprintf(stderr, "Out of memory. Closing file.\n");
		closeFile(1);	/* keep the backup */
		return HE_FAIL;
	    }
	    if (DFdiread(df, he_desc[i].tag, he_desc[i].ref) < 0)
	    {
		hdfErr();
		return HE_FAIL;
	    }
	    for (j = 0; j < he_grp[he_numGrp].size; j++)
		DFdiget(he_grp[he_numGrp].ddList + j);

	    he_numGrp++;
	}
    }
    DFclose(df);
    return HE_OK;
}

int initFile(file)
    char *file;
{
    if (he_file) free(he_file);
    he_file = copyStr(file);

    if (updateDesc() < 0)
	return HE_FAIL;

    /* if there are groups in this file, go to the first group tag */
    /* otherwise, just go to the first element */
    if (he_numGrp > 0)
	he_currDesc = he_grp[0].desc;
    else
	he_currDesc = 0;

    return resetPred();
}

int closeFile(keep)
    int keep;
{
    register int i;
    char *back;

    if (!fileOpen())
    {
	fprintf(stderr, "No open file to close.\n");
	return HE_FAIL;
    }
    /* free some dynamic storages */
    if (he_backup && !keep)
    {
	back = backupName(he_file);
	(void) removeFile(back);
	free(back);
    }
    free(he_file);
    he_file = NULL;
    for (i = 0; i < he_numGrp; i++)
	free(he_grp[i].ddList);

    return HE_OK;
}

DFdesc *currDesc()
{
    return (DFdesc *) (he_desc + he_currDesc);
}

int getR8(xdim, ydim, image, pal, comp)
    int xdim, ydim;
    char *image;
    char *pal;
    int comp;
{
    FILE *fp;
    int32 length;
    char *buf;

    if (!fileOpen())
    {
	noFile();
	return HE_FAIL;
    }
    if (pal) 
	if (setPal(pal) < 0)
	    /* Error already signalled by setPal */
	    return HE_FAIL;

    length = xdim * ydim;
    buf = (char *) malloc(length);

    if ((fp = fopen(image, "r")) == NULL)
    {
	fprintf(stderr,"Error opening image file: %s.\n", image);
	return HE_FAIL;
    }
    if (fread(buf, xdim, ydim, fp) < ydim)
    {
	fprintf(stderr,"Error reading image file: %s.\n", image);
	return HE_FAIL;
    }
    fclose(fp);

    if (DFR8addimage(he_file, buf, xdim, ydim, comp) < 0)
    {
	hdfErr();
	return HE_FAIL;
    }
    free(buf);

    if (updateDesc() < 0)
	return HE_FAIL;

    return HE_OK;
}

int setPal(pal)
    char *pal;
{
    FILE *fp;
    char reds[HE_COLOR_SZ], greens[HE_COLOR_SZ], blues[HE_COLOR_SZ];
    char palette[HE_PALETTE_SZ];
    register char *p;
    register int i;

    if ((fp = fopen(pal, "r")) == NULL)
    {
	fprintf(stderr,"Error opening palette file: %s.\n", pal);
	return HE_FAIL;
    }
    if (fread(reds, 1, HE_COLOR_SZ, fp) < HE_COLOR_SZ ||
	fread(greens, 1, HE_COLOR_SZ, fp) < HE_COLOR_SZ ||
	fread(blues, 1, HE_COLOR_SZ, fp) < HE_COLOR_SZ)
    {
	fprintf(stderr, "Error reading palette file: %s.\n", pal);
	return HE_FAIL;
    }

    /* convert sun palette to hdf palette */
    p = palette;
    for (i = 0; i < HE_COLOR_SZ; i++)
    {
	*p++ = reds[i];
	*p++ = greens[i];
	*p++ = blues[i];
    }

    if (DFR8setpalette(palette) < 0)
    {
	fputs("Error setting palette.\n",stderr);
	return HE_FAIL;
    }

    return HE_OK;
}

uint16 currTag()
{
    return he_desc[he_currDesc].tag;
}

int currGrpNo()
{
    return desc2Grp(he_currDesc);
}

int findDesc(dd)
    DFdi *dd;
{
    register int i;

    for (i = 0; i < he_numDesc; i++)
	if ((he_desc[i].tag == dd->tag) && (he_desc[i].ref == dd->ref))
	    return i;

    return HE_FAIL;
}

int desc2Grp(desc)
    int desc;
{
    register int i;

    for (i = 0; i < he_numGrp; i++)
	if (he_grp[i].desc == desc) return i;

    NOT_REACHED();
    return HE_FAIL;
}

int hasReference(desc)
    int desc;
{
    register int i, j;

    for (i = 0; i < he_numGrp; i++)
	for (j = 0; j < he_grp[i].size ; j++)
	    if (he_grp[i].ddList[j].tag == he_desc[desc].tag &&
		he_grp[i].ddList[j].ref == he_desc[desc].ref)
		return YES;
    return NO;
}

int deleteDesc(desc)
    int desc;
{
    DF *df;

    if ((df = DFopen(he_file, DFACC_WRITE, 0)) == NULL)
    {
	hdfErr();
	return HE_FAIL;
    }
    if (DFdel(df, he_desc[desc].tag, he_desc[desc].ref) < 0 ||
	DFupdate(df) < 0)
    {
	hdfErr();
	return HE_FAIL;
    }
    return DFclose(df);
}

int getCurrRig(pXdim, pYdim, pPalette, pRaster)
    int *pXdim, *pYdim;
    char **pPalette;
    char **pRaster;
{
    int ispal;

    goTo(he_currDesc);

    if (DFR8getdims(he_file, pXdim, pYdim, &ispal)<0)
    {
	fprintf(stderr, "Error getting image group.\n");
	hdfErr();
	return HE_FAIL;
    }
    if (ispal)
	*pPalette = (char *) malloc(HE_PALETTE_SZ);
    else
	*pPalette = (char *) NULL;
    *pRaster = (char *) malloc((*pXdim) * (*pYdim));

    if (DFR8getimage(he_file, *pRaster, *pXdim, *pYdim, *pPalette) < 0) 
    {
	fprintf(stderr, "Error getting image group.\n");
	hdfErr();
	return HE_FAIL;
    }

    return HE_OK;
}

int putWithTempl(template, n1, n2, n3, data, length, verbose)
    char *template;
    int n1, n2, n3;
    char *data;
    int length;
    int verbose;
{
    char *file;
    int ret;

    convertTemplate(template, n1, n2, n3, &file);
    if (verbose)
	printf("Writing to file: %s\n", file);
    ret = writeToFile(file, data, length);
    free(file);

    return ret;
}

int revert()
{
    char *back;

    back = backupName(he_file);
    if (copyFile(back, he_file) < 0)
	return HE_FAIL;
    return initFile(he_file);
}

int writeElt(file, ref, elt)
    char *file;
    uint16 ref;
    int elt;
{
    int ret;
    char *data;
    int32 eltLength;
    char *p;
    uint16 rank;
    register int i;
    uint16 ntTag;
    uint16 ntRef;
    DFdi *ntDesc;
    int nt;

    eltLength = getElement(elt, &data);
    if (eltLength <= 0)
    {
	fprintf(stderr,"Cannot get element: tag %d ref %d.\n",
		he_desc[elt].tag, he_desc[elt].ref);
	return HE_FAIL;
    }
    /* special case */
    if (he_desc[elt].tag == DFTAG_SDD)
    {
	/* lots of hack here */
	/* assume that the number types are of the same ref as this elt */
	p = data;

	/* get the rank */
	/* NOTE: UINT16READ and UINT16WRITE advances p */
	UINT16READ(p, rank);
	/* move to the NT of the data */
	p += (rank * 4);
	UINT16READ(p, ntTag);
	UINT16READ(p, ntRef);

	/* set up to write the number type element */
	ntDesc = (DFdi *) malloc(sizeof(DFdi));
	ntDesc->tag = ntTag;
	ntDesc->ref = ntRef;
	nt = findDesc(ntDesc);
	free(ntDesc);
	writeElt(file, ref, nt);

	p -= 2;
	UINT16WRITE(p, ref);
	/* do the NT of scales */
	for (i = 0; i < rank; i++)
	{
	    p += 2;
	    UINT16WRITE(p, ref);
	}
    }
    ret = putElement(file, he_desc[elt].tag, ref, data, eltLength);
    free(data);
    return ret;
}

int putElement(file, tag, ref, data, len)
    char *file;
    uint16 tag;
    uint16 ref;
    char *data;
    int32 len;
{
    int ret;
    DF *df;

    if ((df = DFopen(file, DFACC_READ | DFACC_WRITE, 0)) == NULL)
	/* a little tricky here */
	if (DFerror != DFE_FNF || (df = DFopen(file, DFACC_ALL, 0)) == NULL)
	{
	    hdfErr();
	    return HE_FAIL;
	}
    if ((ret = DFputelement(df, tag, ref, data, len)) < 0)
    {
	hdfErr();
	return ret;
    }
    return DFclose(df);
}

int writeGrp(file)
    char *file;
{
    register int i;
    uint16 ref;
    int grp;
    int elt;
    int ret;
    DF *df;

    getNewRef(file, &ref);

    grp = currGrpNo();
    DFdisetup(he_grp[grp].size);
    for (i = 0; i < he_grp[grp].size; i++)
    {
	elt = findDesc(he_grp[grp].ddList + i);
	if (elt >= 0)
	    writeElt(file, ref, elt);
	/* update the group dd list */
	DFdiput(he_grp[grp].ddList[i].tag, ref);
    }
    /* do the group now */

    if ((df = DFopen(file, DFACC_READ | DFACC_WRITE, 0)) == NULL)
    {
	hdfErr();
	return HE_FAIL;
    }
    if ((ret = DFdiwrite(df, currTag(), ref)) < 0)
    {
	hdfErr();
	return ret;
    }
    return DFclose(df);
}

int getNewRef(file, pRef)
    char *file;
    uint16 *pRef;
{
    DF *df;

    if ((df = DFopen(file, DFACC_READ | DFACC_WRITE, 0)) == NULL)
	/* a little tricky here */
	if (DFerror != DFE_FNF || (df = DFopen(file, DFACC_ALL, 0)) == NULL)
	{
	    hdfErr();
	    return HE_FAIL;
	}
    *pRef = DFnewref(df, 0);
    return DFclose(df);
}

int writeAnnot(file, tag, ref)
    char *file;
    uint16 tag;
    uint16 ref;
{
    char *data;
    int32 eltLength;
    char *p;
    uint16 newRef;

    while (tag == 0)
    {
	printf("Attach to what tag? (> 0)");
	scanf("%u", tag);
    }
    while (ref == 0)
    {
	printf("Attach to what ref? (> 0)");
	scanf("%u", ref);
    }
    eltLength = getElement(he_currDesc, &data);
    if (eltLength <= 0)
    {
	fprintf(stderr,"Cannot get element: tag %d ref %d.\n",
		he_desc[he_currDesc].tag, he_desc[he_currDesc].ref);
	return HE_FAIL;
    }
    p = data;
    UINT16WRITE(p, tag);
    UINT16WRITE(p, ref);

    if (getNewRef(file, &newRef) < 0)
    {
	fprintf(stderr,"Error getting new ref number.\n");
	return HE_FAIL;
    }

    return putElement(file, he_desc[he_currDesc].tag, newRef, data, eltLength);
}

int32 getAnn(ann, tag, ref, pBuf)
    int ann;
    uint16 tag;
    uint16 ref;
    char **pBuf;
{
    int32 len;

    if (ann == HE_LABEL)
    {
	len = DFANgetlablen(he_file, tag, ref);
	if (len > 0)
	{
	    *pBuf = (char *) malloc(len+1);
	    DFANgetlabel(he_file, tag, ref, *pBuf, len+1);
	}
	else
	    *pBuf = NULL;
    }
    else
    {
	len = DFANgetdesclen(he_file, tag, ref);
	if (len > 0)
	{
	    *pBuf = (char *) malloc(len);
	    DFANgetdesc(he_file, tag, ref, *pBuf, len);
	}
	else
	    *pBuf = NULL;
    }
    return len;
}

int putAnn(ann, tag, ref, buf, len)
    int ann;
    uint16 tag;
    uint16 ref;
    char *buf;
    int32 len;
{
    int ret;

    if (ann == HE_LABEL)
	ret = DFANputlabel(he_file, tag, ref, buf);
    else
	ret = DFANputdesc(he_file, tag, ref, buf, len);
    if (ret < 0)
	hdfErr();

    return ret;
}

int32 readFromFile(file, pBuf)
    char *file;
    char **pBuf;
{
    FILE *fp;
    int32 soFar;
    int32 bufLen;
    int32 read;

    fp = fopen(file, "r");
    if (fp == NULL) return HE_FAIL;

    soFar = 0;
    bufLen = 0;
    for (read = HE_BUF_SZ; read == HE_BUF_SZ; soFar += read)
    {
	bufLen += HE_BUF_SZ;
	if (bufLen == HE_BUF_SZ)
	    *pBuf = (char *) malloc(bufLen);
	else
	    *pBuf = (char *) realloc(*pBuf, bufLen);
	if (*pBuf == NULL) return HE_FAIL;

	read = fread((*pBuf)+soFar, 1, HE_BUF_SZ, fp);
    }
    *pBuf = (char *) realloc(*pBuf, soFar + 1);
    (*pBuf)[soFar] = '\0';
    fclose(fp);
    return soFar;
}

/* end of hehdf.c */
