/* hdfmkr8ani.c - make HDF raster 8 animation */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "df.h"
#ifdef MAC
#  include <CursorCtl.h>
#else
   extern char *malloc();
#  ifdef SUN
   extern free();
#  else
   extern void free(void*);
#  endif
#endif
extern int loadmat();

usage()
{
#	define pr(X) printf(X)
/*     -----------This line has 80 minus signs in it-----------------------------------*/
	pr("Usage: hdfmkr8ani [-SmTVHCRIt] [-o outfile] [-d n] file1 [file2 ..]\n");
	pr("Make a HDF raster-8 animation.\n");
	pr("Options:\n");
	pr("    -S read SDS Sequence from the first File argument\n");
	pr("    -m Manually set max and min\n");
	pr("    -T Transpose images\n");
	pr("    -V Vertical flip or reverse (before transposing if -T is given too)\n");
	pr("    -H Horizontal flip or reverse (before transposing)\n");
	pr("    -C,R,I Compression options: C (default, RLE), R (also RLE), I (IMCOMP)\n");
	pr("    -t write Tracing output to stderr\n");
	pr("    -o Output file, default out.hdf. Special char: '#' frame counter, start 1)\n");
	pr("    -d read nth Dataset, default 1, or dataset with label n (string)\n");
	pr("If -S is given, -d has no effect. Example: You have HDF files u0001.hdf thru\n");
	pr("u0100.hdf (100 files) with one 2D SDS in each. Then\n");
	pr("    hdfmkr8ani -o uani#.hdf u*.hdf  produces animation files uani0001.hdf, ...\n");
	pr("    hdfmkr8ani -o uani.hdf u*.hdf   produces a RIS8 sequence file uani.hdf\n");
	exit(-1);
}

static error(s) char *s; {
	fprintf(stderr,"*** Error: %s\n*** Call with no args for usage\n",s);
	exit(-1);
}
	
#define real double

typedef struct {int IsLabel; int n; char *label} DatasetStruct;
int Sflag=0, Vflag=0, Mflag=0, Tflag=0, Comp=0, VFlipFlag=0, HFlipFlag=0, Tracing=0;
char *Outname;
DatasetStruct Dataset = {0,1,""};
real min = 1.234E34, max = -1.23E32;

memerr() {
	fprintf(stderr,"*** Not enough memory!\n");
	exit(-1);
}

sizeerror(xdim1,ydim1,xdim,ydim) int32 xdim1,ydim1,xdim,ydim; {
	fprintf(stderr,"*** All images must be of the same size!\n");
	fprintf(stderr,"    One image is %ld x %ld, while it should be %ld x %ld\n",
			(long)xdim1,(long)ydim1,(long)xdim,(long)ydim);
	exit(-1);
}

not2d() {
	fprintf(stderr,"*** The scientific data must be two-dimensional.\n");
	exit(-1);
}

SeekDataset(fn,Dataset) char *fn; DatasetStruct Dataset; {
	/* Seek specific SDS in HDF file fn */
#	define LISTSIZE 500
#	define MAXLEN 16
	uint16 reflist[LISTSIZE],ref=0;
	char labellist[MAXLEN*LISTSIZE+1];
	int nlabels;
	nlabels = DFANlablist(fn,DFTAG_SDG, reflist,labellist, LISTSIZE, MAXLEN, 1);
	if (nlabels < 1) fprintf(stderr,"*** Warning: Error with DFANlablist\n");
	if (Dataset.IsLabel) {
		int i,found=0;
		for (i=0; i<nlabels; i++)
			if (!strcmp(labellist+i*MAXLEN,Dataset.label)) {
				ref = reflist[i];
				found = 1;
				break;
			}
		if (found) DFSDreadref(fn,ref); else
			fprintf(stderr,"*** Warning: Label %s not found in file %s\n",Dataset.label,fn);
	} else {
		int i;
		if (Dataset.n > nlabels)
			fprintf(stderr,"*** Warning: File %s has only %d SDGs, not %d\n",fn,nlabels,Dataset.n);
		else {ref = reflist[Dataset.n-1]; DFSDreadref(fn,ref);}
	}
} /* SeekDataset */

