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

				SIG_READ.C
				Read SIG format audio data files.
				These files were used at Bell Laboratories 1980-2002.

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 "gpk_std.h"
#include "gpklib.h"
#include "gpklib/ieee754.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)




BOOL parse_hdr_record(char *s, char **a, char **v, char **c)
{
	chomp_newline(s);
	while(*s && isspace(*s))
		s++;
	*a = s;
	while(*s && (isalnum(*s) || *s=='_'))
		s++;
	if(! *s)
		return FALSE;
	*s = '\0';
	s++;
	while(*s && isspace(*s))
		s++;
	*v = s;
	*c = 0;
	while(*s) {
		if(*s == '#')
			*c = s;
		s++;
		}
	if(c) {
		*c = '\0';
		c++;
		}
	return TRUE;
}


int bitpix_from_typecode(int s)
{
	C int SIG_FORMAT = 0xf00;
	C int SIG_DATA_TYPE = 0xf;
	C int SIG_ASCII = 0x100;
	C int SIG_BINARY = 0x000;
	C int APERIODIC_SIGNALS = 0x30;
	C int VAR_REC_SIGNALS = 0xc0;

	if(s & (VAR_REC_SIGNALS | APERIODIC_SIGNALS))
		fprintf(stderr, "WARN: VAR_REC or APERIODIC flag is set: %x\n", s);

	if((s & SIG_FORMAT) == SIG_ASCII)
		return 0;
	if((s & SIG_FORMAT) != SIG_BINARY) {
		fprintf(stderr, "DBG: oops: NOT BINARY: %x\n", s);
		return INT_NAN;	/* We don't handle byteswapped SIGs. */
		}

 	C int code = s & SIG_DATA_TYPE;
	if(code == 8)
		return -64;
	else if(code == 7)
		return -32;
	else if(code == 5)
		return 32;
	else if(code == 4)
		return 16;
	else if(code == 1)
		return 8;

	return INT_NAN;
}



int min(int a, int b)
{
 return a<b ? a : b;
}


