/*----------------------------------------------------------------------*\
 | Program to create a portable pixmap from an unsigned 16-bit image.	|
 |																		|
 | Peter N. Schweitzer (U.S. Geological Survey, Reston, VA 22092)		|
\*----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXVAL	255

/*----------------------------------------------------------------------*\
 | Explanation of value_color structure:								|
 |																		|
 | top is intended as the upper limit of the bin, but is actually the	|
 | lower limit of the following bin.									|
 |																		|
 | r,g,b: Color of this bin; all values less than bin[i].top but greater|
 | than or equal to bin[i-1].top will have this color.					|
\*----------------------------------------------------------------------*/

struct value_color {
	unsigned short top;
	unsigned char r;
	unsigned char g;
	unsigned char b;
	};

struct value_color default_bins[] = {
	{    0,0x00,0xff,0xff},
	{   10,0x00,0xff,0x00},
	{   20,0x10,0xff,0x00},
	{   30,0x20,0xff,0x00},
	{   40,0x30,0xff,0x00},
	{   50,0x40,0xff,0x00},
	{   60,0x50,0xff,0x00},
	{   70,0x60,0xff,0x00},
	{   80,0x70,0xff,0x00},
	{   90,0x80,0xff,0x00},
	{  100,0x90,0xff,0x00},
	{  110,0xa0,0xff,0x00},
	{  120,0xb0,0xff,0x00},
	{  130,0xc0,0xff,0x00},
	{  140,0xd0,0xff,0x00},
	{  150,0xe0,0xff,0x00},
	{  160,0xf0,0xff,0x00},
	{  170,0xff,0xff,0x00},
	{  180,0xff,0xf0,0x00},
	{  190,0xff,0xe0,0x00},
	{  200,0xff,0xd0,0x00},
	{  300,0xff,0xc0,0x00},
	{  400,0xff,0xb0,0x00},
	{  500,0xff,0xa0,0x00},
	{  600,0xff,0x90,0x00},
	{  700,0xff,0x80,0x00},
	{  800,0xff,0x70,0x00},
	{  900,0xff,0x60,0x00},
	{ 1000,0xff,0x50,0x00},
	{ 2000,0xff,0x40,0x00},
	{ 3000,0xff,0x30,0x00},
	{ 4000,0xff,0x20,0x00},
	{ 5000,0xff,0x10,0x00},
	{ 6000,0xff,0x00,0x00},
	{65535,0xff,0x00,0x00},
	};

enum byte_order_t {
	LSBfirst,
	MSBfirst
	};

static enum byte_order_t get_byte_order (void) {
	short w = 255;
	unsigned char *s = (unsigned char *) &w;
	if (*s) return (LSBfirst);
	else return (MSBfirst);
	}

