/*----------------------------------------------------------------------*\
 | Output routines for BNA format (AtlasPro)							|
\*----------------------------------------------------------------------*/

#include <stdio.h>

#include "lgo.h"
#include "bna.h"
#include "attr.h"

/*----------------------------------------------------------------------*\
 | External procedures
\*----------------------------------------------------------------------*/

extern char *state_name (int FIPS_code);
extern char *county_name (int state_code, int county_code);

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);

/*----------------------------------------------------------------------*\
 | External data
\*----------------------------------------------------------------------*/

extern int verbose;
extern struct attr search;
extern char file_name[];
extern int target;

/*----------------------------------------------------------------------*\
 | Static procedures
\*----------------------------------------------------------------------*/

static void bna_write_one_node (FILE *geo, FILE *attr, struct node *p);
static void bna_write_one_area (FILE *geo, FILE *attr, struct category *c, int i);
static void bna_write_one_line (FILE *geo, FILE *attr, struct line *p);

/*----------------------------------------------------------------------*\
 | Static data
\*----------------------------------------------------------------------*/

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

/*----------------------------------------------------------------------*\
 | Write standard information at the beginning of the BNA file			|
\*----------------------------------------------------------------------*/

void bna_write_header (FILE *attr) {
	if (attr) {
		fprintf (attr,"ID\tnumber\tDepartment\tBureau\t");
		fprintf (attr,"Major 1\tminor 1\tMajor 2\tminor 2\tMajor 3\tminor 3\n");
		}
	}

/*----------------------------------------------------------------------*\
 | Output routines for nodes											|
\*----------------------------------------------------------------------*/

static void bna_write_one_node (FILE *geo, FILE *attr, struct node *p) {
	int j;
	struct attr_table *a;
	char primary_id[MAXLEN];

	/*------------------------------------------------------------------*\
	 | Output attribute information										|
	\*------------------------------------------------------------------*/

	sprintf (primary_id,"%s-N%04d",file_name,p->id);
	fprintf (attr,"%s\t%d\t",primary_id,p->id);
	if (p->attr_cnt) {
		a = attribute (p->attr[0]);
		fprintf (attr,"%s\t%s",a->dept,a->bureau);
		for (j=0; j < min(p->attr_cnt,3); j++)
			fprintf (attr,"\t%d\t%d",p->attr[j].major,p->attr[j].minor);
		fprintf (attr,"\n");
		}
	else
		fprintf (attr,"N/A\tN/A\n");

	/*------------------------------------------------------------------*\
	 | Output coordinates												|
	\*------------------------------------------------------------------*/

	if (verbose) printf ("%s\tnode\t1\n",primary_id);
	fprintf (geo,"%s\tnode\t1\n",primary_id);
	fprintf (geo,"%.6lf\t%.6lf\n",p->x,p->y);
	}

void bna_write_nodes (FILE *geo, FILE *attr, struct category *c, struct output output) {
	int i;
	struct node *p;

	if (!geo) return;

	switch (output.nodes) {
		case ALL:
			for (i=0; i < c->node_cnt; i++) bna_write_one_node (geo,attr,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)
							bna_write_one_node (geo,attr,c->node[i]);
			break;
		case ATTR_ONLY:
			for (i=0; i < c->node_cnt; i++)
				if (c->node[i]->attr_cnt) bna_write_one_node (geo,attr,c->node[i]);
			break;
		case NONE:
			break;
		default:
			break;
		}
	}

/*----------------------------------------------------------------------*\
 | Output routines for areas											|
\*----------------------------------------------------------------------*/

