/* hdfr8tiles.c - "combine" HDF raster sequences vertically or horizontally */

#include <stdio.h>
#include <string.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: hdfr8tiles [-vCRI] [-o outfile] [-m margin] -i {files1} [-i {files2} ...]\n");
	pr("   or: hdfr8tiles -S [-vCRI] [-o outfile] [-m margin] file1 [file2 ..]\n");
	pr("Combine two or more HDF raster sequences to single HDF raster sequence.\n");
	pr("Options flags: -S process Sequence files, -v Vertically aligned output,\n");
	pr("    -C use default (RLE) Compression, -R use RLE (run length encoding),\n");
	pr("    -I use IMCOMP (warning: not 100 %% faithful method), -o Output file\n");
	pr("    (default out.hdf), -i delimites Input file sequences (when no -S)\n");
	pr("Special characters: # in Output file name is the frame counter, starting\n");
	pr("from 1. Precede '#' with a backslash when using the Bourne shell!\n\n");
	pr("Example 1: Given files n1.hdf, n2.hdf, n3.hdf, u1.hdf, u2.hdf and u3.hdf\n");
	pr("in current directory, each containing one RIS8, then\n");
	pr("  hdfr8tiles -o nu#.hdf -i n*.hdf -i u*.hdf\n");
	pr("makes files nu1.hdf, nu2.hdf and nu3.hdf, each containing one RIS8 which\n");
	pr("displays the n and u rasters together. If you leave off #, then the output\n");
	pr("file is nu.hdf (an image sequence file)\n\n");
	pr("Example 2: If the sequence files n.hdf and u.hdf contain 3 images each,\n");
	pr("  hdfr8tiles -S -o nu#.hdf n.hdf u.hdf\n");
	pr("produces the same output as Example 1.\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

int Sflag=0, Vflag=0, Comp=0;
int32 margin=0;
char *Outname;
#define MAX 10
char *SeqFiles[MAX];	/* table to hold sequence file names (max. 10)	*/
char **SeqPointers[MAX];/* table to hold pointers to argv (max. 10)		*/
char *InpFiles[MAX];
int32 Xcorner[MAX],Ycorner[MAX];
int N = 0;				/* number of sequence files names (max. 10)		*/
#define LISTSIZE 1000
uint16 reflist[MAX*LISTSIZE+1];
int Counters[MAX], NFrames[MAX];

toomany() {
	fprintf(stderr,"*** The maximum number of combined sequences is %d\n",MAX);
	exit(N);
}

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);
}

noteq() {
	static int called = 0;
	if (!called) {
		fprintf(stderr,"*** Warning: Unequal number of frames in input files\n");
		called = 1;
	}
}

ProcessBunch(FrameCounter)
int FrameCounter;
/* Input file names are in InpFiles[0] .. InpFiles[N-1]. Output file name is
   Outname. Vflag affects the processing. If Outname contains '#', the 
   value of FrameCounter is substituted for it. */
{
	int i,NN=0,INDEX=0,ispalette;
	static int CalledBefore = 0;
	char outname[256];	/* resulting output name */
	static int32 xdim,ydim,Xdim,Ydim,x,y;
	static unsigned char *theRaster = 0, *theImage = 0;
	int32 xmargin,ymargin;
	/* 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 */
	/* find out the dimensions of the first file in the bunch, if this routine is
	   called for the first time. These dimensions are then used throughout
	   the program */
	if (!CalledBefore) {
		DFR8getdims(InpFiles[0],&xdim,&ydim,&ispalette);
		DFR8restart();
		CalledBefore = 1;
		xmargin = ymargin = margin;
		if (!Vflag) {
			Xdim = N*xdim + (N+1)*xmargin;
			Ydim = ydim + 2*ymargin;
			for (i=0; i<N; i++) {
				Xcorner[i] = xmargin+i*(xdim+xmargin);
				Ycorner[i] = ymargin;
			}
		} else {
			Xdim = xdim + 2*xmargin;
			Ydim = N*ydim + (N+1)*ymargin;
			for (i=0; i<N; i++) {
				Xcorner[i] = xmargin;
				Ycorner[i] = ymargin+i*(ydim+ymargin);
			}
		}
		theRaster = (unsigned char *)malloc(sizeof(unsigned char)*Xdim*Ydim);
		if (!theRaster) memerr();
		theImage = (unsigned char *)malloc(sizeof(unsigned char)*xdim*ydim);
		if (!theImage) memerr();
	}	/* end of initialization stuff */
	/* reset theRaster */
	for (x=0; x<Xdim; x++) for (y=0; y<Ydim; y++)
		theRaster[x*Ydim+y] = (unsigned char)254;	/* Black is black */
	/* loop over the input bunch, writing into theRaster at the same time */
	for (i=0; i<N; i++) {
		int32 xdim1,ydim1;
		if (Sflag) {
			DFR8readref(InpFiles[i],reflist[i*LISTSIZE+Counters[i]]);
			Counters[i]++;
		}
		DFR8getdims(InpFiles[i],&xdim1,&ydim1,&ispalette);
		if (xdim1 != xdim || ydim1 != ydim) sizeerror(xdim1,ydim1,xdim,ydim);
#		ifdef MAC
		SpinCursor(-32);
#		endif
		DFR8getimage(InpFiles[i],theImage,xdim,ydim,(unsigned char *)0);
		for (x=0; x<xdim; x++) for (y=0; y<ydim; y++)
			theRaster[(Ycorner[i]+y)*Xdim+Xcorner[i]+x] = theImage[y*xdim+x];
	}
	/* write theRaster into outname */
#	ifdef MAC
	SpinCursor(-32);
#	endif
	DFR8addimage(outname,theRaster,Xdim,Ydim,Comp);
}

