/*----------------------------------------------------------------------*\
 | Output routines for MapInfo .mif and .mid files						|
\*----------------------------------------------------------------------*/

#include <stdio.h>

#include "lgo.h"
#include "mif.h"
#include "attr.h"

/*----------------------------------------------------------------------*\
 | External data
\*----------------------------------------------------------------------*/

extern struct attr search;
extern int verbose;

/*----------------------------------------------------------------------*\
 | External procedures
\*----------------------------------------------------------------------*/

extern struct node *find_node (struct category *c, int id);
extern struct area *find_area (struct category *c, int id);
extern struct line *find_line (struct category *c, int id);

/*----------------------------------------------------------------------*\
 | Static procedures
\*----------------------------------------------------------------------*/

static void mif_write_one_node (FILE *mif, FILE *mid, struct node *p);
static void mif_write_one_area (FILE *mif, FILE *mid, struct category *c, int i);
static void mif_write_one_line (FILE *mif, FILE *mid, struct line *p);

/*----------------------------------------------------------------------*\
 | Static data
\*----------------------------------------------------------------------*/

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

/*----------------------------------------------------------------------*\
 | Write standard information at the beginning of the mif file			|
\*----------------------------------------------------------------------*/

void mif_write_header (FILE *mif) {
	if (mif) {
		fputs ("version 1\n",mif);
		fputs ("delimiter \"@\"\n",mif);
		fputs ("columns 9\n",mif);
		fputs ("  name char(64)\n",mif);
		fputs ("  dept char(8)\n",mif);
		fputs ("  bureau char(8)\n",mif);
		fputs ("  attr1_major smallint\n",mif);
		fputs ("  attr1_minor smallint\n",mif);
		fputs ("  attr2_major smallint\n",mif);
		fputs ("  attr2_minor smallint\n",mif);
		fputs ("  attr3_major smallint\n",mif);
		fputs ("  attr3_minor smallint\n",mif);
		fputs ("data\n",mif);
		}
	}

/*----------------------------------------------------------------------*\
 | Output routines for nodes											|
\*----------------------------------------------------------------------*/

static void mif_write_one_node (FILE *mif, FILE *mid, struct node *p) {
	int j;
	struct attr_table *a;

	/*------------------------------------------------------------------*\
	 | Output attribute information										|
	\*------------------------------------------------------------------*/

	if (p->attr_cnt) {
		a = attribute (p->attr[0]);
		fprintf (mid,"name@%s@%s",a->dept,a->bureau);
		for (j=0; j < min(p->attr_cnt,3); j++)
			fprintf (mid,"@%d@%d",p->attr[j].major,p->attr[j].minor);
		fprintf (mid,"\n");
		}
	else
		fprintf (mid,"name@N/A@N/A\n");

	/*------------------------------------------------------------------*\
	 | Output coordinates												|
	\*------------------------------------------------------------------*/

	fprintf (mif,"POINT\t%.6lf\t%.6lf\n",p->x,p->y);
	}

void mif_write_nodes (FILE *mif, FILE *mid, struct category *c, struct output output) {
	int i;
	struct node *p;

	if (!mif) return;

	switch (output.nodes) {
		case ALL:
			for (i=0; i < c->node_cnt; i++) mif_write_one_node (mif,mid,c->node[i]);
			break;
		case SEARCH:
			for (i=0; i < c->node_cnt; i++)
				if (c->node[i]->attr_cnt)
					if (c->node[i]->attr[0].major == search.major)
						if (c->node[i]->attr[0].minor == search.minor)
							mif_write_one_node (mif,mid,c->node[i]);
			break;
		case ATTR_ONLY:
			for (i=0; i < c->node_cnt; i++)
				if (c->node[i]->attr_cnt) mif_write_one_node (mif,mid,c->node[i]);
			break;
		case NONE:
			break;
		default:
			break;
		}
	}

