/*****************************************************************************

				READ.C
				Main routine that calls format-specific read routines.

Copyright (C) 2002 Greg Kochanski, gpk@pribble.com .

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
#include <math.h>
#include <unistd.h>
#include <string.h>
#include <memory.h>
#include <errno.h>
#include <ctype.h>

#include "gpk_std.h"
#include "gpklib/ieee754.h"
#include "gpklib/NaN.h"
#include "gpklib/image.h"
#include "gpkio.h"
#include "gpklib.h"
#include "gpklib/err.h"
#include "gpklib/innerloop2.h"
#include "rbfile.h"
#include "internals.h"
#include "read_data.h"
#include "rw_misc.h"
#include "gpklib/assert.h"

#define D	if(0)


static inline unsigned U(char c)
{
	return 0377&(unsigned)c;
}



char *gpkio_read_in_data(RBFILE& s,
			struct image *p, int rows, int cols,
			int bitpix, long blank,
			read_in_e swap)
{
 C char test[4] = {(char)1, (char)2, (char)3, (char)4};
 C short *ts = (C short *)test;
 C int *ti = (C int *)test;
 BOOL swapbytes = FALSE;
 BOOL swapwords = FALSE;
 D fprintf(stderr, "ts=%x ti=%x\n", *ts, *ti);
 switch(swap) {
 case LOCAL_ORDER:
	break;
 case NETWORK_ORDER:
 	swapbytes = ((*ts)&0377) == 1 || ((*ts)&0377)==3;
 	swapwords = ((*ti)&0377) == 1 || ((*ti)&0377)==2;
	break;
 case ANTI_NETWORK:
 	swapbytes = ((*ts)&0377) == 2 || ((*ts)&0377)==4;
 	swapwords = ((*ti)&0377) == 3 || ((*ti)&0377)==4;
	break;
 case SWAB:
	swapbytes = TRUE;
	break;
 case SWAW:
	swapwords = TRUE;
	break;
 case SWAWB:
	swapwords = TRUE;
	swapbytes = TRUE;
	break;
 default:
	assert(0);
 }
 D fprintf(stderr, "SWAPBYTES=%d SWAPWORDS=%d ts=%x ti=%x\n",
		swapbytes, swapwords, *ts, *ti);

 BOOL aok;
 if(abs(bitpix) <= 16)
	aok = image_alloc(p, rows, cols);
 else
	aok = image_alloc_float(p, rows, cols);

 if(!aok)
	gpk_err("Cannot allocate memory for images", 0);

D fprintf(stderr, "alloc check.\n");


 if(bitpix == 8)	{
	 unsigned char *h = (unsigned char *)malloc(sizeof(unsigned char)*p->npts);
	 assert(h);
	 C int np = p->npts;
	 C char bblank = blank;
	 int i;
	 for(i=0; i<p->nlines; i++)	{
		if(s.fread((char *)h, 1, np) != np)
			return gpkio_errmsg(s);

		short *H = p->h[i];
		if(blank == bblank)	{
			INNERLOOP(j, 0, np, H[j] = h[j]==bblank ? SHRT_NAN : (short)(h[j]));
			}
		else	{
			INNERLOOP(j, 0, np, H[j] = (short)(h[j]));
			}
		}

	if(short2test(p->h)!=0)	{
		fprintf(stderr, "Corrupted array on %d=%d\n", i, short2test(p->h));
		fprintf(stderr, "Apparent size= %d x %d\n", p->nlines, p->npts);
		assert(0);
		}

	free((char *)h);
	p->prop->add("BITPIX", (int)(sizeof(short)*8));
 	}

 else if(bitpix == 16)	{
	assert(sizeof(short) == 2);
	C short sblank = blank;
	C int np = p->npts;
	assert(p->h[0] + (p->nlines-1)*p->npts == p->h[p->nlines-1]);
	if(s.fread((char *)(p->h[0]), 2, p->nlines*np) != p->nlines*np)
		return gpkio_errmsg(s);
	if(swapbytes)
		gpkio_swab_in_place(p->h[0], p->nlines*np*sizeof(short));
	if(blank == sblank) {	/* I.e. if blank is a value which could match a short... */
		short *h = p->h[0];
		int j;
		OPENMP(parallel for if(np*p->nlines>500))
		for(j=0; j<np*p->nlines; j++)
			if(h[j] == sblank)
				h[j] = SHRT_NAN;
		/* INNERLOOP(j, 0, np*p->nlines, if(h[j] == sblank) h[j] = SHRT_NAN); */
		}
	if(short2test(p->h)!=0)	{
		fprintf(stderr, "Corrupted array:%d\n", short2test(p->h));
		fprintf(stderr, "Nominal size id %d by %d.\n",
				p->nlines, p->npts);
		assert(0);
		}
 }

 else if(bitpix == -32)	{		/* IEEE-754 Floats. */
	assert(p->H);
	/* Faster, on the assumption that the array is all contiguous. */
	assert(sizeof(float) == 4);
	assert(p->H[0] + (p->nlines-1)*p->npts == p->H[p->nlines-1]);
	if(s.fread(p->H[0], 4, p->nlines*p->npts) != p->nlines*p->npts)	{
		return gpkio_errmsg(s);
		}
	if(swapbytes)
		gpkio_swab_in_place((short *)(p->H[0]), sizeof(float)*p->npts*p->nlines);
	if(swapwords)
		gpkio_swaw((char *)(p->H[0]), sizeof(float)*p->npts*p->nlines);
	if(float2test(p->H)!=0)	{
		fprintf(stderr, "Corrupted array=%d\n", float2test(p->H));
		fprintf(stderr, "Apparent size= %d x %d\n", p->nlines, p->npts);
		assert(0);
		}
	}
 else if(bitpix == 32)	{
	 assert(sizeof(fourbyte) == 4);
	 fourbyte *h = (fourbyte *)malloc(sizeof(fourbyte)*p->npts);
	 C float qnan = quiet_nan(0);
	 assert(h);
	 int i;
	 C int np = p->npts;
	 for(i=0; i<p->nlines; i++)	{
		if(s.fread((char *)h, 4, np) != np)	{
			return gpkio_errmsg(s);
			}

		if(swapbytes)
			gpkio_swab_in_place((short *)h, sizeof(fourbyte)*np);
		if(swapwords)
			gpkio_swaw((char *)h, sizeof(fourbyte)*np);
		float *H = p->H[i];
		INNERLOOP(j, 0, np, H[j] = h[j]==blank ? qnan : h[j]);
		}

	if(float2test(p->H)!=0)	{
		fprintf(stderr, "Corrupted array on %d=%d\n", i, float2test(p->H));
		fprintf(stderr, "Apparent size= %d x %d\n", p->nlines, p->npts);
		assert(0);
		}

	free((char *)h);
 	}
 else if(bitpix == -64)	{
	 assert(sizeof(double) == 8);
	 double *h = (double *)malloc(sizeof(double)*p->npts);
	 assert(h);
	 int i;
	 C int np = p->npts;
	 for(i=0; i<p->nlines; i++)	{
		if(s.fread((char *)h, 8, np) != np)	{
			return gpkio_errmsg(s);
			}

		if(swapbytes)
			gpkio_swab_in_place((short *)h, sizeof(double)*np);
		if(swapwords)
			gpkio_swaw((char *)h, sizeof(double)*np);
		float *H = p->H[i];
		INNERLOOP(j, 0, np, H[j] = h[j])
		}

	if(float2test(p->H)!=0)	{
		fprintf(stderr, "Corrupted array on %d=%d\n", i, float2test(p->H));
		fprintf(stderr, "Apparent size= %d x %d\n", p->nlines, p->npts);
		assert(0);
		}

	free((char *)h);
	p->prop->add("BITPIX", (int)(-8*sizeof(float)));
 	}
 else
	gpk_err("Bad format combination", 0);

 return 0;
}


