/*
 * poly_check : read and check a polygon file
 *          currently only limited syntax checking is performed
 *
 *         v0.9 Jim Craven (send comments/suggestions to craven@cg.nrcan.gc.ca)
 *
 */
#include <stdio.h>
#include <strings.h>

#define NONUM  (0)
#define YESNUM (1)
#define FALSE (0)
#define TRUE (1)

typedef struct _half_space {
     double east_bind;
     double north_bind;
     int nparm ;
     double *param_vals;
} HALF_SPACE;

typedef struct _polygon {
     int id;
     int nvert;
     double *yvert;
     double *zvert;
     struct _polygon *next;
     int nparam ;
     double *param_vals;
} POLYGON ;

typedef struct _poly_model {
     char name[81];
     char title[81];
     char prf_name[81];
     char info[81];
     char yaxis_label[81];
     char zaxis_label[81];
     double azimuth;
     struct _half_space *half;
     int npoly;
     struct _polygon *head_poly;
} POLY_MODEL;

POLY_MODEL *pm;
POLYGON *curr_p;

POLY_MODEL *alloc_model();
POLYGON *alloc_poly();
HALF_SPACE *alloc_half_space();
double *alloc_nvert();
double *alloc_params();
int stod ();

main(argc,argv)
int argc;
char *argv[];
{
     FILE *pfp=NULL; /* poly file pointer */
     char *p;        /* misc pointer to a char */
     char c;
     char name[81];
     char buffer[81], angle[5], title[81], prfname[81];
     char yaxis_label[81], zaxis_label[81];

     int i, done=0;
     int got_title, got_prf, got_angle;
     int add_to_title, add_to_angle, add_to_prf;
     int ibuf, itit, iang, iprf;
     int bad_header, bad_prf_token;
     
     double azimuth;

     int id, nparam, nvert, npoly;
     double east_bind, north_bind;

     while ( pfp==NULL ) {
	  fprintf(stdout,"Enter polyfile name: ");
	  fscanf(stdin,"%s", name) ;
	  c=getchar();
	  pfp = fopen(name,"r");
	  if(pfp==NULL)
	       perror(name);
     }
     if ( (pm = alloc_model() ) == NULL ) {
	  fprintf(stderr,"Out of memory allocating model space.\n");
	  exit (1);
     }

     if ( p = (char *) rindex(name,'.') )
	  *p == '\0';
     strcpy(pm->name,name);

     add_to_title = 1;
     add_to_angle= 0;
     add_to_prf =0;
     ibuf = 0;
     itit=0;
     iang=0;
     iprf=0;
     bad_header=0;
     got_title=0;
     got_prf=0;
     got_angle=0;
     while ( (c=fgetc(pfp)) != '\n') {
	  if ( c =='!' ) {
	       add_to_title=0;
	       title[itit++]='\0';
	       got_title=1;
	  }
	  else if ( c == '>' ) {
	       add_to_angle=1;
	  }
	  else if ( c == ' ' || c == '\t' ) {
	       if ( add_to_prf ) {
		    add_to_prf = 0;
		    prfname[iprf++] = '\0';
		    got_prf=1;
	       }
	       else if ( add_to_angle ) {
		    add_to_angle = 0;
		    angle[iang++] = '\0';
		    if( (stod(angle,&azimuth)) != YESNUM)  {
			 bad_header =1;
			 fprintf(stderr,"ERROR: Unable to convert angle to double %s\n", angle);
		    }
		    else {
			 pm->azimuth=azimuth;
			 got_angle=1;
		    }
	       }
	       else if ( add_to_title ) {
		    title[itit++] = c;
	       }
	  }
	  else {
	       if ( add_to_title ) {
		    title[itit++] = c;
	       }
	       else if ( add_to_angle ) {
		    angle[iang++] = c;
	       }
	       else if ( add_to_prf ) {
		    prfname[iprf++] = c;
	       }
	       else if ( c == 'P' ) {
		    bad_prf_token = 1;
		    c = fgetc(pfp);
		    if ( c == 'R' ) {
			 c = fgetc(pfp);
			 if ( c == 'F' ) {
			      c = fgetc(pfp);
			      if ( c == '=' ) {
				   add_to_prf = 1;
				   bad_prf_token = 0;
			      }
			 }
		    }
		    if ( bad_prf_token ) {
			 fprintf(stderr,"ERROR: Bad PRF= tag in header\n");
			 bad_header = 1;
		    }
	       }
	       else if (got_title && got_angle && got_prf) {
		    if (c != '\n' ) fprintf(stderr,"Warning: Extra characters in header on or about character %d  ==> %c\n",ibuf,c);
	       }
	       else {
		    fprintf(stderr,"ERROR: Parsing first line on or about character %d ==> %c\n",ibuf,c);
		    bad_header=1;
	       }
	  }
     }
     if ( bad_header ) {
	  fprintf(stdout,"\nUnsuccessful read of header.");
	  if( got_title || got_angle || got_prf ) {
	       fprintf(stdout,"  Header as parsed:\n");
	  }
	  else
	       fprintf(stdout," Unable to read title, azimuth or prf file name\n");
     }
     else {
	  fprintf(stdout,"\nHeader record successfully parsed:\n");
     }
     if(got_title) {
	  fprintf(stdout,"\tTitle: %s\n",title);
	  strcpy(pm->title,title);
     }
     if(got_angle) fprintf(stdout,"\tAngle: %s\n",angle);
     if(got_prf)   {
	  fprintf(stdout,"\tProfile file name: %s\n",prfname);
	  strcpy(pm->prf_name,prfname);
     }
	  
     fgets(yaxis_label,81,pfp);
     fprintf(stdout,"Y (horizontal) axis label: %s\n",yaxis_label);
     fgets(zaxis_label,81,pfp);
     fprintf(stdout,"Z (vertical) axis label: %s\n",zaxis_label);
     

     if ( (pm->half=alloc_half_space()) == NULL ) {
	  fprintf(stderr,"Out of memory allocating space for the half-space.\n");
	  exit (1);
     }
     fscanf(pfp,"%d %lf %lf %d", &id, &east_bind, &north_bind, &nparam);
     pm->half->east_bind=east_bind;
     pm->half->north_bind=north_bind;
     fprintf(stdout,"Model bind points are %lf & %lf \n",pm->half->east_bind,pm->half->north_bind);

     if(nparam<=0) {
	  fprintf(stdout,"ERROR: bogus number of parameters in half-space detected ==> %d\n",nparam);
	  exit (1);
     }
     if(nparam>10) {
	  fprintf(stdout,"WARNING: anomalously high no. of parameters detected ==> %d\n",nparam);
     }
     if( (pm->half->param_vals=(double *)alloc_params(nparam)) == NULL ) {
	  fprintf(stderr,"Out of memory allocating space for %d parameters\n", nparam);
	  exit (1);
     }

     fprintf(stdout,"Reading %d half-space parameter(s)\n", nparam);
     for (i=1;i<=nparam;i++) {
	  fscanf(pfp,"%lf",&(pm->half->param_vals[i]));
	  fprintf(stdout,"Half-space parameter %d, value= %lf\n",i,pm->half->param_vals[i]);
     }

     npoly=0;
     while (!done) {
	  if (fscanf(pfp,"%d %d %d",&id, &nvert, &nparam) !=3 ) {
	       pm->npoly=npoly;
	       done = 1;
	       continue;
	  }
	  npoly++;
	  if ( npoly==1 ) {
	       if( (pm->head_poly=alloc_poly())==NULL ) {
		    fprintf(stderr,"Out of memory allocating space for polygon %d\n",npoly);
		    exit (1); 
	       }
	       else {
		    curr_p = pm->head_poly;
	       }
	  }
	  else {
	       if ( (curr_p->next=alloc_poly())==NULL ) {
		    fprintf(stderr,"Out of memory allocating space for polygon %d\n",npoly);
		    exit (1);
	       }
	       curr_p = curr_p->next;
	  }
	  if(nparam<=0) {
	       fprintf(stdout,"ERROR: bogus number of parameters in polygon %d detected ==> %d\n",id,nparam);
	       exit (1);
	  }
	  if(nparam>10) {
	       fprintf(stdout,"WARNING: anomalously high no. of parameters %d detected in polygon %d ==> %d\n",nparam, id);
	  }
	  if( (curr_p->param_vals=(double *)alloc_params(nparam)) == NULL ) {
	       fprintf(stderr,"Out of memory allocating space for %d parameters for polygon id = %d\n", nparam,id);
	       exit (1);
	  }
	  if( (curr_p->yvert=alloc_nvert(nvert))==NULL) {
	       fprintf(stderr,"Out of memory allocating space for vertices for polygon id = %d.\n",id);
	       exit (1);
	  }
	  if( (curr_p->zvert=alloc_nvert(nvert))==NULL) {
	       fprintf(stderr,"Out of memory allocating space for vertices for polygon id = %d.\n",id);
	       exit (1);
	  }
	  curr_p->id=id;
	  curr_p->nvert=nvert;
	  curr_p->nparam=nparam;

	  for (i=1;i<=nparam;i++) {
	       fscanf(pfp,"%lf",&(curr_p->param_vals[i]));
	  }

	  for (i=1;i<=nvert;i++) {
	       fscanf(pfp,"%lf %lf",&(curr_p->yvert[i]),&(curr_p->yvert[i]));
	  }
	  if ( npoly % 100 == 0 ) fprintf(stdout,"Polygon %d\n",npoly);

     }
     fprintf(stdout,"Read of polygon file complete, number of polygons = %d\n",pm->npoly);
}
POLY_MODEL *alloc_model()
{
     POLY_MODEL *tmp;

     if ((tmp = (POLY_MODEL *) calloc(1,sizeof(POLY_MODEL))) == NULL) {
	  return NULL;
     }
     else {
	  return tmp;
     }

}