static getrefs(i) int i; {
	char labellist[12*LISTSIZE+1];
	int nlabels;
	nlabels = DFANlablist(InpFiles[i],DFTAG_RIG,reflist+i*LISTSIZE,labellist,LISTSIZE,12,1);
	if (nlabels < 0) {fprintf(stderr,"*** Error with DFANlablist\n"); exit(nlabels);}
	NFrames[i] = nlabels;
}

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]=='-' && argv[start][1]!='i'; start++) {
		if (oneof(argv[start][1],"SvCRI")) {
			for (i=1; argv[start][i]; i++) {
				int ch;
				ch = argv[start][i];
				if (ch == 'S') Sflag=1; else
				if (ch == 'v' || ch == 'V') Vflag=1; else
				if (ch == 'C') Comp = DFTAG_RLE; else
				if (ch == 'R') Comp = DFTAG_RLE; else
				if (ch == 'I') Comp = DFTAG_IMCOMP; else
				fprintf(stderr,"**Unknown switch %c\n",ch);
			}
		} else if (oneof(argv[start][1],"om")) {
			int ch; char *valstr;
			ch = argv[start][1];
			valstr = argv[start][2] ? argv[start]+2 : argv[++start];
			if (!valstr) error("Missing option value");
			if (ch == 'o') Outname = valstr; else
			if (ch == 'm') {
				int marg;
				sscanf(valstr,"%d",&marg);
				if (marg < 0) marg = 0; if (marg > 100) marg = 100;
				margin = marg;
			}
		} else fprintf(stderr,"** Unknown arg %s\n",argv[start]);
	}
	if (argc <= start+1) error("Not enough args specified");
#	if 0
	if (argc <= start+1) usage();
	if (argv[start][0] == '-' && argv[start][1] == 'S') {Sflag=1; start++;}
	if (argc <= start+1) usage();
	if (argv[start][0] == '-' && oneof(argv[start][1],"vCRI")) {
		int i;
		for (i=1; argv[start][i]; i++) {
			int ch;
			ch = argv[start][i];
			if (ch == 'v' || ch == 'V') Vflag=1; else
			if (ch == 'C') Comp = DFTAG_RLE; else
			if (ch == 'R') Comp = DFTAG_RLE; else
			if (ch == 'I') Comp = DFTAG_IMCOMP; else
			fprintf(stderr,"**Unknown switch %c\n",ch);
		}
		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 (argv[start][0] == '-' && argv[start][1] == 'm') {
		int marg;
		start++;	/* pass -m */
		if (argc <= start+1) usage();
		sscanf(argv[start],"%d",&marg);
		if (marg < 0) marg = 0; if (marg > 100) marg = 100;
		margin = marg;
		start++;
		if (argc <= start+1) usage();
	}
#	endif
	if (Sflag)
		for (;start<argc; start++) {	/* scan all remaining args */
			if (N >= MAX) toomany();
			SeqFiles[N] = argv[start];
			N++;
		}
	else
		for (;start<argc; start++) {	/* scan all remaining args for '-i' */
			if (N >= MAX) toomany();
			if (argv[start][0] == '-' && argv[start][1] == 'i') {
				SeqPointers[N] = argv + start + 1;	/* Mark argv position into table */
				N++;
			}
		}
	if (N<=0) error("No -i options specified");
	if (Sflag) {
		int minframes;
		for (i=0; i<N; i++) {
			InpFiles[i] = SeqFiles[i];
			getrefs(i);
			Counters[i] = 0;
		}
		minframes = NFrames[0];
		for (i=1; i<N; i++) {
			if (NFrames[i]!=NFrames[0]) noteq();
			if (NFrames[i]<minframes) minframes = NFrames[i];
		}
		for (i=0; i<N; i++) NFrames[i] = minframes;
		for (FrameCounter=0; !DFerror && FrameCounter<NFrames[0]; FrameCounter++)
			ProcessBunch(FrameCounter+1);
	}
	else
		for (FrameCounter=0; SeqPointers[N-1][FrameCounter]; FrameCounter++) {
			for (i=0; i<N; i++)
				InpFiles[i] = SeqPointers[i][FrameCounter];
			ProcessBunch(FrameCounter+1);
		}
	return 0;
}
