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

				STM_READ.C
				Reads G. Kochanski ancient STM data files (2D images).

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 <gpk_std.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include "savefmt.h"	/* in d:/oldsgb */
#include "read.h"
#include "image.h"
#include <assert.h>
#include <mylib.h>
#include <readwrite.h>
#include <string.h>
#include <unistd.h>

#define VERBOSE	if(0)

LOCAL BOOL read_b2float_2(FILE *fp, struct b2float *p)
{
 return read_schar_1(fp, &p->mantissa) && read_schar_1(fp, &p->exponent);
}

LOCAL BOOL read_b4float_4(FILE *fp, struct b4float *p)
{
 return read_chars(fp, 3, (char *)(p->mantissa))
 			&& read_schar_1(fp, &p->exponent);
}

LOCAL BOOL read_filehdr(FILE *fp, struct filehdr *p)
{
 BOOL ok;

 {
  short tmp;
  ok = read_short_2(fp, &tmp);
  p->type = (imagetype_et)tmp;
 }
 ok &= read_short_2(fp, &p->angle);
 ok &= read_short_2(fp, &p->xrange);
 ok &= read_short_2(fp, &p->nlines);
 ok &= read_short_2(fp, &p->delay);
 ok &= read_chars(fp, sizeof(p->zunits), p->zunits);
 ok &= read_short_2(fp, &p->Zhi);
 ok &= read_short_2(fp, &p->Zlo);
 ok &= read_long_4(fp, &p->constr_time);
 ok &= read_short_2(fp, &p->npts);
 VERBOSE fprintf(stderr, "nlines=%d, npts=%d\n", p->nlines, p->npts);
 ok &= read_short_2(fp, &p->bias);
 ok &= read_short_2(fp, &p->Zlolo);
 ok &= read_short_2(fp, &p->yrange);
 ok &= read_TBOOL_1(fp, &p->flags);
 ok &= read_TBOOL_1(fp, &p->sign);
 ok &= read_TBOOL_1(fp, &p->acdc);
 ok &= read_TBOOL_1(fp, &p->idnum);
 VERBOSE fprintf(stderr, "idnum: 0x%x\n", p->idnum);
 ok &= read_short_2(fp, &p->xcenter);
 ok &= read_short_2(fp, &p->ycenter);
 ok &= read_uchar_1(fp, &p->xyatten);
 VERBOSE fprintf(stderr, "xyatten: %d\n", p->xyatten);
 ok &= read_uchar_1(fp, &p->zatten);
 VERBOSE fprintf(stderr, "zatten: %d\n", p->zatten);
 ok &= read_uchar_1(fp, &p->aloop);
 ok &= read_uchar_1(fp, &p->pad2);
 ok &= read_b2float_2(fp, &p->inoise);
 ok &= read_b2float_2(fp, &p->current);
 ok &= read_chars(fp, sizeof(p->currentunits), p->currentunits);

 return ok;
}

LOCAL BOOL read_filefooter(FILE *fp, struct filefooter *p)
{
 BOOL ok = TRUE;

 ok &= read_chars(fp, sizeof(p->xunits), p->xunits);
 ok &= read_chars(fp, sizeof(p->yunits), p->yunits);
 ok &= read_chars(fp, sizeof(p->label), p->label);
 ok &= read_chars(fp, sizeof(p->constr_time), p->constr_time);
 ok &= read_short_2(fp, &p->bias);
 ok &= read_short_2(fp, &p->bias_lo);
 ok &= read_TBOOL_1(fp, &p->idnum);
 ok &= read_TBOOL_1(fp, &p->pad0);
 ok &= read_chars(fp, sizeof(p->comment), p->comment);
 ok &= read_short_2(fp, &p->scantype);
 ok &= read_b2float_2(fp, &p->time_per_pixel);
 ok &= read_b2float_2(fp, &p->time_per_line);
 {
 int i;
 for(i=0;i<sizeof(p->pad1)/sizeof(*(p->pad1));i++)
 	ok &= read_short_2(fp, &p->pad1[i]);
 }
 ok &= read_chars(fp, sizeof(p->pad2), p->pad2);
 {
 int i;
 for(i=0;i<sizeof(p->where)/sizeof(*(p->where));i++)
 	ok &= read_b4float_4(fp, &p->where[i]);
 }
 ok &= read_b4float_4(fp, &p->zshift);
 ok &= read_b4float_4(fp, &p->zscale);
 {
 int i;
 for(i=0;i<5;i++)
 	ok &= read_long_4(fp, &p->pad3[i]);
 }
 return ok;
}

BOOL is_Z_name(C char *p)
{
 assert(p);
 int x = strlen(p);
 return x>2 && p[x-1]=='Z' && p[x-2]=='.';
}

FILE *zcat_pipe(C char *name)
{
 C static char retrieve[] = "zcat ";
 char *cmdbuf = strcat(strcpy((char *)malloc(strlen(name)+sizeof(retrieve)),
									retrieve),
							name);
 assert(cmdbuf);
 fprintf(stderr, "# %s\n", cmdbuf);
 return popen(cmdbuf, "rb");
}