main (int argc, char *argv[]) {
	int i,j;
	unsigned short nb;
	int x,y;
	FILE *in, *out;
	char input_file [FILENAME_MAX];
	char output_file [FILENAME_MAX];
	char bin_file [FILENAME_MAX];
	char string[128];
	int width,height;
	char *s;
	int verbose = 0;
	enum byte_order_t machine_byte_order, image_byte_order;
	int swap_bytes = 0;

	long k,n;
	unsigned short *value = NULL;
	char *scratch = NULL;
	struct value_color *bin;

	if (argc == 1) {
		fprintf (stderr,"Usage: %s input_file -w width -h height [-byte LSBfirst|MSBfirst] [-bins bin_file] [-o output_file]\n",argv[0]);
		exit (0);
		}

	width = height = 0;

	*input_file = 0;
	*bin_file = 0;
	*output_file = 0;
	machine_byte_order = get_byte_order();

	for (i=1; i < argc; i++)
		if (memcmp (argv[i],"-bins",5) == 0) {
			i++;
			strcpy (bin_file,argv[i]);
			}
		else
			if (memcmp (argv[i],"-o",2) == 0) {
				i++;
				strcpy (output_file,argv[i]);
				}
			else
				if (memcmp (argv[i],"-w",2) == 0) {
					i++;
					j = atoi (argv[i]);
					if (j > 0) width = j;
					else {
						fprintf (stderr,"Error: width must be a positive integer\n");
						exit (1);
						}
					}
				else
					if (memcmp (argv[i],"-h",2) == 0) {
						i++;
						j = atoi (argv[i]);
						if (j > 0) height = j;
						else {
							fprintf (stderr,"Error: height must be a positive integer\n");
							exit (1);
							}
						}
					else
						if (memcmp (argv[i],"-v",2) == 0) {
							verbose = 1;
							}
						else
							if (memcmp (argv[i],"-byte",5) == 0) {
								i++;
								if (toupper(*argv[i]) == 'L')
									image_byte_order = LSBfirst;
								else
									if (toupper(*argv[i]) == 'M')
										image_byte_order = MSBfirst;
									else {
										fprintf (stderr,"Error: argument following -byte must be LSBfirst or MSBfirst\n");
										exit (1);
										}
								if (image_byte_order != machine_byte_order) swap_bytes = 1;
								}
							else
								strcpy (input_file,argv[i]);

	if (width == 0 || height == 0) {
		fprintf (stderr,"Usage: %s [-v] input_file -w width -h height [using bin_file] [to output_file]\n",argv[0]);
		fprintf (stderr,"You must specify width and height on the command line.\n");
		exit (1);
		}

	if (*bin_file) {
		if (in = fopen (bin_file,"r")) {
			nb = 0;
			while (fgets (string,128,in)) nb++;
			if (bin = (struct value_color *) malloc (nb * sizeof (struct value_color))) {
				rewind (in);
				i = 0;
				while (fgets (string,128,in)) {
					int itop,ir,ig,ib;

					if (sscanf (string,"%d%x%x%x",&itop,&ir,&ig,&ib) == 4) {
						bin[i].top = (unsigned short) itop;
						bin[i].r = (unsigned char) ir;
						bin[i].g = (unsigned char) ig;
						bin[i].b = (unsigned char) ib;
						i++;
						}
					else
						fprintf (stderr,"Warning: line ignored in bin file: \"%s\"\n",string);
					}
				if (i < nb) {
					fprintf (stderr,"Warning: %d bins expected, %d bins found\n",nb,i);
					nb = i;
					}
				}
			else {
				fprintf (stderr,"Error: could not allocate memory for %d bins\n",nb);
				exit (1);
				}
			fclose (in);
			}
		else {
			fprintf (stderr,"Error: could not open bin file %s\n",bin_file);
			exit (1);
			}
		}
	else {
		bin = default_bins;
		for (nb=0; bin[nb].top < 65535; nb++);
		if (bin[nb].top == 65535) nb++;
		}

	if (verbose) {
		fprintf (stderr,"  i   value   r  g  b\n");
		for (i=0; i < nb; i++)
			fprintf (stderr,"%3d:  %5d  %02X %02X %02X\n",i,bin[i].top,bin[i].r,bin[i].g,bin[i].b);
		}

	if (in = fopen (input_file,"rb")) {
		if (value = (unsigned short *) malloc (width * sizeof(unsigned short))) {
			if (!(scratch = (unsigned short *) malloc (width * sizeof(unsigned short)))) {
				fprintf (stderr,"Error: could not allocate scratch array of size %d\n",width*sizeof(unsigned short));
				exit (1);
				}
			if (*output_file == 0) {
				strcpy (output_file,input_file);
				strcat (output_file,".ppm");
				}
			if (out = fopen (output_file,"wb")) {
				fprintf (out,"P6 %d %d %d\n",width,height,MAXVAL);
				for (y=0; y < height; y++)
					if ((n = fread (scratch,sizeof(unsigned short),width,in)) == width) {
						if (swap_bytes)
							swab (scratch,(char *)value,n*sizeof(unsigned short));
						else
							memcpy (value,scratch,n*sizeof(unsigned short));
						for (x=0; x < width; x++) {
							for (i=0; i < nb; i++)
								if (value[x] < bin[i].top) {
									fputc (bin[i].r,out);
									fputc (bin[i].g,out);
									fputc (bin[i].b,out);
									break;
									}
							if (i >= nb) fprintf (stderr,"Error at (%d,%d); value = %d = 0x%04X\n",x,y,value[x],value[x]);
							}
						if (verbose) fprintf (stderr,"%5d\r",y);
						fflush (stdout);
						}
					else
						fprintf (stderr,"Error: only %ld of %ld words read from input file\n",n,width);
				fclose (out);
				}
				else
					fprintf (stderr,"Error: could not create output file %s\n",output_file);
			free (value);
			}
		else
			fprintf (stderr,"Error: could not allocate array for input data\n");
		fclose (in);
		}
	else
		fprintf (stderr,"Error: could not open input file %s\n",input_file);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

