/*----------------------------------------------------------------------*\
 | Program to write ungenerated ARC/INFO polygons to MIF file			|
\*----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLEN		128
#define MAXPTS		8192
#define MAXPOLY		4096

static double *xx = NULL;
static double *yy = NULL;
static long maxpts = 0;
static long n = 0;
static int id;
static double cx,cy;

struct poly {
	double *x;
	double *y;
	int n;
	struct poly *next;
	};

struct region {
	double cx;
	double cy;
	struct poly *p;
	};

static struct region region [MAXPOLY];

static void add_point (double x, double y) {
	if (n < maxpts) {
		xx[n] = x;
		yy[n] = y;
		n++;
		}
	else {
		maxpts += MAXPTS;
		xx = (double *) realloc (xx,maxpts);
		yy = (double *) realloc (yy,maxpts);
		if (!xx || !yy) {
			fprintf (stderr,"Error: could not enlarge xx and yy to %ld\n",maxpts);
			exit (1);
			}
		xx[n] = x;
		yy[n] = y;
		n++;
		}
	}

static void write_mif_header (FILE *out) {
	fprintf (out,"VERSION 1\n");
	fprintf (out,"COLUMNS 1\n");
	fprintf (out,"    ID integer\n");
	fprintf (out,"DATA\n");
	}

static void output_region (FILE *out, struct region *r) {
	int i,np;
	struct poly *p;

	np = 0;
	for (p=r->p; p; p=p->next) np++;
	fprintf (out,"REGION %d\n",np);
	for (p=r->p; p; p=p->next) {
		fprintf (out,"%4d\n",p->n);
		for (i=0; i < p->n; i++)
			fprintf (out,"%.6lf\t%.6lf\n",p->x[i],p->y[i]);
		}
	fprintf (out,"CENTER %lf %lf\n",r->cx,r->cy);
	}

static void resolve_topological_problems (struct region *r) {
	struct poly *o, *p, *q;

	/*------------------------------------------------------------------*\
	 | A valid polygon segment has three or more points.  Any segments	|
	 | that do not should be deleted from the list and discarded.		|
	\*------------------------------------------------------------------*/

	/*------------------------------------------------------------------*\
	 | The first segment is a special case, because its address is put	|
	 | into r->p, not p->next for some p.								|
	\*------------------------------------------------------------------*/

	p = r->p;
	if (p->n < 3) {
		while (p && p->n < 3) {
			o = p;
			p = p->next;
			if (o->x) free (o->x);
			if (o->y) free (o->y);
			free (o);
			}
		r->p = p;
		}

	/*------------------------------------------------------------------*\
	 | Walk through the other polygons, discarding invalid segments		|
	\*------------------------------------------------------------------*/

	p = r->p;
	while (p) {
		q = p->next;
		while (q && q->n < 3) {
			o = q;
			q = q->next;
			if (o->x) free (o->x);
			if (o->y) free (o->y);
			free (o);
			}
		p->next = q;
		p = p->next;
		}
	}