Process(inpname,Outname,FrameCounter)
char *inpname, *Outname; int FrameCounter;
/* Read next SDS from inpname, check that it's 2D, scale it using min,max,
   and add the resulting raster to Outname, interpreting possible '#'
   characters also. */
{
	int i,NN=0,INDEX=0,ispalette;
	char outname[256];	/* resulting output name */
	static int32 sizes[10],xdim=0,ydim=0;
	static float32 *data = 0;
	static unsigned char *image = 0;
	int rank;
	int32 x,y;
	float32 scaling;
	/* process Outname */
	for (i=0; Outname[i]; i++) if (Outname[i] == '#') {NN++; INDEX=i;}
	if (NN>1) {
		fprintf(stderr,"*** Output name %s should contain at most one '#'\n",Outname);
		exit(NN);
	}
	if (NN==0) strcpy(outname,Outname); else {
		int j = 0,k = 0;
		char s[20];
		for (i=0; i<INDEX; i++) outname[j++] = Outname[i];
		sprintf(s,"%.4d",FrameCounter);
		while (s[k]) outname[j++] = s[k++];
		for (i=INDEX+1; Outname[i]; i++) outname[j++] = Outname[i];
		outname[j] = '\0';
	}
	/* now outname contains the final output file name */
	if (Tracing) {fprintf(stderr,"%s >> ",inpname); fflush(stderr);}
	if (!Sflag && (Dataset.IsLabel || Dataset.n > 1)) SeekDataset(inpname,Dataset);
	DFSDgetdims(inpname,&rank,sizes,10);
	if (rank != 2) not2d();
	if (xdim != sizes[0] || ydim != sizes[1]) {
		if (data) free(data);
		data = (float32 *)malloc(sizeof(float32)*sizes[0]*sizes[1]);
		if (!data) memerr();
		if (image) free(image);
		image = (unsigned char *)malloc(sizeof(unsigned char)*sizes[0]*sizes[1]);
		if (!image) memerr();
		xdim = sizes[0]; ydim = sizes[1];
	}
	if (!Sflag && (Dataset.IsLabel || Dataset.n > 1)) SeekDataset(inpname,Dataset);
	DFSDgetdata(inpname,2,sizes,data);
	/* now data contains the float values read, make the raster next */
	if (min >= max) max = min+1;
	scaling = 252.0/(max-min);
	for (x=0; x<xdim; x++) for (y=0; y<ydim; y++) {
		float32 u;
		u = scaling*(data[x*ydim+y]-min);
		if (u < 0.) u = 0.; else if (u > 252.) u = 252.;
		if (Tflag)
			image[y*xdim+x] = 1+(unsigned char)u;
		else
			image[x*ydim+y] = 1+(unsigned char)u;
	}
	if (HFlipFlag) { /* Vertical == First dimension == y if no transpose */
		register unsigned char temp;
		if (Tflag) /* Flip x dimension */
			for (x=0; x<xdim/2; x++) for (y=0; y<ydim; y++) {
				temp = image[y*xdim+x];
				image[y*xdim+x] = image[y*xdim+(xdim-1-x)];
				image[y*xdim+(xdim-1-x)] = temp;
			}
		else /* Flip y dimension */
			for (x=0; x<xdim; x++) for (y=0; y<ydim/2; y++) {
				temp = image[x*ydim+y];
				image[x*ydim+y] = image[x*ydim+(ydim-1-y)];
				image[x*ydim+(ydim-1-y)] = temp;
			}
	}
	if (VFlipFlag) { /* Horizontal == Second dimension == x if no transpose */
		register unsigned char temp;
		if (Tflag) /* Flip y dimension */
			for (x=0; x<xdim; x++) for (y=0; y<ydim/2; y++) {
				temp = image[y*xdim+x];
				image[y*xdim+x] = image[(ydim-1-y)*xdim+x];
				image[(ydim-1-y)*xdim+x] = temp;
			}
		else /* Flip x dimension */
			for (x=0; x<xdim/2; x++) for (y=0; y<ydim; y++) {
				temp = image[x*ydim+y];
				image[x*ydim+y] = image[(xdim-1-x)*ydim+y];
				image[(xdim-1-x)*ydim+y] = temp;
			}
	}
	/* write it to output file */
	if (Tflag)
		DFR8addimage(outname,image,xdim,ydim,Comp);
	else
		DFR8addimage(outname,image,ydim,xdim,Comp);
	if (Tracing) fprintf(stderr,"%s\n",outname);
#	ifdef MAC
	SpinCursor(-32);
#	endif
}