static void bna_write_one_area_0 (FILE *geo, FILE *attr, struct category *c, int i) {
	int j,k,m,n,dir,nc;
	struct area *p;
	struct line *L;
	struct attr_table *a;
	int state_code,county_code;
	char *name;
	char primary_id[MAXLEN];

	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										|
	\*------------------------------------------------------------------*/

	name = "area";

	sprintf (primary_id,"%s-A%04d",file_name,p->id);
	fprintf (attr,"%s\t%d\t",primary_id,p->id);
	if (p->attr_cnt) {

		/*--------------------------------------------------------------*\
		 | If you are looking at political boundaries, name the area.	|
		\*--------------------------------------------------------------*/

		state_code = county_code = 0;
		for (j=0; j < p->attr_cnt; j++) {
			if (p->attr[j].major == 91) state_code = p->attr[j].minor;
			if (p->attr[j].major == 92) county_code = p->attr[j].minor;
			}
		if (state_code && county_code) name = county_name (state_code,county_code);
		else
			if (state_code) name = state_name (state_code);

		/*--------------------------------------------------------------*\
		 | Output the result to the attribute file						|
		\*--------------------------------------------------------------*/

		if (a = attribute (p->attr[0])) {
			fprintf (attr,"%s\t%s",a->dept,a->bureau);
			for (j=0; j < min(p->attr_cnt,3); j++) {
				fprintf (attr,"\t%d\t%d",p->attr[j].major,p->attr[j].minor);
				}
			fprintf (attr,"\n");
			}
		else
			fprintf (attr,"DLG section boundary\tN/A\n");
		}
	else
		fprintf (attr,"N/A\tN/A\n");

	/*------------------------------------------------------------------*\
	 | Output geography													|
	\*------------------------------------------------------------------*/

	/*------------------------------------------------------------------*\
	 | 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 (geo,"%s\t%s\t%d\n",primary_id,name,nc);
	if (verbose) printf ("%s\t%s\t%d\n",primary_id,name,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 (geo,"%.6lf\t%.6lf\n",L->coord[k].x,L->coord[k].y);
			else
				for (k=L->coord_cnt-1; k >= 0; k--)
					fprintf (geo,"%.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 separate area with	|
	 | the same primary id.												|
	\*------------------------------------------------------------------*/

	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 (geo,"%s\tisland\t%d\n",primary_id,nc);
			if (verbose) printf ("%s\tisland\t%d\n",primary_id,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 (geo,"%.6lf\t%.6lf\n",L->coord[k].x,L->coord[k].y);
					else
						for (k=L->coord_cnt-1; k >= 0; k--)
							fprintf (geo,"%.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);
				}
			}
		}
	}