main (int argc, char *argv[]) {
	int i,j,k;
	int count, line_count;
	char input_file [FILENAME_MAX];
	char mif_file [FILENAME_MAX];
	FILE *in, *mif, *mid;
	char string [MAXLEN];
	char *s;
	double x,y;
	struct poly *p;
	int flipx = 0;
	int flipy = 0;
	int verbose = 0;

	if (argc < 2) {
		fprintf (stderr,"Usage: %s [-v] [-flip x|y] input_file\n",argv[0]);
		exit (0);
		}

	*input_file = 0;

	for (i=1; i < argc; i++)
		if (memcmp (argv[i],"-v",2) == 0) {
			verbose = 1;
			}
		else
			if (memcmp (argv[i],"-flip",5) == 0) {
				i++;
				if (toupper (*argv[i]) == 'X') flipx = 1;
				else
					if (toupper (*argv[i]) == 'Y') flipy = 1;
					else {
						fprintf (stderr,"Error: expected 'x' or 'y' after -flip in command line\n");
						exit (1);
						}
				}
			else
				strcpy (input_file,argv[i]);

	if (*input_file) {
		if (in = fopen (input_file,"r")) {
			strcpy (mif_file,input_file);
			strcat (mif_file,".mif");
			if (mif = fopen (mif_file,"w")) {
				write_mif_header (mif);
				j = 0;

				maxpts = MAXPTS;
				xx = (double *) malloc (maxpts * sizeof (double));
				yy = (double *) malloc (maxpts * sizeof (double));
				if (!xx || !yy) {
					fprintf (stderr,"Error: could not allocate xx and yy\n");
					exit (1);
					}

				for (i=0; i < MAXPOLY; i++) {
					region[i].cx = 0.0;
					region[i].cy = 0.0;
					region[i].p = NULL;
					}

				line_count = 0;
				while (fgets (string,MAXLEN,in)) {
					if (s = strrchr (string,'\n')) *s = 0;
					line_count++;
					if (strstr (string,"END")) {

						if (region[id].cx == 0 && region[id].cy == 0) {
							region[id].cx = cx;
							region[id].cy = cy;
							}

						/*----------------------------------------------*\
						 | If this is the end of the current segment,	|
						 | find the corresponding element in the region	|
						 | array, and add the current segment's data to	|
						 | the linked list of segments for that region.	|
						\*----------------------------------------------*/

						if (n > 0) {
							struct poly *new;

							/*------------------------------------------*\
							 | Create a poly structure to hold segment	|
							 | and put the current segment's data in it	|
							\*------------------------------------------*/

							if (new = (struct poly *) malloc (sizeof (struct poly))) {
								new->n = n;
								new->x = (double *) malloc (n * sizeof (double));
								new->y = (double *) malloc (n * sizeof (double));
								if (!new->x || !new->y) {
									fprintf (stderr,"Error: unable to allocate space for %ld points at line %ld\n",n,line_count);
									exit (1);
									}
								for (i=0; i < n; i++) {
									new->x[i] = xx[i];
									new->y[i] = yy[i];
									}
								new->next = NULL;
								}
							else {
								fprintf (stderr,"Error: unable to allocate poly structure at line %ld\n",line_count);
								exit (1);
								}

							/*------------------------------------------*\
							 | Find the right place to wire the new		|
							 | poly structure in.						|
							\*------------------------------------------*/

							if (region[id].p) {
								for (p=region[id].p; p->next; p=p->next);
								p->next = new;
								}
							else
								region[id].p = new;

							}
						}
					else {
						double a,b,c;
						int ia = 0;
						char copy[MAXLEN];

						strcpy (copy,string);

						/*----------------------------------------------*\
						 | If this is not an "END" line, then it is		|
						 | (a) the beginning of an island, (b) a point,	|
						 | or (c) a separate polygon.					|
						\*----------------------------------------------*/

						a = b = c = 0.0;
						s = string;
						i = 0;
						if (s = strtok (string," ")) {
							i++;
							ia = atoi (s);
							a = strtod (s,NULL);
							}
						if (s = strtok (NULL," ")) {
							i++;
							b = strtod (s,NULL);
							}
						if (s = strtok (NULL," ")) {
							i++;
							c = strtod (s,NULL);
							}
						switch (i) {
							case 1:
								fprintf (stderr,"%s\n",copy);
								if (ia == -99999) n = 0;
								else
									fprintf (stderr,"Warning: I don't understand line %ld: \"%s\"\n",line_count,string);
								break;
							case 2:
								if (flipx) a = -a;
								if (flipy) b = -b;
								add_point (a,b);
								break;
							case 3:
								fprintf (stderr,"%s\n",copy);
								id = ia;
								if (flipx) b = -b;
								if (flipy) c = -c;
								cx = b;
								cy = c;
								n = 0;
								break;
							default:
								break;
							}
						}
					}

				/*------------------------------------------------------*\
				 | Write the output file								|
				\*------------------------------------------------------*/

				for (i=0; i < MAXPOLY; i++) {
					if (region[i].p) {

						if (verbose) {
							printf ("%5d %12.6lf %12.6lf ",i,region[i].cx,region[i].cy);
							for (p=region[i].p; p; p=p->next) printf ("%4d ",p->n);
							printf ("\n");
							}

						resolve_topological_problems (&region[i]);

						if (verbose) {
							printf ("%5d %12.6lf %12.6lf ",i,region[i].cx,region[i].cy);
							for (p=region[i].p; p; p=p->next) printf ("%4d ",p->n);
							printf ("\n");
							}

						output_region (mif,&region[i]);
						}
					}

				fclose (mif);
				}
			else
				fprintf (stderr,"Error: could not create mif file %s\n",mif_file);
			fclose (in);
			}
		else {
			fprintf (stderr,"Error: could not open input file %s\n",input_file);
			exit (1);
			}
		}
	else {
		fprintf (stderr,"Usage: %s input_file\n",argv[0]);
		exit (0);
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/