scanminmax(fn,allflag)
char *fn; int allflag;
/* Scan HDF SDS file fn updating global variables min and max. If allflag
   is nonzero, read all datasets in the file. */
{
	static int32 sizes[10],xdim=0,ydim=0;
	static float32 *data = 0;
	int rank,i;
	int32 x,y;
	register real localmax = -1.234E35, localmin = 1.234E35,g;
	do {
		DFSDgetdims(fn,&rank,sizes,10);
		if (DFerror) break;
		if (rank != 2) not2d();
		if (xdim != sizes[0] || ydim != sizes[1]) {
			if (data) free(data);
			data = (float32 *)malloc(sizeof(float32)*sizes[0]*sizes[1]);
			if (!data) memerr();
			xdim = sizes[0]; ydim = sizes[1];
		}
		if (!Sflag && (Dataset.IsLabel || Dataset.n > 1)) SeekDataset(fn,Dataset);
		DFSDgetdata(fn,2,sizes,data);
		/* now data contains the float values read, scan min,max */
		for (x=0; x<xdim; x++) for (y=0; y<ydim; y++) {
			g = data[x*ydim+y];
			if (g > localmax) localmax = g;
			if (g < localmin) localmin = g;
		}
		if (localmin < min) min = localmin;
		if (localmax > max) max = localmax;
	}
	while (allflag && !DFerror);
}

static int oneof(c,s) int c; char *s; {
/* Returns nonzero if c is found in string s */
	int i;
	for (i=0; s[i]; i++) if (c == s[i]) return 1;
	return 0;
}