struct multi_image readfile(C char *name)
{
 FILE *InStream = 0;
 int i;
 struct filehdr in;
 struct filefooter foot;
 struct multi_image rv;
 BOOL fail = FALSE, pipe_in_use = FALSE;

 assert(sizeof(filehdr)==60);


 multi_image_empty(&rv);


 if(strcmp(name, "-")==0)
	InStream = stdin;
 else if(strcmp(name, "-.Z")==0)
	pipe_in_use = (InStream = zcat_pipe("-"))!=0;
 else if(!is_Z_name(name) && access(name, R_OK)==0)	{
	fprintf(stderr, "Opening %s directly\n", name);
	InStream = fopen(name, "rb");
	}
 else if(is_Z_name(name) && access(name, R_OK)==0)	{
	fprintf(stderr, "Opening %s through zcat\n", name);
	pipe_in_use = (InStream = zcat_pipe(name))!=0;
	}
 else if(!is_Z_name(name))	{
	C static char suffix[] = ".Z";
	char *nameZ = strcat(strcpy((char *)malloc(strlen(name)+sizeof(suffix)),
							name), suffix);
	if(access(nameZ, R_OK)==0)	{
		fprintf(stderr, "Opening %s through zcat with .Z=%s\n", name, nameZ);
		pipe_in_use = (InStream = zcat_pipe(nameZ))!=0;
		}
	free(nameZ);
	}

 if(!InStream)	{
	fprintf(stderr, "ERR: Cannot open %s!", name);
	return rv;
	}