C unsigned sig_magic = ('S'<<24) | ('I' << 16) | ('G' << 8) | '\n';
C unsigned sig_magic_swab = ('I'<<24) | ('S' << 16) | ('\n' << 8) | 'G';
C unsigned sig_magic_swaw = ('G'<<24) | ('\n' << 16) | ('S' << 8) | 'I';
C unsigned sig_magic_swawb = ('\n'<<24) | ('G' << 16) | ('I' << 8) | 'S';

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

 s.rewind();
 multi_image_empty(rv);

 p->prop = new proplist;
 assert(p->prop);

 int hdrsize;
 int hdr_line = 1;
 unsigned hdr_magic;
 assert(sizeof(hdr_magic) == 4);
 if(!s.fread(&hdr_magic, 1, sizeof(hdr_magic))) {
	multi_image_destr(rv);
	return strdup("read error at top of header.");
	}

 if(hdr_magic == sig_magic)
	swap = LOCAL_ORDER;
 else if(hdr_magic == sig_magic_swab)
	swap = SWAB;
 else if(hdr_magic == sig_magic_swaw)
	swap = SWAW;
 else if(hdr_magic == sig_magic_swawb)
	swap = SWAWB;
 else {
	multi_image_destr(rv);
	return strdup("not SIG: top of header mismatch.");
	}
 D fprintf(stderr, "swap=%d\n", swap);
 hdr_line++;
 char hdr_rec[10000];
 if(!s.fgets(hdr_rec, sizeof(hdr_rec))
		 || (hdrsize = safe_atoi(hdr_rec))==INT_NAN
		|| hdrsize<0 || hdrsize>300000) {
	multi_image_destr(rv);
	return strdup("not SIG: bad header size on line 2.");
	}
	
 C long startheaderposition = s.tell();
 /* We terminate conservatively, to allow for miscounting in the
 header length.  We know that there are no lines shorter than 'x \n'
 */
 while(s.tell() < hdrsize + startheaderposition - 3) {
	D fprintf(stderr, "tell=%ld, hdrsize=%d h+shp=%ld\n", s.tell(),
			hdrsize, hdrsize+startheaderposition);
	hdr_line++;
	if(!s.fgets(hdr_rec, sizeof(hdr_rec)))	{
		multi_image_destr(rv);
		sprintf(err, "not SIG: 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' || is_blank_line(hdr_rec))
		continue;

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

	char *attr, *val, *com;
	if(! parse_hdr_record(hdr_rec, &attr, &val, &com)) {
		multi_image_destr(rv);
		sprintf(err, "not SIG: can't parse header line %d", hdr_line);
		return strdup(err);
	}

	D fprintf(stderr, "ATTR<%s> val=<%s>\n", attr, val);
	if(strcmp(attr, "remark")==0 || strcmp(attr, "comment")==0)
		add_append_property(p->prop, "COMMENT", val, com);
	else if(strcmp(attr, "description")==0
			|| strcmp(attr, "operation")==0
			|| strcmp(attr, "operator")==0)
		add_append_property(p->prop, "HISTORY", val, com);
	else if(strcmp(attr, "frequency")==0) {
		p->prop->add("CDELT2", 1.0/atof(val), com);
		}
	else if(strcmp(attr, "samples")==0)
		p->prop->add("NAXIS2", atoi(val), com);
	else if(strcmp(attr, "dimensions")==0)
		p->prop->add("NAXIS1", atoi(val), com);
	else if(strcmp(attr, "type_code")==0) {
		C int SPECIAL_SIGNALS = 0xff000;	/* Signals.h */
		C int typecode = strtol(val, (char **)0, 16);
		C int bitpix = bitpix_from_typecode(typecode);
		if(bitpix == INT_NAN) {
			multi_image_destr(rv);
			sprintf(err, "not SIG: can't interpret type-code(%s) line %d", val, hdr_line);
			return strdup(err);
			}
		p->prop->add("BITPIX", bitpix, com);

		if(typecode & SPECIAL_SIGNALS)	{
			char ssbf[128];
			sprintf(ssbf, "%#x", typecode & SPECIAL_SIGNALS);
			p->prop->add("SIG_SPECIAL_SIGNALS", ssbf);
			}
		else
			p->prop->add("SIG_SPECIAL_SIGNALS", (char *)0);
		}
	else if(strcmp(attr, "start_time")==0) {
		p->prop->add("CRVAL2", atof(val), com);
		p->prop->add("CRPIX2", 1.0);
		}
	else if(strcmp(attr, "end_time") == 0)
		;
	else if(strcmp(attr, "time")==0)
		p->prop->add("TIME", val, com);
	else if(strcmp(attr, "date")==0)
		p->prop->add("DATE", val, com);
	else
		p->prop->add(attr, val, com);
	D fprintf(stderr, "s.tell()=%ld\n", s.tell());
	}

 while(s.tell() < hdrsize + startheaderposition)
	s.fgetc();

 if(p->prop->get("identifiers")) {
	/* I think these aren't handled quite right,
	   esp. if they contain newline,
	   or if a weird bounding character is used.
	*/
	char *ss = strdup(p->prop->gets("identifiers"));
	char **isp = csplit(ss, " \t\r\n,;");
	C int csc = csplitcount(isp);
	C int na1 =  p->prop->geti("NAXIS1", -1);
	if(csc != na1)
		fprintf(stderr, "#WARN: SIG_read: number of identifiers doesn't match NAXIS1");
	int i;
	for(i=0; i<csc && i<na1; i++) {
		char tt[512];
		sprintf(tt, "TTYPE%1d", i+1);
		p->prop->add(tt, isp[i]);
		}
	p->prop->add("identifiers", (C char *)0);
	}

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

 int cols = p->prop->geti("NAXIS1", -1);
 if(cols <= 0)	{
	sprintf(err, "not SIG: bad NAXIS1=%s", p->prop->gets( "NAXIS1", ""));
	multi_image_destr(rv);
	return strdup(err);
	}

 D fprintf(stderr, "Committed to SIG_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)
		fail = gpkio_read_in_data(s, p, rows, cols, bitpix, p->prop->getl("BLANK"), swap);
	else
		fail = strdup("can't read SIG files with BITPIX=0");

	if(fail)	{
		multi_image_destr(rv);
		sprintf(err, "not SIG: read fails after header: %s.", fail);
		free(fail);
		return strdup(err);
		}
	}
 p->xscale = p->prop->getd("CDELT1");
 p->yscale = p->prop->getd("CDELT2");
 p->zscale = p->prop->geti("BITPIX", 0)<=0 ? 1.0 : pow(0.5, p->prop->geti("BITPIX", 0));
 p->zshift = 0.0;

 p->xcenter = p->prop->getd("CRVAL1", 0.0)
		+ p->xscale*(0.5*(cols-1) - (p->prop->getd("CRPIX1", 1.0)-1.0));
 p->ycenter = p->prop->getd("CRVAL2", 0.0)
		+ p->yscale*(0.5*(rows-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), rows);

 p->i_noise = 0.0;
 p->prop->add("_FILETYPE", "SIG", "Lucent/AT&T audio format");
 gpkio_date_fix(p->prop);
 rv->n_image++;

 return 0;
}
