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

				EST_READ.C
				Reads in Edinburgh Speech Tools format data files.

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 <string.h>
#include <errno.h>
#include <ctype.h>
#include <math.h>

#include "gpklib/ieee754.h"
#include "gpklib.h"
#include "gpklib/image.h"
#include "gpklib/NaN.h"
#include "rbfile.h"
#include "gpklib/assert.h"
#include "internals.h"
#include "rw_misc.h"
#include "read_data.h"


#define D	if(0)

const char Channel[] = "Channel_";

LOCAL BOOL parse_line(proplist *p, C char *s, char *commentchar, BOOL *breaks)
{
	C int chnl = strlen(Channel);
	char *k, *kk, *v, *vv, *comment = NULL;
	C char *comments;

	while(*s && isspace(*s))
		s++;
	kk = k = (char *)malloc(strlen(s));
	vv = v = (char *)malloc(strlen(s));
	while(*s && !isspace(*s))
		*(kk++) = *(s++);
	*kk = '\0';
	while(*s && isspace(*s))
		s++;
	if(!*s) {
		free(k);
		free(v);
		return FALSE;
	}
	while(*s && *s!=*commentchar)
		*(vv++) = *(s++);
	while(vv>v && isspace(vv[-1]))
		vv--;		/* Remove spaces at end of value. */
	*vv = '\0';
	while(*s && isspace(*s))
		s++;
	comments = s;

	if(strcmp(k, "CommentChar") == 0)
		*commentchar = v[0];
	else if(strcmp(k, "DataType") == 0) {
		if(strcmp(v, "binary") == 0)
			p->add("BITPIX", -32, comment);
		else
			p->add("BITPIX", 0, comment);
		}
	else if(strcmp(k, "NumFrames") == 0)
		p->add("NAXIS2", safe_atoi(v), comment);
	else if(strcmp(k, "BreaksPresent") == 0)
		*breaks = strcasecmp(v, "true")==0 || strcmp(v, "1")==0;
	else if(strcmp(k, "NumChannels") == 0) {
		D fprintf(stderr, "NC=%s %d\n", v, safe_atoi(v)+1);
		p->add("NAXIS1", safe_atoi(v), comment);
	}
	else if(strncmp(k, Channel, chnl) == 0) {
		C int idx = safe_atoi(&k[chnl]);
		char buf[128];
		sprintf(buf, "TTYPE%1d", idx+2+(*breaks?1:0));	/* TTYPE1 is time axis, TTYPE2 is break */
		p->add(buf, v, comment);
	}
	else
		p->add(k, v, comment);

	free(k);
	free(v);
	return TRUE;
}