/*----------------------------------------------------------------------*\
 | Output routines for areas											|
\*----------------------------------------------------------------------*/

static void mif_write_one_area (FILE *mif, FILE *mid, struct category *c, int i) {
	int j,k,m,n,dir,nc;
	struct area *p;
	struct line *L;
	struct attr_table *a;

	p = c->area[i];

	/*------------------------------------------------------------------*\
	 | Here we ignore areas that have attributes but do not have		|
	 | associated boundary lines. These are regions too small to		|
	 | describe by their boundaries. Ideally they should be output as	|
	 | POINTs.															|
	\*------------------------------------------------------------------*/

	if (p->line_list_cnt == 0) return;

	/*------------------------------------------------------------------*\
	 | Output attribute information										|
	\*------------------------------------------------------------------*/

	if (p->attr_cnt) {
		if (a = attribute (p->attr[0])) {
			fprintf (mid,"name@%s@%s",a->dept,a->bureau);
			for (j=0; j < min(p->attr_cnt,3); j++) {
				fprintf (mid,"@%d@%d",p->attr[j].major,p->attr[j].minor);
				}
			fprintf (mid,"\n");
			}
		else
			fprintf (mid,"DLG section boundary@N/A@N/A\n");
		}
	else
		fprintf (mid,"name@N/A@N/A\n");

	/*------------------------------------------------------------------*\
	 | Output coordinates												|
	\*------------------------------------------------------------------*/

	fprintf (mif,"REGION %d\n",1 + p->island_cnt);

	/*------------------------------------------------------------------*\
	 | First count the number of coords in the area excluding islands.	|
	\*------------------------------------------------------------------*/

	nc = 0;
	for (n=0; n < p->line_list_cnt; n++) {
		if (p->line[n] == 0) break;

		/*--------------------------------------------------------------*\
		 | Find the line whose ID equals the number in the line list	|
		 | (ignore the sign here).										|
		\*--------------------------------------------------------------*/

		if (L = find_line (c,abs(p->line[n]))) nc += L->coord_cnt;
		else
			printf ("Warning: area %d refers to nonexistent line %d\n",i,m);
		}
	fprintf (mif,"%5d\n",nc);

	/*------------------------------------------------------------------*\
	 | Then output the coordinates themselves.							|
	\*------------------------------------------------------------------*/

	for (j=0; j < n; j++) {
		m = p->line[j];
		dir = m/abs(m);
		if (L = find_line (c,abs(m))) {
			if (dir > 0)
				for (k=0; k < L->coord_cnt; k++)
					fprintf (mif,"%.6lf\t%.6lf\n",L->coord[k].x,L->coord[k].y);
			else
				for (k=L->coord_cnt-1; k >= 0; k--)
					fprintf (mif,"%.6lf\t%.6lf\n",L->coord[k].x,L->coord[k].y);
			}
		else
			printf ("Warning: Area %d refers to nonexistent line %d\n",i,m);
		}

	/*------------------------------------------------------------------*\
	 | If there are islands, output each island as a part of the region	|
	\*------------------------------------------------------------------*/

	if (p->island_cnt > 0) {
		int ni,beg,end;
		end = n;
		for (ni=0; ni < p->island_cnt; ni++) {

			/*----------------------------------------------------------*\
			 | Count lines in this island, and count coordinates too.	|
			\*----------------------------------------------------------*/

			beg = end + 1;		/* p->line[end] will always be zero here */
			if (beg > p->line_list_cnt) {
				if (ni < p->island_cnt) printf ("\nWarning: Area %d has %d islands, but too few elements in its line list.\n\n",i,p->island_cnt);
				break;
				}

			nc = 0;
			for (end=beg; p->line[end] && end < p->line_list_cnt; end++) {
				if (L = find_line (c,abs(p->line[end]))) nc += L->coord_cnt;
				else
					printf ("Warning: Island %d of area %d refers to nonexistent line %d\n",ni,i,m);
				}
			fprintf (mif,"%d\n",nc);

			/*----------------------------------------------------------*\
			 | Output the coordinates themselves.						|
			\*----------------------------------------------------------*/

			for (j=beg; j < end; j++) {
				m = p->line[j];
				dir = m/abs(m);
				if (L = find_line (c,abs(m))) {
					if (dir > 0)
						for (k=0; k < L->coord_cnt; k++)
							fprintf (mif,"%.6lf\t%.6lf\n",L->coord[k].x,L->coord[k].y);
					else
						for (k=L->coord_cnt-1; k >= 0; k--)
							fprintf (mif,"%.6lf\t%.6lf\n",L->coord[k].x,L->coord[k].y);
					}
				else
					printf ("Warning: island %d of area %d refers to nonexistent line %d\n",ni,i,m);
				}
			}
		}
	}

