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

iost.c (C-Munipack project)
SBIG files low-level routines
Copyright (C) 2003 David Motl, dmotl@volny.cz

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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include "comfun.h"
#include "cmpack_common.h"
#include "iost.h"

/* File descriptor */
struct _stfile
{
	FILE *f;
	char *head;
};

/* Compressed or uncompressed file? */
static int stgetftype(const char *head)
{
	char *aux, *aux2;
	
	if (head) {
		if ((strstr(head, "ST-") == head) || (strstr(head, "SBIG") == head) || (strstr(head, "PixCel") == head)) {
			aux = strchr(head, 0x0A);
			aux2 = strstr(head, "Compressed");
			if (aux != NULL) {
				/* SBIG compressed or uncompressed file */
				return ((aux2 != NULL) && (aux2 < aux) ? 2 : 1);
			}
		}
	}
	/* This is not SBIG file */
	return -1;
}

/* Seek routine both for file and memory buffer */
static int stseek(stfile *st, int offset, int base)
{
	return fseek(st->f, offset, base);
}

/* Seek routine both for file and memory buffer */
static size_t stread(void *buffer, size_t size, size_t num, stfile *st)
{
	return fread(buffer, size, num, st->f);
}

/* Open the file and read the header into memory */
int stopen(stfile **phandle, const char *filename)
{
	size_t headlen;
	stfile *st;

	*phandle = NULL;

	/* open the file */
	st = (stfile*) cmpack_calloc(1, sizeof(stfile));
	st->f = fopen(filename,"rb");
	if (st->f==NULL) {
		/* File not found */
		cmpack_free(st);
		return CMPACK_ERR_OPEN_ERROR;
	}

	/* Read header of the file */
	st->head = (char *)cmpack_malloc(STHEADSIZE);
	headlen = fread(st->head,1,STHEADSIZE,st->f);

	/* Identify the file type */
	if ((headlen!=STHEADSIZE)||(stgetftype(st->head)<0)) {
		/* Invalid file header */
		fclose(st->f);
		cmpack_free(st->head);
		cmpack_free(st);
		return CMPACK_ERR_READ_ERROR; 
	}

	/* Normal return */
	*phandle = st;
	return 0;
}

/* Close a file and free allocated memory */
void stclos(stfile *st)
{
	if (st) {
		if (st->f) 
			fclose(st->f);
		cmpack_free(st->head);
		cmpack_free(st);
	}
}

/* Get string by key */
int stgkys(stfile *st, const char *key, char **buf)
{
	size_t len;
	char *aux, *j;

	if (buf) *buf = NULL;
	
	/* Locate a key in the header */
	aux = (char *)cmpack_malloc(strlen(key)+3);
	strcpy(aux,key);
	strcat(aux," =");
	j = strstr(st->head,aux);
	cmpack_free(aux);
	if (j==NULL) {
		/* The key was not found */
		return CMPACK_ERR_KEY_NOT_FOUND;   
	}
	/* Copy the value to buf */
	if (buf) {
		j = j + strlen(key) + 3;
		while (*j==' ')
			j++;
		len = strcspn(j, "\r\n\x1A");
		while (len>0 && j[len-1]==' ')
			len--;
		*buf = (char*)cmpack_malloc(len+1);
		if (len>0)
			memcpy(*buf, j, len);
		(*buf)[len] = '\0';
	}
	return 0;
}

/* Get integer number by key */
int stgkyi(stfile *st, const char *key, int *value)
{
	char *aux, *j;
	
	*value = 0;
	/* Locate a key in the header */
	aux = (char *)cmpack_malloc(strlen(key)+3);
	strcpy(aux,key);
	strcat(aux," =");
	j = strstr(st->head,aux);
	cmpack_free(aux);
	if (j==NULL) {
		/* The key was not found */
		return CMPACK_ERR_KEY_NOT_FOUND; 
	}
	/* Convert a value */
	j = j + strlen(key) + 3;
	while (*j==' ')
		j++;
	*value = atol(j);
	return 0;
}

/* Get floating number by key */
int stgkyd(stfile *st, const char *key, double *value)
{
	char *aux, *j;
	
	*value = 0.0;
	/* Locate a key in the header */
	aux = (char *)cmpack_malloc(strlen(key)+3);
	strcpy(aux,key);
	strcat(aux," =");
	j = strstr(st->head,aux);
	cmpack_free(aux);
	if (j==NULL) {
		/* The key was not found */
		return CMPACK_ERR_KEY_NOT_FOUND; 
	}
	/* Convert a value */
	j = j + strlen(key) + 3;
	while (*j==' ')
		j++;
	*value = atof(j);
	return 0;
}

