/*----------------------------------------------------------------------*\
 | Read	sea ice concentration files in HDF format (DOS + UNIX version).	|
\*----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "hdftags.h"

#ifdef DEC_ALPHA
	typedef short int16;
	typedef unsigned short uint16;
	typedef int int32;
	typedef unsigned int uint32;
#else
	typedef short int16;
	typedef unsigned short uint16;
	typedef long int32;
	typedef unsigned long uint32;
#endif

/*----------------------------------------------------------------------*\
 | Code to handle byte-ordering problems.								|
\*----------------------------------------------------------------------*/

enum byte_order_t {
	LSBfirst,
	MSBfirst
	};

static enum byte_order_t get_byte_order (void) {
	uint16 w = 255;
	unsigned char *s = (unsigned char *) &w;
	if (*s) return (LSBfirst);
	else return (MSBfirst);
	}

#ifndef ASM_SWAB

static void swab_word (void *p) {
	unsigned char *s,q;
	s = (unsigned char *) p;
	q = *s;
	*s = *(s+1);
	*(s+1) = q;
	}

static void swab_dword (void *p) {
	unsigned char *s,q;
	s = (unsigned char *) p;
	q = *s;
	*s = *(s+3);
	*(s+3) = q;
	s++;
	q = *s;
	*s = *(s+1);
	*(s+1) = q;
	}

#else

extern void swab_word (void *p);
extern void swab_dword (void *p);

#endif

/*----------------------------------------------------------------------*\
 | HDF run-length encoding: read										|
\*----------------------------------------------------------------------*/

static uint32 decode_HDF_rle (unsigned char *dst, unsigned char *src, long len) {
	int n;
	unsigned char *d,*s,*end;
	unsigned char c,q;

	d = dst;
	s = src;
	end = src + len;
	while (src < end) {

		/*--------------------------------------------------------------*\
		 | Get character count, check high bit.							|
		\*--------------------------------------------------------------*/

		q = (*src & 0x80);
		n = (int) (*src & 0x7F);
		src++;
		if (q) {

			/*----------------------------------------------------------*\
			 | If high bit is set, repeat next src byte n times.		|
			\*----------------------------------------------------------*/

			c = *src++;
			while (n-- > 0) *d++ = c;
			}
		else

			/*----------------------------------------------------------*\
			 | If high bit is clear, copy next n src bytes.				|
			\*----------------------------------------------------------*/

			while (n-- > 0) *d++ = *src++;
		}
	return (d - dst);
	}

/*----------------------------------------------------------------------*\
 | Create a string representation of an HDF tag name					|
\*----------------------------------------------------------------------*/

#ifdef DEBUG

struct tag_table {
	uint16 tag;
	char *name;
	};

static struct tag_table tag_table[] = {
	{DFTAG_WILDCARD,	"WILDCARD"},
	{DFTAG_NULL,		"NULL"},
	{DFTAG_LINKED,		"LINKED"},
	{DFTAG_VERSION,		"VERSION"},
	{DFTAG_FID,			"FID"},
	{DFTAG_FD,			"FD"},
	{DFTAG_TID,			"TID"},
	{DFTAG_TD,			"TD"},
	{DFTAG_DIL,			"DIL"},
	{DFTAG_DIA,			"DIA"},
	{DFTAG_NT,			"NT"},
	{DFTAG_MT,			"MT"},
	{DFTAG_ID8,			"ID8"},
	{DFTAG_IP8,			"IP8"},
	{DFTAG_RI8,			"RI8"},
	{DFTAG_CI8,			"CI8"},
	{DFTAG_II8,			"II8"},
	{DFTAG_ID,			"ID"},
	{DFTAG_LUT,			"LUT"},
	{DFTAG_RI,			"RI"},
	{DFTAG_CI,			"CI"},
	{DFTAG_RIG,			"RIG"},
	{DFTAG_LD,			"LD"},
	{DFTAG_MD,			"MD"},
	{DFTAG_MA,			"MA"},
	{DFTAG_CCN,			"CCN"},
	{DFTAG_CFM,			"CFM"},
	{DFTAG_AR,			"AR"},
	{DFTAG_DRAW,		"DRAW"},
	{DFTAG_RUN,			"RUN"},
	{DFTAG_XYP,			"XYP"},
	{DFTAG_MTO,			"MTO"},
	{DFTAG_T14,			"T14"},
	{DFTAG_T105,		"T105"},
	{DFTAG_SDG,			"SDG"},
	{DFTAG_SDD,			"SDD"},
	{DFTAG_SD,			"SD"},
	{DFTAG_SDS,			"SDS"},
	{DFTAG_SDL,			"SDL"},
	{DFTAG_SDU,			"SDU"},
	{DFTAG_SDF,			"SDF"},
	{DFTAG_SDM,			"SDM"},
	{DFTAG_SDC,			"SDC"},
	{DFTAG_SDT,			"SDT"},
	{DFTAG_SDLNK,		"SDLNK"},
	{DFTAG_NDG,			"NDG"},
	{DFTAG_CAL,			"CAL"},
	{DFTAG_FV,			"FV"},
	{DFTAG_BREQ,		"BREQ"},
	{DFTAG_EREQ,		"EREQ"},
	{DFTAG_VG,			"VG"},
	{DFTAG_VH,			"VH"},
	{DFTAG_VS,			"VS"},
	{0xFFFF,			"unknown tag"}
	};