static void bna_write_one_area_1 (FILE *geo, FILE *attr, struct category *c, int i) {
	int j,k,m,n,dir,nc;
	struct area *p;
	struct line *L;
	struct attr_table *a;
	int state_code,county_code;
	char *name;
	char primary_id[MAXLEN];
	struct point *first;

	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										|
	\*------------------------------------------------------------------*/

	sprintf (primary_id,"%s-A%04d",file_name,p->id);
	name = primary_id;
	fprintf (attr,"%s\t%d\t",primary_id,p->id);
	if (p->attr_cnt) {

		/*--------------------------------------------------------------*\
		 | If you are looking at political boundaries, name the area.	|
		\*--------------------------------------------------------------*/

		state_code = county_code = 0;
		for (j=0; j < p->attr_cnt; j++) {
			if (p->attr[j].major == 91) state_code = p->attr[j].minor;
			if (p->attr[j].major == 92) county_code = p->attr[j].minor;
			}
		if (state_code && county_code) name = county_name (state_code,county_code);
		else
			if (state_code) name = state_name (state_code);

		/*--------------------------------------------------------------*\
		 | Output the result to the attribute file						|
		\*--------------------------------------------------------------*/

		if (a = attribute (p->attr[0])) {
			fprintf (attr,"%s\t%s",a->dept,a->bureau);
			for (j=0; j < min(p->attr_cnt,3); j++) {
				fprintf (attr,"\t%d\t%d",p->attr[j].major,p->attr[j].minor);
				}
			fprintf (attr,"\n");
			}
		else
			fprintf (attr,"DLG section boundary\tN/A\n");
		}
	else
		fprintf (attr,"N/A\tN/A\n");

	/*------------------------------------------------------------------*\
	 | Output geography													|
	\*------------------------------------------------------------------*/

	/*------------------------------------------------------------------*\
	 | First count the number of coords in the area, including islands.	|
	\*------------------------------------------------------------------*/

	nc = 0;
	for (n=0; n < p->line_list_cnt; n++) {

		/*--------------------------------------------------------------*\
		 | If you encounter a zero line index, add one coordinate.		|
		\*--------------------------------------------------------------*/

		if (p->line[n] == 0) nc++;
		else {

			/*----------------------------------------------------------*\
			 | Find the line whose ID equals the number in the line		|
			 | list (ignore the sign here).								|
			 | Add the number of coordinates in the line.				|
			\*----------------------------------------------------------*/

			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);
			}
		}

	/*------------------------------------------------------------------*\
	 | If there are islands, you'll need one more point at the end.		|
	\*------------------------------------------------------------------*/

	if (p->island_cnt) nc++;

	/*------------------------------------------------------------------*\
	 | Write the area header to the bna file; echo if verbose.			|
	\*------------------------------------------------------------------*/

	fprintf (geo,"%s\t%s\t%d\n",primary_id,name,nc);
	if (verbose) printf ("%s\t%s\t%d\n",primary_id,name,nc);

	/*------------------------------------------------------------------*\
	 | Output the coordinates themselves.								|
	 |																	|
	 | "first" is a pointer to the first point. It should appear at the	|
	 | beginning and end of the area coordinate list, and it should		|
	 | appear before each island.										|
	\*------------------------------------------------------------------*/

	first = NULL;

	for (j=0; j < n; j++) {
		m = p->line[j];

		/*--------------------------------------------------------------*\
		 | If you encounter a zero line index, output the first point.	|
		 | Otherwise, output all of the points in this line.			|
		\*--------------------------------------------------------------*/

		if (m == 0 && first) fprintf (geo,"%.6lf\t%.6lf\n",first->x,first->y);
		else {
			dir = m/abs(m);
			if (L = find_line (c,abs(m))) {
				if (dir > 0) {
					if (!first) first = &L->coord[0];
					for (k=0; k < L->coord_cnt; k++)
						fprintf (geo,"%.6lf\t%.6lf\n",L->coord[k].x,L->coord[k].y);
					}
				else {
					if (!first) first = &L->coord[L->coord_cnt - 1];
					for (k=L->coord_cnt-1; k >= 0; k--)
						fprintf (geo,"%.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 were islands, write out the first point again to close.	|
	\*------------------------------------------------------------------*/

	if (p->island_cnt && first) fprintf (geo,"%.6lf\t%.6lf\n",first->x,first->y);
	}

void bna_write_areas (FILE *geo, FILE *attr, struct category *c, struct output output) {
	int i,j;
	void (*bna_write_one_area) (FILE *geo, FILE *attr, struct category *c, int i);

	if (!geo) return;

	switch (target) {
		case Tactician:
			bna_write_one_area = bna_write_one_area_1;
			break;
		case AtlasPro:
		default:
			bna_write_one_area = bna_write_one_area_0;
			break;
		}

	switch (output.areas) {
		case ALL:
			for (i=0; i < c->area_cnt; i++) (*bna_write_one_area) (geo,attr,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)
								(*bna_write_one_area) (geo,attr,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)
					(*bna_write_one_area) (geo,attr,c,i);
			break;
		case NONE:
			break;
		default:
			break;
		}
	}

/*----------------------------------------------------------------------*\
 | Output routines for lines											|
\*----------------------------------------------------------------------*/

static void bna_write_one_line (FILE *geo, FILE *attr, struct line *p) {
	int j;
	struct attr_table *a;
	char primary_id[MAXLEN];

	/*------------------------------------------------------------------*\
	 | Output attribute information										|
	\*------------------------------------------------------------------*/

	sprintf (primary_id,"%s-L%04d",file_name,p->id);
	fprintf (attr,"%s\t%d\t",primary_id,p->id);
	if (p->attr_cnt) {
		a = attribute (p->attr[0]);
		fprintf (attr,"%s\t%s",a->dept,a->bureau);
		for (j=0; j < min(p->attr_cnt,3); j++)
			fprintf (attr,"\t%d\t%d",p->attr[j].major,p->attr[j].minor);
		fprintf (attr,"\n");
		}
	else
		fprintf (attr,"N/A\tN/A\n");

	/*------------------------------------------------------------------*\
	 | Output coordinates												|
	\*------------------------------------------------------------------*/

	if (verbose) printf ("%s\tline\t%d\n",primary_id,p->coord_cnt);
	fprintf (geo,"%s\tline\t%d\n",primary_id,p->coord_cnt);
	for (j=0; j < p->coord_cnt; j++)
		fprintf (geo,"%.6lf\t%.6lf\n",p->coord[j].x,p->coord[j].y);
	}

void bna_write_lines (FILE *geo, FILE *attr, struct category *c, struct output output) {
	int i;

	if (!geo) return;

	switch (output.lines) {
		case ALL:
			for (i=0; i < c->line_cnt; i++) bna_write_one_line (geo,attr,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)
							bna_write_one_line (geo,attr,c->line[i]);
			break;
		case ATTR_ONLY:
			for (i=0; i < c->line_cnt; i++)
				if (c->line[i]->attr_cnt) bna_write_one_line (geo,attr,c->line[i]);
			break;
		case NONE:
			break;
		default:
			break;
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/
