#include <stdio.h>
#include <string.h>
#include <math.h>
#include <grass/gis.h>
#include <grass/glocale.h>
#include "local_proto.h"


#define DOT 	   "."		/* for determining data type -tw */
#define INT	   "int"
#define FLOAT      "float"
#define DOUBLE     "double"
#define TMPBUFSIZE 8192

static int error(char *);
static int missing(int,char *);
static int extract(int,char *,char *,void *,int,int (*)());
static int scan_int(char *,int *,int);

const char gs_ascii_flag[5]={"DSAA"};


int getgrdhead (
FILE *fd,
struct Cell_head *cellhd)
{
	char grd_flag[6];
	int nc,nr;
	float xmin,xmax,ymin,ymax,zmin,zmax;

/* make sure the input file is positioned at the beginning */
	rewind(fd);

/* read and check the flag on the first line */
	fgets(grd_flag,sizeof(grd_flag),fd);
	if(strncmp(gs_ascii_flag,grd_flag,strlen(gs_ascii_flag)))
	{
		G_warning(_("input file is not a Surfer ascii grid file"));
		return 0;
	}

/* read the row and column dimensions */
	if(fscanf(fd,"%d %d \n",&nc,&nr) != 2 )
	{
		G_warning(_("error reading the column and row dimension from the Surfer grid file"));
		return 0;
	}

/* read the range of x values */
	if(fscanf(fd,"%f %f \n", &xmin, &xmax) != 2 )
	{
		G_warning(_("error reading the X range from the Surfer grid file"));
		return 0;
	}

/* read the range of y values */
	if(fscanf(fd,"%f %f \n", &ymin, &ymax) != 2 )
	{
		G_warning(_("error reading the Y range from the Surfer grid file"));
		return 0;
	}

/* read the range of z values (not used) */
	if(fscanf(fd,"%f %f \n", &zmin, &zmax) != 2 )
	{
		G_warning(_("error reading the Z range from the Surfer grid file"));
		return 0;
	}

/* initialize the cell header */
	cellhd->zone = G_zone();
	cellhd->proj = G_projection();
	cellhd->rows=nr;
	cellhd->cols=nc;

	cellhd->ew_res=(double)(xmax-xmin)/(nc-1);
	cellhd->ns_res=(double)(ymax-ymin)/(nr-1);
/* the Surfer grid specifies x,y locations of gridded points.  The GRASS raster
   specifies an area covered by rectangular cells centerd at gridded points.
   That difference requires an adjustment */
	cellhd->north = ymax+cellhd->ns_res/2.;
	cellhd->south = ymin-cellhd->ns_res/2.;
	cellhd->east = xmax+cellhd->ew_res/2.;
	cellhd->west = xmin-cellhd->ew_res/2.;

	return 1;

}