 while(read_filehdr(InStream, &in))	{
	struct image *p = &rv.image[rv.n_image];
	VERBOSE fprintf(stderr, "N_image=%d\n", rv.n_image);
	VERBOSE fprintf(stderr, "idnum=%d\n", in.idnum);
	if(in.idnum==0x25 && (in.type==0 || in.type==82))	{
		image_constr(p, in.angle,
			     in.xrange*((in.flags&FLAG_XS)?0.001:1.0)/in.npts,
			     "Angstroms", it_Intensity);
		image_alloc(p, in.nlines, in.nlines);
		rv.n_image++;
		image_setzscale(p, in.Zhi + in.Zlo/100.0, "");
		p->constr_time = 0;
		image_addlabel(p, "");
		p->delay = in.delay*((in.flags&FLAG_DELAY) ? 1e-6 : 1e-3);
		p->tipbias = (in.bias)/100.0;
		p->zshift = p->i_noise = p->current = 0.0;
		}
	else if(in.idnum==0x16)	{
		p->type = (imagetype_et) in.type;
		image_alloc(p, in.nlines, in.npts);
		rv.n_image++;
		image_setzscale(p, in.Zhi + in.Zlo/100.0 + in.Zlolo/1e6, in.zunits);
		p->xscale = in.xrange*((in.flags&FLAG_XS)?1e-3:1.0)/in.npts;
		p->yscale = in.yrange*((in.flags&FLAG_YS)?1e-3:1.0)/in.nlines;
		p->xcenter = in.xcenter*((in.flags&FLAG_XS)?1e-3:1.0);
		p->ycenter = in.ycenter*((in.flags&FLAG_YS)?1e-3:1.0);
		p->angle = in.angle;
		p->constr_time = in.constr_time;
		p->delay = in.delay*((in.flags&FLAG_DELAY) ? 1e-6 : 1e-3);
		p->i_noise = *(short*)&in.inoise/204.8;
		p->zshift = p->current = 0.0;
		}
	else if(in.idnum==0x17 || in.idnum==0x18 || in.idnum==0x19
													|| in.idnum==0x1A)	{
		p->type = (imagetype_et) in.type;
		assert(in.nlines>0 && in.nlines<2500 && in.npts>0 && in.npts<2500);
		VERBOSE fprintf(stderr, "ready to alloc\n");
		image_alloc(p, in.nlines, in.npts);
		VERBOSE fprintf(stderr, "alloc done\n");
		rv.n_image++;
		if(in.idnum<0x1A)	{
			image_setzscale(p, in.Zhi + in.Zlo/100.0 + in.Zlolo/1e6, in.zunits);
			p->zshift = 0.0;
			}
		VERBOSE fprintf(stderr, "zscale done\n");
		p->xscale = in.xrange*((in.flags&FLAG_XS)?1e-3:1.0)/in.npts;
		p->yscale = in.yrange*((in.flags&FLAG_YS)?1e-3:1.0)/in.nlines;
		p->xcenter = in.xcenter*((in.flags&FLAG_XS)?1e-3:1.0);
		p->ycenter = in.ycenter*((in.flags&FLAG_YS)?1e-3:1.0);
		p->angle = in.angle;
		p->constr_time = in.constr_time;
		p->delay = in.delay*((in.flags&FLAG_DELAY) ? 1e-6 : 1e-3);
		VERBOSE fprintf(stderr, "ready to bfloat\n");
		p->i_noise = b2float_double(in.inoise);
		p->current = b2float_double(in.current);
		assert(strncmp(in.currentunits, "V", sizeof(in.currentunits))==0);
		}
	else if(in.type>40 && in.currentunits[0]==0 && in.currentunits[5]==0)	{
		printf("Reading Metrograph\n");
		printf("in.npts=%d\n",in.npts);
		printf("in.nlines=%d\n",in.nlines);
		printf("in.xrange=%d\n",in.xrange);
		printf("in.yrange=%d\n",in.yrange);
		p->type = it_Intensity;
		in.yrange = in.xrange;
		in.npts = in.nlines;
		printf("\nlines %d points %d\n",in.nlines, in.npts);
		image_alloc(p, in.nlines, in.nlines);
		rv.n_image++;
		p->constr_time = 0;
		p->tipbias = in.bias/100.0;
		image_setzscale(p, in.Zhi + in.Zlo/100.0, "nm");
		p->xscale = in.xrange/in.npts;
		p->yscale = in.yrange/in.nlines;
		p->xcenter = p->ycenter = 0;
		p->angle = in.angle;
		p->delay = in.delay/1000.0;
		strncpy(p->xunits, "nm", sizeof(p->xunits));
		strncpy(p->yunits, "nm", sizeof(p->yunits));
		image_addlabel(p, "Metrograph");
		}
	else	{
		fprintf(stderr, "Unrecognized idnum:0x%x or %d\n", in.idnum, in.idnum);
		fail = TRUE;
		break;
		}

	 VERBOSE fprintf(stderr, "if done\n");
	 if(!multi_image_allocated(&rv))	{
		fprintf(stderr, "Can't allocate memory for images.\n");
		fail = TRUE;
		break;
		}

	 VERBOSE fprintf(stderr, "ready to read shorts: nl=%d h=%p\n", p->nlines,
						p->h);
	 for(i=0;i<p->nlines;i++)	{
		VERBOSE fprintf(stderr, "reading %d shorts at %p; i=%d\n",
								p->npts, p->h[i], i);
		if(!read_shorts_2(InStream, p->npts, p->h[i]))	{
			fprintf(stderr, "Read fails on image: npts=%d, i=%d\n", p->npts, i);
			fail = TRUE;
			break;
			}
		if(short2test(p->h)!=0)	{
			fprintf(stderr, "Corrupted array on %d %d=%d\n", rv.n_image,
										i, short2test(p->h));
			fail=TRUE;
			break;
			}
		}

	VERBOSE fprintf(stderr, "ready to read footer\n");
	if(in.idnum==0x16 || in.idnum==0x17 || in.idnum==0x18 || in.idnum==0x19
										|| in.idnum==0x1A) {
		if(!read_filefooter(InStream, &foot))	{
 			fprintf(stderr, "Cannot read footer from %s\n", name);
			fail = TRUE;
			break;
			}
		assert(foot.idnum == in.idnum);

		VERBOSE fprintf(stderr, "footer read\n");
		strncpy(p->xunits, foot.xunits, sizeof(p->xunits));
		strncpy(p->yunits, foot.yunits, sizeof(p->yunits));
		image_addlabel(p, foot.label);
		VERBOSE fprintf(stderr, "labels done\n");
		p->tipbias = foot.bias + foot.bias_lo*1e-4;
		if(in.idnum==0x17 || in.idnum==0x18 || in.idnum==0x19)
			rv.type = (enum scantype_e) foot.scantype;
		if(in.idnum == 0x18 || in.idnum==0x19)	{
			p->where.xxx = b4float_double(foot.where[0]);
			p->where.yyy = b4float_double(foot.where[1]);
			p->where.zzz = b4float_double(foot.where[2]);
			}
		if(in.idnum==0x19 || in.idnum==0x1A)	{
			p->time_per_pixel = b2float_double(foot.time_per_pixel);
			p->time_per_line = b2float_double(foot.time_per_line);
			}
		if(in.idnum==0x1A)	{
			p->zshift = b4float_double(foot.zshift);
			image_setzscale(p, b4float_double(foot.zscale), in.zunits);
			}
		}
	VERBOSE
		fprintf(stderr, "label: %s, x:%s, y:%s, z:%s\n", foot.label,
						foot.xunits, foot.yunits, in.zunits);
	VERBOSE
		fprintf(stderr, "%s\n", foot.constr_time);

 	}
 if(fail)     {
	multi_image_destr(&rv);
	multi_image_empty(&rv);
	}
 if(rv.n_image <= 0)
 	fprintf(stderr, "Nothing done\n");

 if(pipe_in_use)	pclose(InStream);
 else	fclose(InStream);

 return rv;
}
/*

void plot_empty(struct plot *tthis)
{
 tthis->hl = 0;
 tthis->contr = 0;
 tthis->plotr_wt = 0;
 tthis->range.hmin = 10000;
 tthis->range.hmax = -10000;
 tthis->x_pix = tthis->y_pix = 0;
 tthis->x_spot = tthis->y_spot = 0;
}

*/