static char *tag_name (unsigned short tag) {
	struct tag_table *t = tag_table;
	while (t->tag != 0xFFFF && t->tag != tag) t++;
	return (t->name);
	}

#endif	/* DEBUG */

/*----------------------------------------------------------------------*\
 | Read a sea ice concentration file in HDF format where the image may	|
 | or may not be compressed using modified run-length encoding.			|
\*----------------------------------------------------------------------*/

struct dd {
	uint16 tag;
	uint16 ref;
	uint32 offset;
	uint32 length;
	};

struct DFdi {
	uint16 tag;
	uint16 ref;
	};
typedef struct DFdi DFdi;

struct DFRdr {
	uint32 xdim;
	uint32 ydim;
	DFdi nt;
	uint16 ncomponents;
	uint16 interlace;
	DFdi compr;
	};
typedef struct DFRdr DFRdr;

int DFR8getimage (char *name, unsigned char *dst, long w, long h, unsigned char *pal) {
	int i;
	size_t size;
	FILE *in;
	uint32 signature;
	uint16 ndds;
	uint32 next;
	struct dd *dd;
	int ok = 0;
	size_t n;
	unsigned char *scratch;
	enum byte_order_t byte_order = get_byte_order();

	if (in = fopen (name,"rb")) {
		if (fread (&signature,4,1,in)) {
			if (byte_order == LSBfirst) swab_dword (&signature);
			if (signature == 0x0E031301) {
				do {
					fread (&ndds,2,1,in);
					if (byte_order == LSBfirst) swab_word (&ndds);
					fread (&next,4,1,in);
					if (byte_order == LSBfirst) swab_dword (&next);
					#ifdef DEBUG
					fprintf (stderr,"ndds = %hd; next = 0x%lX\n",ndds,next);
					#endif
					if (ndds) {
						size = (size_t) ndds * sizeof(struct dd);
						if (dd = (struct dd *) malloc (size)) {
							if (fread (dd,size,1,in)) {
								if (byte_order == LSBfirst)
									for (i=0; i < ndds; i++) {
										swab_word (&dd[i].tag);
										swab_word (&dd[i].ref);
										swab_dword (&dd[i].offset);
										swab_dword (&dd[i].length);
										}
								#ifdef DEBUG
								for (i=0; i < ndds; i++)
									fprintf (stderr,"%s (%hd) at 0x%lX has length %ld bytes\n",tag_name (dd[i].tag),dd[i].ref,dd[i].offset,dd[i].length);
								#endif
								for (i=0; i < ndds; i++)
									switch (dd[i].tag) {
										case DFTAG_RI:
											if (fseek (in,dd[i].offset,SEEK_SET) == 0) {
												if (dd[i].length <= w*h) {
													size = dd[i].length;
													if (n = fread (dst,1,size,in)) {
														if (n < dd[i].length) {
															fprintf (stderr,"Could not read raster image data\n");
															ok = 1;
															}
														}
													else
														fprintf (stderr,"Could not read raster image data\n");
													}
												else
													fprintf (stderr,"Image data is larger than expected\n");
												}
											else
												fprintf (stderr,"Improper offset in raster image data descriptor\n");
											break;
										case DFTAG_CI:
											if (fseek (in,dd[i].offset,SEEK_SET) == 0) {
												if (dd[i].length <= w*h) {
													size = dd[i].length;
													if (scratch = (unsigned char *) malloc (size)) {
														if (n = fread (scratch,1,size,in))
															if (n < dd[i].length) {
																fprintf (stderr,"Could not read compressed image data\n");
																ok = 1;
																}
															else
																decode_HDF_rle (dst,scratch,dd[i].length);
														else
															fprintf (stderr,"Could not read compressed image data\n");
														free (scratch);
														}
													else
														fprintf (stderr,"Could not allocate scratch space for image\n");
													}
												else
													fprintf (stderr,"Compressed image data is larger than expected\n");
												}
											else
												fprintf (stderr,"Improper offset in compressed image data descriptor\n");
											break;
										default:
											break;
										}
								}
							else
								fprintf (stderr,"Unable to read dd records\n");
							free (dd);
							}
						else
							fprintf (stderr,"Unable to allocate memory for dd array\n");
						}
					if (next) fseek (in,next,SEEK_SET);
					} while (next);
				}
			else
				fprintf (stderr,"Input file lacks HDF signature ^N^C^S^A\n");
			}
		else
			fprintf (stderr,"Input file exists but is empty\n");
		fclose (in);
		}
	return (ok);
	}