POLYGON *alloc_poly()
{
     POLYGON *tmp;

     if ((tmp = (POLYGON *) calloc(1,sizeof(POLYGON))) == NULL) {
	  return NULL;
     }
     else {
	  return tmp;
     }

}

HALF_SPACE *alloc_half_space()
{
     HALF_SPACE *tmp;

     if ((tmp = (HALF_SPACE *) calloc(1,sizeof(HALF_SPACE))) == NULL) {
	  return NULL;
     }
     else {
	  return tmp;
     }

}

double *alloc_vert(nvert)
int nvert;
{
     double *tmp;
     if ((tmp = (double *) calloc(nvert,sizeof(double))) == NULL ) {
	  return NULL;
     }
     else {
	  return (--tmp);
     }
}
double *alloc_params(nparam)
int nparam;
{
     double *tmp;
     if ((tmp = (double *) calloc(nparam,sizeof(double))) == NULL ) {
	  return NULL;
     }
     else {
	  return (--tmp);
     }
}
double *alloc_nvert(nvert)
int nvert;
{
     double *tmp;
     if ((tmp = (double *) calloc(nvert,sizeof(double))) == NULL ) {
	  return NULL;
     }
     else {
	  return (--tmp);
     }
}

/********  stod  ********/
  /* Written by Tim Cote
  converts string to double length float and makes status report.
  stod converts a string to a double and puts the result in the
  location pointed to by the pointer double_ptr.
  NOTE:  this routine only checks to make sure the number is valid and then
  gives it to atof to convert it to a double. WE have to check to make sure
  it is valid since atof only returns 0.0 if it is invalid. WE don't do the
  conversion because it is easier to get atof to do it.
  a null string ('\0') is an invalid number.
  Arguments:    pointer to character string, & pointer to double.
  Return:       YESNUM if sucessful, NONUM otherwise.
  */