char *getdata_EST(struct multi_image *rv, RBFILE& s, BOOL header_only)
{
 char err[1024];
 struct image * p = &rv->image[0];

 s.rewind();
 multi_image_empty(rv);
 assert(rv->n_image == 0);

 p->prop = new proplist;
 assert(p->prop);
 p->prop->add("BITPIX", -32);
 p->prop->add("NAXIS", 2);
 p->prop->add("CDELT2", 0.002);
 p->prop->add("TTYPE1", "time");
 p->prop->add("TUNITS1", "s");

 char hdr_rec[10000];
 int hdr_line = 0;
 char commentchar = '\0';
 BOOL breaks = FALSE;
 hdr_line++;
 if(!s.fgets(hdr_rec, sizeof(hdr_rec)))	{
	multi_image_destr(rv);
	sprintf(err, "not EST: can't read header, line %d.",
			hdr_line);
	return strdup(err);
	}
 if(strcmp(hdr_rec, "EST_File Track\n") != 0) {
	multi_image_destr(rv);
	sprintf(err, "not EST: wrong first header line.");
	return strdup(err);
	}

 while(1)	{
	hdr_line++;
	if(!s.fgets(hdr_rec, sizeof(hdr_rec)))	{
		multi_image_destr(rv);
		sprintf(err, "not EST: can't read header, line %d.",
				hdr_line);
		return strdup(err);
		}
	D fprintf(stderr, "DBG: s.fgets done: {%s}\n", hdr_rec);

	if(hdr_rec[0] == '\0')	{
		multi_image_destr(rv);
		sprintf(err, "not EST: null header line %d.",
				hdr_line);
		return strdup(err);
		}
	if(strcmp(hdr_rec, "EST_Header_End\n") == 0)
		break;

	if(!chomp_newline(hdr_rec) < 0)	{
		multi_image_destr(rv);
		sprintf(err, "not EST: header missing newline "
				"(line %d too long or null?)", hdr_line);
		return strdup(err);
		}

	if(hdr_rec[0] == '\0')
		continue;

	if(!parse_line(p->prop, hdr_rec, &commentchar, &breaks)) {
		multi_image_destr(rv);
		sprintf(err, "not EST: cannot parse line %d", hdr_line);
		return strdup(err);
		}
	}
 assert(rv->n_image == 0);

 if(breaks)
	p->prop->add("TTYPE2", "break");


 p->nlines = p->prop->geti("NAXIS2", -1);
 if(p->nlines <= 0)	{
	sprintf(err, "INFO: not EST bad NAXIS2=%s", p->prop->gets( "NAXIS2", ""));
	multi_image_destr(rv);
	return strdup(err);
	}

 p->npts = p->prop->geti("NAXIS1", -1) + (breaks?2:1);
 if(p->npts <= 0)	{
	sprintf(err, "not EST bad NAXIS1=%s", p->prop->gets( "NAXIS1", ""));
	multi_image_destr(rv);
	return strdup(err);
	}

 assert(rv->n_image == 0);
 D fprintf(stderr, "Committed to ascii_image\n");
 s.commit();		/* Toss out the buffer. */

 if(!header_only)	{
	char * fail = 0;
	C int bitpix = p->prop->geti("BITPIX", 0);
	if(bitpix != 0) {
		read_in_e order = ANTI_NETWORK;
		if(strcmp(p->prop->gets("ByteOrder", "01"), "01") == 0)
			order = ANTI_NETWORK;
		else
			order = NETWORK_ORDER;
		fail = gpkio_read_in_data(s, p, p->nlines, p->npts, bitpix, LONG_NAN, order);
	}
	else
		fail = gpkio_read_in_data_ascii(s, p);

	if(fail)	{
		multi_image_destr(rv);
		sprintf(err, "not ascii image: read fails after header: %s.", fail);
		free(fail);
		return strdup(err);
		}
	}
 p->xscale = p->prop->getd("CDELT1", 1.0);	/* Preferred. */
 p->yscale = p->prop->getd("DeltaT", 1.0);
 p->yscale = p->prop->getd("CDELT2", p->yscale);	/* Preferred. */
 p->zscale = p->prop->getd("BSCALE", 1.0);	/* Preferred. */
 p->zshift = p->prop->getd("BZERO", 1.0);		/* Preferred. */

 D fprintf(stderr, "zscale=%g from %s or %s\n", p->zscale,
	p->prop->gets("zscale", "<nil>"), p->prop->gets("BSCALE", "<nil>"));
 D fprintf(stderr, "zshift=%g from %s or %s\n", p->zshift,
	p->prop->gets("zshift", "<nil>"), p->prop->gets("BZERO", "<nil>"));

 /* CRPIX1 corresponds to the first pixel. */
 p->xcenter = p->prop->getd("CRVAL1", 0.0)
		+ p->xscale*(0.5*(p->npts-1) - (p->prop->getd("CRPIX1", 1.0)-1.0));
 p->ycenter = p->prop->getd("CRVAL2", 0.0)
		+ p->yscale*(0.5*(p->nlines-1) - (p->prop->getd("CRPIX2", 1.0)-1.0));
 D fprintf(stderr, "ycenter=%g from v=%g sc=%g pix=%g rows=%d\n",
		p->ycenter, p->prop->getd("CRVAL2", 0.0), p->yscale,
		p->prop->getd("CRPIX2", 1.0), p->nlines);

 if(p->prop->get("BUNIT"))
	p->prop->add("zunits", p->prop->gets("BUNIT", "???"));
 if(p->prop->get("CTYPE1"))
	p->prop->add("xunits", p->prop->gets("CTYPE1", "???"));
 if(p->prop->get("CTYPE2"))
	p->prop->add("yunits", p->prop->gets("CTYPE2", "???"));
 if(p->prop->get("CROTA1"))
	p->prop->add("angle", p->prop->gets("CROTA1", "???"));
 p->i_noise = p->prop->getd("I_NOISE", 0.0);
 p->prop->add("_FILETYPE", "EST", "Edinburgh Speech Tools");
 p->prop->add("ByteOrder", (char *)0);
 D fprintf(stderr, "Nl=%d np=%d\n", p->nlines, p->npts);
 gpkio_date_fix(p->prop);
 assert(rv->n_image == 0);
 rv->n_image++;
 assert(rv->n_image == 1);

 return 0;
}