main(argc,argv)
int argc; char *argv[];
{
	int i,start=1,FrameCounter;
	float Min,Max;
#	ifdef MAC
	InitCursorCtl(0);
#	endif
	if (argc <= 1) usage();
	Outname = "out.hdf";
	for (start=1; start<argc && argv[start][0]=='-'; start++) {
		if (oneof(argv[start][1],"SmTVHCRIt")) {
			for (i=1; argv[start][i]; i++) {
				if (argv[start][i] == 'S') Sflag = 1; else
				if (argv[start][i] == 'm') Mflag = 1; else
				if (argv[start][i] == 'T') Tflag = 1; else
				if (argv[start][i] == 'V') VFlipFlag = 1; else
				if (argv[start][i] == 'H') HFlipFlag = 1; else
				if (argv[start][i] == 'C') Comp = DFTAG_RLE; else
				if (argv[start][i] == 'R') Comp = DFTAG_RLE; else
				if (argv[start][i] == 'I') Comp = DFTAG_IMCOMP; else
				if (argv[start][i] == 't') Tracing = 1; else
				fprintf(stderr,"** Unknown flag %c\n",argv[start][i]);
			}
		} else if (oneof(argv[start][1],"od")) {
			int ch; char *valstr;
			ch = argv[start][1];
			if (argv[start][2]) valstr = &argv[start][2]; else valstr = argv[++start];
			if (!valstr) error("Missing option value");
			/*printf("valstr='%s', argv[%d]='%s'\n",valstr,start,argv[start]);*/
			if (ch == 'o') Outname = valstr; else
			if (ch == 'd') {
				int p, IsLabel = 0;
				for (p=0; valstr[p]; p++) if (!isdigit(valstr[p])) IsLabel = 1;
				if (IsLabel) {
					Dataset.IsLabel = 1;
					Dataset.n = 1;
					Dataset.label = valstr;
				} else {
					sscanf(valstr,"%d",&Dataset.n);
					if (Dataset.n < 1 || Dataset.n > 10000) {
						fprintf(stderr,"** Warning: Bad -d option %d, unity set\n",Dataset.n);
						Dataset.n = 1;
					}
					Dataset.IsLabel = 0; Dataset.label = "";
				}
			}
		} else fprintf(stderr,"** Unknown option %s\n",argv[start]);
	}
	if (argc <= start) error("Not enough args specfied");
#	if 0
	if (argc <= start+1) usage();
	if (argv[start][0] == '-' && oneof(argv[start][1],"SmTCRI")) {
		for (i=1; argv[start][i]; i++) {
			if (argv[start][i] == 'S') Sflag = 1; else
			if (argv[start][i] == 'm') Mflag = 1; else
			if (argv[start][i] == 'T') Tflag = 1; else
			if (argv[start][i] == 'C') Comp = DFTAG_RLE; else
			if (argv[start][i] == 'R') Comp = DFTAG_RLE; else
			if (argv[start][i] == 'I') Comp = DFTAG_IMCOMP; else
			fprintf(stderr,"** Unknown flag %c\n",argv[start][i]);
		}
		start++;
	}
	if (argc <= start+1) usage();
	if (argv[start][0] == '-' && argv[start][1] == 'o') {
		start++;	/* pass -o */
		if (argc <= start+1) usage();
		Outname = argv[start];
		start++;	/* pass outname */
		if (argc <= start+1) usage();
	}
	if (argc <= start+1) usage();
	if (argv[start][0] == '-' && argv[start][1] == 'd') {
		int p, IsLabel = 0;
		start++;	/* pass -d */
		if (argc <= start+1) usage();
		for (p=0; argv[start][p]; p++) if (!isdigit(argv[start][p])) IsLabel = 1;
		if (IsLabel) {
			Dataset.IsLabel = 1;
			Dataset.n = 1;
			Dataset.label = argv[start];
		}
		else {
			sscanf(argv[start],"%d",&Dataset.n);
			if (Dataset.n < 1 || Dataset.n > 10000) {
				fprintf(stderr,"** Warning: Bad -d option %d, unity set\n",Dataset.n);
				Dataset.n = 1;
			}
			Dataset.IsLabel = 0; Dataset.label = "";
		}
		start++;
		if (argc <= start+1) usage();
	}
#	endif
	if (Sflag)
		scanminmax(argv[start],1);
	else
		for (i=start; i<argc; i++) scanminmax(argv[i],0);
	if (Mflag) {
		float newmin,newmax;
retry:
		printf("True min = %g, true max = %g, give Your choice:\n", (double)min,(double)max);
		printf("new min: "); fflush(stdout);
		scanf("%f",&newmin);
		printf("new max: "); fflush(stdout);
		scanf("%f",&newmax);
		if (newmin >= newmax) {
			printf("You gave minimum greater than maximum! Retry...\n");
			goto retry;
		}
		min = newmin; max = newmax;
	}
	fprintf(stderr,"min = %g, max = %g\n",(double)min,(double)max);
	DFSDrestart();
	if (Sflag) {
		for (FrameCounter=0; !DFerror; FrameCounter++)
			Process(argv[start],Outname,FrameCounter+1);
	} else {
		for (FrameCounter=0; start+FrameCounter<argc; FrameCounter++)
			Process(argv[start+FrameCounter],Outname,FrameCounter+1);
	}
	return 0;
}