#ifdef ANSI
int stod (char *string, double *double_ptr)
#else
int stod (string, double_ptr)
  char *string;
  double *double_ptr;
#endif
{
  int index = 0;        /*  index into the string  */
  int got_number = FALSE;  /*  do we have the non-exponent part of number  */
  int got_exp_sig = FALSE;  /*  do we have the exponent signal (e,E,d,D)  */
  int got_exponent = FALSE;  /*  do we have exponent since we got_exp_sig  */

  /*  a null string ('\0') is an invalid number.  */
  if (string[index] == '\0')
    return (NONUM);

  /*  check for a sign character  */
  if (string[index] == '-' || string[index] == '+')
    index++;

  /*  check for the non-decimal, non-exponent part of number (ie 1st part)  */
  while (string[index] >= '0' && string[index] <= '9')
  {
    got_number = TRUE;
    index++;
  }

  /*  check for the decimal point  */
  if (string[index] == '.')
  {
    index++;
    /*  check for the decimal part of number  */
    while (string[index] >= '0' && string[index] <= '9')
    {
      got_number = TRUE;
      index++;
    }
  }

  /*  at this point we should have got the non-exponent part of the number  */
  /*  if got_number is not true by this point then the number is garbage  */
  if (!(got_number))
    return (NONUM);

  /*  check for the exponent signal  */
  if (string[index] == 'e'  ||  string[index] == 'E'  ||
       string[index] == 'd'  ||  string[index] == 'D')
  {
    got_exp_sig = TRUE;
    index++;
    /*  check for a sign character  */
    if (string[index] == '-' || string[index] == '+')
      index++;
    /*  check for the exponent  */
    while (string[index] >= '0' && string[index] <= '9')
    {
      got_exponent = TRUE;
      index++;
    }
  }

  /*  if we got the exponent signal then we should have got the exponent  */
  /*  if we didn't then we got something like 1d- or 123E which is bad  */
  if (got_exp_sig  &&  !(got_exponent))
    return (NONUM);

  /*  if we have got this far and the current character is not a '\0' then  */
  /*  we have garbage in the string & therefore we have an invalid number  */
  if (string[index] != '\0')
    return (NONUM);

  /*  we have a valid number so convert it  */
  *double_ptr = atof (string);
  return (YESNUM);
}