int gethead (
FILE *fd,
struct Cell_head *cellhd,
RASTER_MAP_TYPE *d_type,
DCELL *mult,
char **nval)
{
	int n,s,e,w,r,c;
	char label[100], value[100];
	char buf[1024];
	char *err;
	int ret, len;
/* rsb fix */
	fpos_t p;

	n = s = e = w = r = c = 0;

	cellhd->zone = G_zone();
	cellhd->proj = G_projection();

/*	while (n == 0 || s== 0 || e == 0 || w == 0 || r == 0 || c == 0)*/

        while(1)
	{
/* rsb fix */
	  if (fgetpos(fd, &p) != 0)
              G_fatal_error(_("error getting file position"));

	  if (!G_getl2(buf, sizeof(buf), fd)) break;

	  len = strlen(buf);

	  *label = *value = '\0';
	  if(NULL == G_strstr(buf, ":")) break;
	  if(sscanf (buf, "%[^:]:%s", label, value)!=2) break;
	  if (*label == '\0') continue;	/* ignore blank lines */

	  if (strcmp (label, "north") == 0)
	  {
		extract (n++, label, value, &cellhd->north, cellhd->proj, G_scan_northing);
		continue;
	  }

	  if (strcmp (label, "south") == 0)
	  {
		extract (s++, label, value, &cellhd->south, cellhd->proj, G_scan_northing);
		continue;
	  }

	  if (strcmp (label, "east") == 0)
	  {
		extract (e++, label, value, &cellhd->east, cellhd->proj, G_scan_easting);
		continue;
	  }

	  if (strcmp (label, "west") == 0)
	  {
		extract (w++, label, value, &cellhd->west, cellhd->proj, G_scan_easting);
		continue;
	  }

	  if (strcmp (label, "rows") == 0)
	  {
		extract (r++, label, value, &cellhd->rows, cellhd->proj, scan_int);
		continue;
	  }

	  if (strcmp (label, "cols") == 0)
	  {
		extract (c++, label, value, &cellhd->cols, cellhd->proj, scan_int);
		continue;
	  }
  /* try to read optional header fields */

          if (strcmp (label, "type") == 0) /* if not exist then the file scan */
	  {
	    if (*d_type < 0) { /* if data type not set on command line */
		if (!strncmp(value, INT, strlen(INT))) 
			       *d_type = CELL_TYPE;
		else if (!strncmp(value, FLOAT, strlen(FLOAT))) 
			       *d_type = FCELL_TYPE; 
		else if (!strncmp(value, DOUBLE, strlen(DOUBLE))) 
			       *d_type = DCELL_TYPE;
		else 
		{
		     G_warning(_("illegal type field: using type int"));
		     *d_type= CELL_TYPE;
                }
	    }
	    else 
	       G_warning(_("ignoring type filed in header, type is set on command line"));
	    continue;
	  }

	  if (strcmp (label, "multiplier") == 0)
	  {
	    if(G_is_d_null_value(mult)) /* if mult not set on commant line */
	    {
		  if(sscanf(value, "%lf", mult) != 1)
		  {
		     G_warning(_("illegal multiplier field: using 1.0"));
		     *mult = 1.0;
                  }
	    }
	    else 
	       G_warning(_("ignoring multiplier filed in header, multiplier is set on command line"));
	    continue;
	  }

	  if (strcmp (label, "null") == 0)
	  {
	    if(!(*nval)) /* if null val string not set on command line */
		*nval = G_store(value);
	    else 
	       G_warning(_("ignoring null filed in header, null string is set on command line"));
	    continue;
	  }

      } /* while */
      /* the line read was not a header line, but actually
      the first data line, so put it back on the stack and break */
/* rsb fix */
      fsetpos(fd, &p);

      missing(n,"north") ;
      missing(s,"south") ;
      missing(e,"east") ;
      missing(w,"west") ;
      missing(r,"rows") ;
      missing(c,"cols") ;
	  
      if(!(*nval)) *nval = G_store("*");
      if(G_is_d_null_value(mult))
	    *mult = 1.0;
  /* if data type is not set, then scan data to find out data type */
      if(*d_type<0)
      {
          ret = file_scan(fd);
          if (!ret)
              *d_type = DCELL_TYPE;
          else if (ret == 1) 
              *d_type = CELL_TYPE;
          else {
              error("ERROR: in ascii data format");
              return 0;
          }
      }

      if ((err = G_adjust_Cell_head (cellhd, 1, 1)))
      {
        error (err);
        return 0;
      }

      return 1;
}

static int scan_int ( char *s, int *i,int proj)
{
	char dummy[3];

	*dummy = 0;

	if (sscanf (s, "%d%1s", i, dummy) != 1)
		return 0;
	if (*dummy)
		return 0;
	if (*i <= 0)
		return 0;
	return 1;
}

static int extract ( int count,
char *label, char *value,
void *data, int proj,
int (*scanner)())
{
	char msg[1024];
	if (count)
	{
		sprintf (msg, "duplicate \"%s\" field in header", label);
		error (msg);
		return 0;
	}
	if (scanner (value, data, proj))
		return 1;
	sprintf (msg, "illegal \"%s\" value in header", label);
	error (msg);
	sprintf (msg, "  %s: %s", label, value);
	error (msg);
	return 0;
}

static int missing (int count, char *label)
{
	char msg[200];
	if (count) return 0;
	sprintf (msg, "\"%s\" field missing from header", label);
	error(msg);
	return 1;
}

static int error( char *msg)
{
	static int first = 1;

	if (first)
	{
		G_message(_("** errors detected in header section **\n"));
		first = 0;
	}
	fprintf (stderr, "  %s\n", msg);

	return 0;
}

/* file_scan(): determine data type in ascii format */
int 
file_scan (FILE *fd)
{
  long curpos;
  char tmpbuf[TMPBUFSIZE];
  size_t size = TMPBUFSIZE;

  if ((curpos = ftell(fd)) == -1)
	return -1;
  while (!feof(fd)) {
    if (size != fread(tmpbuf, sizeof(char), size, fd)) {
      if (!feof(fd))
	return -1;
    }
    if (strstr(tmpbuf, DOT) != NULL) {
        fseek(fd, curpos-1L, SEEK_SET);
	return 0;
    }
  }
  fseek(fd, curpos-1L, SEEK_SET);
  return 1;
}