int DFR8getdims (char *name, long *width, long *height, short *haspalette) {
	int i;
	size_t size;
	FILE *in;
	uint32 signature;
	uint16 ndds;
	uint32 next;
	struct dd *dd;
	int ok = 1;
	DFRdr id;
	enum byte_order_t byte_order = get_byte_order();

	*haspalette = 0;	/* unimplemented feature */

	if (in = fopen (name,"rb")) {
		if (fread (&signature,4,1,in)) {
			if (byte_order == LSBfirst) swab_dword (&signature);
			if (signature == 0x0E031301) {
				do {
					fread (&ndds,2,1,in);
					if (byte_order == LSBfirst) swab_word (&ndds);
					fread (&next,4,1,in);
					if (byte_order == LSBfirst) swab_dword (&next);
					#ifdef DEBUG
					fprintf (stderr,"ndds = %hd; next = 0x%lX\n",ndds,next);
					#endif
					if (ndds) {
						size = (size_t) ndds * sizeof(struct dd);
						if (dd = (struct dd *) malloc (size)) {
							if (fread (dd,size,1,in)) {
								if (byte_order == LSBfirst)
									for (i=0; i < ndds; i++) {
										swab_word (&dd[i].tag);
										swab_word (&dd[i].ref);
										swab_dword (&dd[i].offset);
										swab_dword (&dd[i].length);
										}
								#ifdef DEBUG
								for (i=0; i < ndds; i++)
									fprintf (stderr,"%s (%hd) at 0x%lX has length %ld bytes\n",tag_name (dd[i].tag),dd[i].ref,dd[i].offset,dd[i].length);
								#endif
								for (i=0; i < ndds; i++)
									switch (dd[i].tag) {
										case DFTAG_ID:
											if (fseek (in,dd[i].offset,SEEK_SET) == 0) {
												size = dd[i].length;
												if (fread (&id,size,1,in)) {
													if (byte_order == LSBfirst) {
														swab_dword (&id.xdim);
														swab_dword (&id.ydim);
														}
													*width  = id.xdim;
													*height = id.ydim;
													ok = 0;
													}
												else
													fprintf (stderr,"Could not read image dimensions\n");
												}
											else
												fprintf (stderr,"Improper offset in image dimension descriptor\n");
											break;
										default:
											break;
										}
								}
							else
								fprintf (stderr,"Unable to read dd records\n");
							free (dd);
							}
						else
							fprintf (stderr,"Unable to allocate memory for dd array\n");
						}
					if (next) fseek (in,next,SEEK_SET);
					} while (next);
				}
			else
				fprintf (stderr,"Input file lacks HDF signature ^N^C^S^A\n");
			}
		else
			fprintf (stderr,"Input file exists but is empty\n");
		fclose (in);
		}
	return (ok);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/