void mif_write_areas (FILE *mif, FILE *mid, struct category *c, struct output output) {
	int i,j;

	if (!mif) return;

	switch (output.areas) {
		case ALL:
			for (i=0; i < c->area_cnt; i++) mif_write_one_area (mif,mid,c,i);
			break;
		case SEARCH:
			for (i=0; i < c->area_cnt; i++)
				if (c->area[i]->attr_cnt)
					for (j=0; j < c->area[i]->attr_cnt; j++)
						if (c->area[i]->attr[j].major == search.major)
							if (c->area[i]->attr[j].minor == search.minor)
								mif_write_one_area (mif,mid,c,i);
			break;
		case ATTR_ONLY:
			for (i=0; i < c->area_cnt; i++)
				if (c->area[i]->attr_cnt && c->area[i]->attr[0].major)
					mif_write_one_area (mif,mid,c,i);
			break;
		case NONE:
			break;
		default:
			break;
		}
	}

/*----------------------------------------------------------------------*\
 | Output routines for lines											|
\*----------------------------------------------------------------------*/

static void mif_write_one_line (FILE *mif, FILE *mid, struct line *p) {
	int j;
	struct attr_table *a;

	/*------------------------------------------------------------------*\
	 | Output attribute information										|
	\*------------------------------------------------------------------*/

	if (p->attr_cnt) {
		if (a = attribute (p->attr[0])) {
			fprintf (mid,"name@%s@%s",a->dept,a->bureau);
			for (j=0; j < min(p->attr_cnt,3); j++)
				fprintf (mid,"@%d@%d",p->attr[j].major,p->attr[j].minor);
			fprintf (mid,"\n");
			}
		else
			fprintf (mid,"name@N/A@N/A\n");
		}
	else
		fprintf (mid,"name@N/A@N/A\n");

	/*------------------------------------------------------------------*\
	 | Output coordinates												|
	\*------------------------------------------------------------------*/

	fprintf (mif,"PLINE %d\n",p->coord_cnt);
	for (j=0; j < p->coord_cnt; j++)
		fprintf (mif,"%.6lf\t%.6lf\n",p->coord[j].x,p->coord[j].y);
	}

void mif_write_lines (FILE *mif, FILE *mid, struct category *c, struct output output) {
	int i;

	if (!mif) return;

	switch (output.lines) {
		case ALL:
			for (i=0; i < c->line_cnt; i++) mif_write_one_line (mif,mid,c->line[i]);
			break;
		case SEARCH:
			for (i=0; i < c->line_cnt; i++)
				if (c->line[i]->attr_cnt)
					if (c->line[i]->attr[0].major == search.major)
						if (c->line[i]->attr[0].minor == search.minor)
							mif_write_one_line (mif,mid,c->line[i]);
			break;
		case ATTR_ONLY:
			for (i=0; i < c->line_cnt; i++)
				if (c->line[i]->attr_cnt) mif_write_one_line (mif,mid,c->line[i]);
			break;
		case NONE:
			break;
		default:
			break;
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/