#if 0

main(int argc, char **argv)
{
 C int TS = 500000;
 char x[TS];
 assert(argc > 1);
 FILE *fp = fopen(argv[1], "r");
 assert(fp);
 RBFILE s(fp);
 int n = 1;
 for(n=1; n<TS; n=n*2)	{
	int i;
	s.rewind();
	for(i=0; i<n; i++)
		x[i] = s.fgetc();
	s.rewind();
	for(i=0; i<n; i++)	{
		char c = s.fgetc();
		if(c != x[i])	{
			printf("ERR: %c(%d)!=%c(%d) at %d", c, c, x[i], x[i], i);
			exit(1);
			}
		}
	printf("OK at %d\n", n);
	}
 return 0;
}
#endif



static int col_sep_set = 0;
static int is_col_sep[256];
/* You cannot cast this to a char array, because on Linux,
 * isspace() returns 8192(nonzero), which would be truncated to 0. */

LOCAL BOOL get_token(RBFILE& s, char *buf, int n)
{
 const int n0 = n;
 const char *b0 = buf;
 if(!col_sep_set)	{
	col_sep_set = 1;
	int i;
	for(i=0; i<256; i++) {
		is_col_sep[i] = isspace(i);
	}
	is_col_sep[U(',')] = 1;
	is_col_sep[U(';')] = 1;
	is_col_sep[U('&')] = 1;
	is_col_sep[U('|')] = 1;
	is_col_sep[U(':')] = 1;
	is_col_sep[U('$')] = 1;
	}

 int c;
 while((c=s.fgetc())!=EOF && isspace(c))
	;
 if(c == EOF)	{
	*buf = '\0';
	fprintf(stderr, "INFO: not ascii image: EOF while looking for token\n");
	return 0;
	}

 if(!is_col_sep[c])
	s.ungetc(c);

 while((c=s.fgetc())!=EOF && isspace(c))
	;
 if(c == EOF)	{
	*buf = '\0';
	fprintf(stderr, "INFO: not ascii image: EOF during 2nd look for token\n");
	return 0;
	}
 s.ungetc(c);

 n--;
 while(n>0 && (c=s.fgetc())!=EOF && !is_col_sep[c])	{
	*buf++ = c;
	n--;
	}
 *buf++ = '\0';

 if(c!=EOF && !is_col_sep[c])	{
	fprintf(stderr, "INFO: not ascii image: token longer than %d bytes: {%s}\n",
			n0-n, b0);
	return 0;	/* Buffer full before we saw another col_separator. */
	}

 return 1;
}