/* Get key name and value by index */
int stgkyn(stfile *st,  int nkey, char **keyname, char **keyval) 
{
	size_t keylen, vallen;
	char *aux, *j, buf[MAXLINE];
	
	if (keyname) *keyname = NULL;
	if (keyval)  *keyval = NULL;
	/* Locate a nth line in the header */
	aux = st->head;
	while ((aux!=NULL)&&(nkey>0)) {
		j = strchr(aux,0x0A);
		if (j) {
			aux = j+2;
			nkey--;
		}
	}
	if (aux==NULL) {
		/* Beyond end of header */
		return CMPACK_ERR_KEY_NOT_FOUND;
	}
	/* Extract the line from the header */
	j = strchr(aux,0x0A);
	strncpy(buf,aux,j-aux);
	buf[j-aux] = '\0';
	if (strcmp(buf,"End")==0) {
		/* Beyond end of header */
		return CMPACK_ERR_KEY_NOT_FOUND;
	}
	/* Parse buf into keyname, keyval (comm is always empty) */
	j = strchr(buf,'=');
	if (j) {
		keylen = (j-buf-1);
		if (keylen>0 && keyname) {
			*keyname = (char*)cmpack_malloc(keylen+1);
			memcpy(*keyname, buf, keylen);
			(*keyname)[keylen] = '\0';
		}
		vallen = strlen(j+2);
		if (vallen>0 && keyval) {
			*keyval = (char*)cmpack_malloc(vallen+1);
			memcpy(*keyval, j+2, vallen);
			(*keyval)[vallen] = '\0';
		}
	} else {
		if (keyname)
			*keyname = cmpack_strdup(buf);
	}
	return 0;
}

/* Decode image data */
int stgimg(stfile *st, uint16_t *data, int bufsize)
{
	int nx, ny, i, j, k, deltai, headlen;
	size_t bytes;
	uint16_t delka, hodnota;
	uint8_t *buf;

	/* Get width and height of the image */
	stgkyi(st, "Width", &nx);
	stgkyi(st, "Height", &ny);
	if ((nx<=0)||(ny<=0)||(nx>=100000)||(ny>=100000)) {
		/* Cannot read the width or height of the image */
		return CMPACK_ERR_INVALID_SIZE;     
	}
	if (bufsize < nx*ny) {
		/* Insufficient buffer */
		return CMPACK_ERR_BUFFER_TOO_SMALL;
	}
	/* Read image data */
	stseek(st, STHEADSIZE, SEEK_SET);
	if (stgetftype(st->head)!=2) {
		/* uncompressed image */
		bytes = nx*ny;
		if (stread(data,2,bytes,st)!=bytes) {
			/* invalid image block */
			return CMPACK_ERR_READ_ERROR;
		}
	} else {
		/* Compressed image, courtesy by P. Pravec */
		buf = (unsigned char *)cmpack_malloc(2*nx);
		for (i=0;i<ny;i++) {
			/* for each line */
			/* length of line in bytes */
			headlen = (int)stread(&delka,1,2,st);
			if ((headlen<2)||(delka>2*nx)) {
				/* invalid row length */
				cmpack_free(buf);
				return CMPACK_ERR_READ_ERROR;
			} else
			if (delka==2*nx) {
				/* no compressed line in compressed image */
				headlen = (int)stread(&data[i*nx],1,2*nx,st);
				if (headlen!=2*nx) {
					/* invalid row length */
					cmpack_free(buf);
					return CMPACK_ERR_READ_ERROR;
				}
			} else {
				/* compressed line */
				headlen = (int)stread(buf,1,delka,st);
				if (headlen!=delka) {
					/* invalid row length */
					cmpack_free(buf);
					return CMPACK_ERR_READ_ERROR;
				}
				/* first value in the line */
				hodnota = buf[0] + 256*buf[1]; 
				data[i*nx] = hodnota;
				j = 1; k = 2;
				while (k<delka) {
					deltai = buf[k];
					k++;
					if (deltai>127) deltai -= 256;
					if (deltai==-128) {
						hodnota=buf[k]+256*buf[k+1];
						k+=2;
					} else {
						hodnota = (uint16_t)(hodnota + deltai);
					}
					data[i*nx+j] = hodnota;
					j++;
				}
			}
		}
		cmpack_free(buf);
	}
	return CMPACK_ERR_OK;
}