char * gpkio_read_in_data_ascii(RBFILE& s, struct image *p)
{
 if(!image_alloc_float(p, p->nlines, p->npts)) {
	fprintf(stderr, "WARN: ascii_read.c: Cannot allocate memory for images");
	assert(0);
	}

 int i;
 for(i=0; i<p->nlines; i++)	{
	int j;
	for(j=0; j<p->npts; j++)	{
		char buf[64];
		if(!get_token(s, buf, sizeof(buf)))	{
			char err[128];
			sprintf(err, "can't tokenize at (%d,%d)",
					i, j);
			image_destr(p);
			return strdup(err);
			}
		p->H[i][j] = safe_atof(buf);
		}
	}

 return 0;
}


char *gpkio_read_in_transposed(RBFILE& s,
			struct image *p, int rows, int cols,
			int bitpix, long blank,
			read_in_e swap)
{
 C char test[4] = {(char)1, (char)2, (char)3, (char)4};
 C short *ts = (C short *)test;
 C int *ti = (C int *)test;
 BOOL swapbytes = FALSE;
 BOOL swapwords = FALSE;
 switch(swap) {
 case LOCAL_ORDER:
	break;
 case NETWORK_ORDER:
 	swapbytes = ((*ts)&0377) != 2;
 	swapwords = ((*ti)&0377) != (swapbytes?3:4);
	break;
 case ANTI_NETWORK:
 	swapbytes = ((*ts)&0377) != 2;
 	swapwords = ((*ti)&0377) != (swapbytes?3:4);
	swapbytes = !swapbytes;
	swapwords = !swapwords;
	break;
 case SWAB:
	swapbytes = TRUE;
	break;
 case SWAW:
	swapwords = TRUE;
	break;
 case SWAWB:
	swapwords = TRUE;
	swapbytes = TRUE;
	break;
 default:
	assert(0);
 }
 D fprintf(stderr, "SWAPBYTES=%d SWAPWORDS=%d ts=%x ti=%x\n",
		swapbytes, swapwords, *ts, *ti);

 BOOL aok;
 if(abs(bitpix) <= 16)
	aok = image_alloc(p, rows, cols);
 else
	aok = image_alloc_float(p, rows, cols);

 if(!aok)
	gpk_err("Cannot allocate memory for images", 0);

D fprintf(stderr, "alloc check.\n");


 if(bitpix == -32)	{
	 assert(sizeof(float) == 4);
	 float *h = (float *)malloc(sizeof(float)*p->nlines);
	 assert(h);
	 int i;
	 C int nl = p->nlines;
	 for(i=0; i<p->npts; i++)	{
		if(s.fread((char *)h, 4, nl) != nl) {
			return gpkio_errmsg(s);
			}

		if(swapbytes)
			gpkio_swab_in_place((short *)h, sizeof(float)*nl);
		if(swapwords)
			gpkio_swaw((char *)h, sizeof(float)*nl);
		float **H = p->H;
		INNERLOOP(j, 0, nl, H[j][i] = h[j])
		}

	if(float2test(p->H)!=0)	{
		fprintf(stderr, "Corrupted array on %d=%d\n", i, float2test(p->H));
		fprintf(stderr, "Apparent size= %d x %d\n", p->nlines, p->npts);
		assert(0);
		}

	free(h);
	p->prop->add("BITPIX", (int)(-8*sizeof(float)));
 	}
 else
	gpk_err("Bad format combination", 0);

 return 0;
}


char * gpkio_read_in_ascii_transposed(RBFILE& s, struct image *p)
{
 if(!image_alloc_float(p, p->nlines, p->npts)) {
	fprintf(stderr, "WARN: ascii_read.c: Cannot allocate memory for images");
	assert(0);
	}

 int i;
 for(i=0; i<p->npts; i++)	{
	int j;
	for(j=0; j<p->nlines; j++)	{
		char buf[64];
		if(!get_token(s, buf, sizeof(buf)))	{
			char err[128];
			sprintf(err, "can't tokenize at (%d,%d)",
					i, j);
			image_destr(p);
			return strdup(err);
			}
		p->H[j][i] = safe_atof(buf);
		}
	}

 return 0;
}
