/*
 * Copyright (C) 2015 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#define DEBUG	0

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

struct sig {
	struct sig *next;
	struct sig *prev;

	const char *name;
	const char *type;
	int is_port;
};
struct port {
	struct port *next;
	struct port *prev;

	char *name;
	char *type;

	struct sig *sig;
};

struct box {
	int x;
	int y;
	int width;
	int height;
};
struct comp {
	int x;
	int y;
	unsigned int rotate;
	unsigned int mirror;
	char *sym_file;

	struct el *con_first;
	struct el *con_last;
};
struct net {
	int x0;
	int y0;
	int x1;
	int y1;

	struct sig *sig;
};
struct pin {
	int x0;
	int y0;
	int x1;
	int y1;
	unsigned int color;

	struct sig *sig;
};
struct text {
	int x;
	int y;
	unsigned int color;
	unsigned int size;
	unsigned int visible;
	unsigned int hide;	/* 1: Hide name, 2: Hide value */
	unsigned int angle;
	unsigned int align;
	char *str[128];
};
struct bus {
	int x0;
	int y0;
	int x1;
	int y1;
	unsigned int color;

	struct sig *sig;
};
struct el {
	struct el *prev;
	struct el *next;

	struct el *child_first;
	struct el *child_last;

	enum {
		BOX, COMP, NET, PIN, TEXT, BUS
	} type;
	union {
		struct box box;
		struct comp comp;
		struct net net;
		struct pin pin;
		struct text text;
		struct bus bus;
	} u;
};

char *progname;
char *idir[100];
unsigned int nidirs = 0;
int opt_M;
char *inname;

static const char *
ident_tmp(void)
{
	static unsigned int nr = 0;
	char tmp[100];

	sprintf(tmp, "tmp%06u", nr++);
	return strdup(tmp);
}

int
_ungetc(int c, FILE *stream)
{
	if (c != EOF) {
		return ungetc(c, stream);
	} else {
		return c;
	}
}

void
skip_white(FILE *fp)
{
	int c;

	c = fgetc(fp);
	while (c == '\t'
	    || c == '\n'
	    || c == '\r'
	    || c == ' ') {
		c = fgetc(fp);
	}
	_ungetc(c, fp);
}

int
read_char(FILE *fp, char *cp)
{
	int c;

	skip_white(fp);

	c = fgetc(fp);

	if (c == EOF
	 || c == '\0') {
		return EOF;
	} else {
		*cp = c;
		return 0;
	}
}

int
read_int(FILE *fp, int *ip)
{
	int sign;
	int c;

	skip_white(fp);

	*ip = 0;
	c = fgetc(fp);
	if (c == '-') {
		sign = -1;
		c = fgetc(fp);
	} else {
		sign = 1;
	}
	while ('0' <= c && c <= '9') {
		*ip *= 10;
		*ip += c - '0';
		c = fgetc(fp);
	}
	_ungetc(c, fp);

	*ip *= sign;

	return 0;
}

int
read_ident(FILE *fp, char *ident)
{
	int c;
	int x;

	skip_white(fp);

	x = 0;
	c = fgetc(fp);
	while (c != EOF
	    && c != '\0'
	    && c != '\t'
	    && c != '\n'
	    && c != '\r'
	    && c != ' ') {
		ident[x++] = c;
		c = fgetc(fp);
	}
	ident[x] = '\0';
	_ungetc(c, fp);

	return 0;
}

int
read_line(FILE *fp, char *ident)
{
	int c;
	int x;

	c = fgetc(fp);
	while (c != '\n') {
		c = fgetc(fp);
	}

	x = 0;
	c = fgetc(fp);
	while (c != EOF
	    && c != '\0'
	    && c != '\n'
	    && c != '\r') {
		ident[x++] = c;
		c = fgetc(fp);
	}
	ident[x] = '\0';
	_ungetc(c, fp);

	return 0;
}

/*forward*/ static int
readp_sub(FILE *fp, struct el **firstp, struct el **lastp);

/*
 * File format specification:
 * http://wiki.geda-project.org/geda:file_format_spec
 */
static int
read_list(FILE *fp, struct el **firstp, struct el **lastp)
{
	int ret;
	char c;
	int num;
	int i;
	char ident[1024];
	struct el *e;

	*firstp = NULL;
	*lastp = NULL;

	for (;;) {
		ret = read_char(fp, &c);
		if (ret == EOF) {
			_ungetc(EOF, fp);
			break;
		}
		if (c == ']'
		 || c == '}') {
			_ungetc(c, fp);
			break;
		}

		switch (c) {
		case '{':
		case '[':
			assert(0);
			break;
		case 'A':
			/* Arc */
			e = NULL;

			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			break;
		case 'B':
			/* Box */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = BOX;
			read_int(fp, &e->u.box.x);
			read_int(fp, &e->u.box.y);
			read_int(fp, &e->u.box.width);
			read_int(fp, &e->u.box.height);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			break;
		case 'C':
			/* Component */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = COMP;
			read_int(fp, &e->u.comp.x); /* X Coord */
			read_int(fp, &e->u.comp.y); /* Y Coord */
			read_int(fp, &num);
			read_int(fp, &e->u.comp.rotate); /* Rotate */
			read_int(fp, &e->u.comp.mirror); /* Mirror */
			read_ident(fp, ident); /* Sym File */
			e->u.comp.sym_file = strdup(ident);
			assert(e->u.comp.sym_file);
			assert(e->u.comp.sym_file[0]);
			readp_sub(fp, &e->child_first, &e->child_last);
			break;
		case 'L':
			e = NULL;

			read_int(fp, &num); /* Start X */
			read_int(fp, &num); /* Start Y */
			read_int(fp, &num); /* End X */
			read_int(fp, &num); /* End Y */
			read_int(fp, &num); /* Color */
			read_int(fp, &num); /* Width */
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			break;
		case 'N':
			/* Single Signal */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = NET;
			read_int(fp, &e->u.net.x0); /* Start X */
			read_int(fp, &e->u.net.y0); /* Start Y */
			read_int(fp, &e->u.net.x1); /* End Y */
			read_int(fp, &e->u.net.y1); /* End Y */
			read_int(fp, &num); /* Color */
			readp_sub(fp, &e->child_first, &e->child_last);
			break;
		case 'P':
			/* Pin */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = PIN;
			read_int(fp, &e->u.pin.x0); /* Start X */
			read_int(fp, &e->u.pin.y0); /* Start Y */
			read_int(fp, &e->u.pin.x1); /* End X */
			read_int(fp, &e->u.pin.y1); /* End Y */
			read_int(fp, &e->u.pin.color); /* Color */
			read_int(fp, &num); /* ? */
			read_int(fp, &num); /* ? */
			readp_sub(fp, &e->child_first, &e->child_last);
			break;
		case 'T': /* Text */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = TEXT;
			read_int(fp, &e->u.text.x);
			read_int(fp, &e->u.text.y);
			read_int(fp, &e->u.text.color);
			read_int(fp, &e->u.text.size);
			read_int(fp, &e->u.text.visible);
			read_int(fp, &e->u.text.hide);
			read_int(fp, &e->u.text.angle);
			read_int(fp, &e->u.text.align);
			read_int(fp, &num); /* Number of Lines */
			for (i = 0; i < num; i++) {
				read_line(fp, ident);
				e->u.text.str[i] = strdup(ident);
				assert(e->u.text.str[i]);
			}
			break;
		case 'U':
			/* Bus */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = BUS;
			read_int(fp, &e->u.bus.x0); /* Start X */
			read_int(fp, &e->u.bus.y0); /* Start Y */
			read_int(fp, &e->u.bus.x1); /* End X */
			read_int(fp, &e->u.bus.y1); /* End Y */
			read_int(fp, &e->u.bus.color); /* Color */
			read_int(fp, &num);
			readp_sub(fp, &e->child_first, &e->child_last);
			break;
		case 'V':
			/* Circle */
			e = NULL;

			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			break;
		case 'v': /* Version of gschem */
			e = NULL;

			read_int(fp, &num);
			read_int(fp, &num);
			break;
		default:
			fprintf(stderr, "c=%c\n", c);
			assert(0);
		}

		if (e) {
			e->prev = *lastp;
			e->next = NULL;
			if (e->prev) {
				e->prev->next = e;
			} else {
				*firstp = e;
			}
			*lastp = e;
		}
	}

	return 0;
}

static int
readp_sub(FILE *fp, struct el **firstp, struct el **lastp)
{
	char c;
	int ret;

	ret = read_char(fp, &c);
	if (ret == EOF) {
		_ungetc(EOF, fp);
		*firstp = NULL;
		*lastp = NULL;
		return EOF;
	}
	if (c != '{') {
		_ungetc(c, fp);
		*firstp = NULL;
		*lastp = NULL;
		return EOF;
	}

	read_list(fp, firstp, lastp);

	ret = read_char(fp, &c);
	assert(ret != EOF
	    && c == '}');

	return 0;
}

static const char *
lookup_str(struct el *e, const char *n)
{
	if (e->type == TEXT
	 && e->u.text.str[0]
	 && strncmp(e->u.text.str[0], n, strlen(n)) == 0
	 && e->u.text.str[0][strlen(n)] == '=') {
		return &e->u.text.str[0][strlen(n) + 1];
	} else {
		return NULL;
	}
}

static const char *
lookup_n(struct el *e, const char *str, unsigned int n)
{
	struct el *ce;
	const char *value;

	for (ce = e->child_first; ce; ce = ce->next) {
		value = lookup_str(ce, str);
		if (value) {
			if (n == 0) {
				return value;
			} else {
				n--;
			}
		}
	}
#if 0
	if (e->type == COMP) {
		for (ce = e->u.comp.con_first; ce; ce = ce->next) {
			value = lookup_str(ce, str);
			if (value) {
				if (n == 0) {
					return value;
				} else {
					n--;
				}
			}
		}
	}
#endif
	return NULL;
}

static const char *
lookup(struct el *e, const char *str)
{
	return lookup_n(e, str, 0);
}

#define MIN(x, y)	(((x) < (y)) ? (x) : (y))
#define MAX(x, y)	(((x) < (y)) ? (y) : (x))

static int
connected_point_line(
	int x,
	int y,
	int x0,
	int y0,
	int x1,
	int y1
)
{
	if (x == x0 && y == y0) {
		return 1;
	} else if (x == x1 && y == y1) {
		return 1;
	} else if (x0 == x1) {
		if (x == x0
		 && MIN(y0, y1) <= y && y <= MAX(y0, y1)) {
			return 1;
		} else {
			return 0;
		}
	} else if (y0 == y1) {
		if (y == y0
		 && MIN(x0, x1) <= x && x <= MAX(x0, x1)) {
			return 1;
		} else {
			return 0;
		}
	} else {
#if DEBUG
		static int count = 0;

		if (count < 10) {
			fprintf(stderr, "WARNING: x0=%d, y0=%d, x1=%d, y1=%d\n",
					x0, y0, x1, y1);
			count++;
		} else if (count == 10) {
			fprintf(stderr, "WARNING: more warnings following...\n");
			count++;
		}
#endif

		return 0;
	}
}

static int
connected_line_line(
	int xa0,
	int ya0,
	int xa1,
	int ya1,
	int xb0,
	int yb0,
	int xb1,
	int yb1
)
{
	return connected_point_line(xa0, ya0, xb0, yb0, xb1, yb1)
	    || connected_point_line(xa1, ya1, xb0, yb0, xb1, yb1)
	    || connected_point_line(xb0, yb0, xa0, ya0, xa1, ya1)
	    || connected_point_line(xb1, yb1, xa0, ya0, xa1, ya1);
}

static void
connect_to(
	const char *comp_type,
	const char *comp_name,
	unsigned int level,
	struct sig *sig,
	int x0,
	int y0,
	int x1,
	int y1,
	struct el *first,
	struct el *last
)
{
	struct el *e;

	for (e = first; e; e = e->next) {
		switch (e->type) {
		case BOX:
			/* Nothing to connect... */
			break;
		case COMP: {
			int x0_new;
			int y0_new;
			int x1_new;
			int y1_new;
			int tmp;

			x0_new = x0 - e->u.comp.x;
			y0_new = y0 - e->u.comp.y;
			x1_new = x1 - e->u.comp.x;
			y1_new = y1 - e->u.comp.y;

			/*
			 * Note:
			 * If component was rotated by 90 counterclockwise
			 * we must back-rotate by 90 clockwise!
			 */
			switch (e->u.comp.rotate) {
			case 0:
				/* Nothing to do... */
				break;
			case 90:
				/* x'=y, y'=-x */
				tmp = x0_new;
				x0_new = y0_new;
				y0_new = -tmp;

				tmp = x1_new;
				x1_new = y1_new;
				y1_new = -tmp;
				break;
			case 180:
				/* x'=-x, y'=-y */
				x0_new = -x0_new;
				y0_new = -y0_new;

				x1_new = -x1_new;
				y1_new = -y1_new;
				break;
			case 270:
				/* x'=-y, y'=x */
				tmp = x0_new;
				x0_new = -y0_new;
				y0_new = tmp;

				tmp = x1_new;
				x1_new = -y1_new;
				y1_new = tmp;
				break;
			default:
				assert(0);
			}

			assert(e->u.comp.mirror == 0);
			connect_to(lookup(e, "device"), lookup(e, "refdes"),
					level + 1, sig,
					x0_new, y0_new, x1_new, y1_new,
					e->u.comp.con_first, e->u.comp.con_last);
			break;
		    }
		case NET:
			if ((sig->name
			  && lookup(e, "netname")
			  && strcmp(sig->name, lookup(e, "netname")) == 0)
			 || connected_line_line(
					e->u.net.x0, e->u.net.y0,
					e->u.net.x1, e->u.net.y1,
					x0, y0, x1, y1)) {
				if (e->u.net.sig == sig) {
					/* Nothing to do... */
				} else if (e->u.net.sig != NULL) {
					assert(0);
				} else {
					const char *netname;

					e->u.net.sig = sig;

					netname = lookup(e, "netname");
					if (netname) {
						if (sig->name) {
							if (strcmp(sig->name, netname) != 0) {
								fprintf(stderr, "ERROR: signal \"%s\" connected to signal \"%s\".\n", netname, sig->name);
							}
						} else {
							sig->name = netname;
						}
					}

					connect_to(comp_type, comp_name,
							level, sig,
							e->u.net.x0, e->u.net.y0,
							e->u.net.x1, e->u.net.y1,
							first, last);
				}
			}
			break;
		case PIN:
			if (connected_line_line(
					e->u.pin.x0, e->u.pin.y0,
					e->u.pin.x1, e->u.pin.y1,
					x0, y0, x1, y1)) {
				if (e->u.pin.sig == sig) {
					/* Nothing to do... */
				} else if (e->u.pin.sig != NULL) {
					fprintf(stderr, "ERROR: Port \"%s\" of \"%s\" already connected.\n",
							lookup(e, "pinlabel"),
							comp_name);
				} else {
					const char *label;
					const char *type;

					e->u.pin.sig = sig;

					if (level == 0) {
						sig->is_port = 1;
					}

					label = lookup(e, "pinlabel");
					if (! label) {
						fprintf(stderr, "ERROR: Pin of \"%s\" has no pinlabel attribute.\n", comp_type);
						label = "unknown";
					}
					assert(label);
					if (level == 0) {
						if (sig->name) {
							if (strcmp(sig->name, label) != 0) {
								fprintf(stderr, "ERROR: Port \"%s\" of \"%s\" connected to named signal \"%s\".\n", label, comp_name, sig->name);
							}
						} else {
							sig->name = label;
						}
					}

					type = lookup(e, "type");
					if (! type) {
						fprintf(stderr, "ERROR: Pin \"%s\" in \"%s\" has no type attribute.\n", label, comp_type);
						type = "boolean";
					}
					assert(type);
					if (sig->type) {
						if (strcmp(sig->type, type) != 0) {
							fprintf(stderr, "ERROR: Port \"%s\" of \"%s\" has type \"%s\" and is connected to signal of type \"%s\".\n", label, comp_type, type, sig->type);
						}
					} else {
						sig->type = type;
					}

#if DEBUG
					fprintf(stderr, " %s/%s", comp_name, label);
#endif

					connect_to(comp_type, comp_name,
							level, sig,
							e->u.pin.x0, e->u.pin.y0,
							e->u.pin.x1, e->u.pin.y1,
							first, last);
				}
			}
			break;
		case TEXT:
			/* Nothing to connect... */
			break;
		case BUS:
			if ((sig->name
			  && lookup(e, "netname")
			  && strcmp(sig->name, lookup(e, "netname")) == 0)
			 || connected_line_line(
					e->u.bus.x0, e->u.bus.y0,
					e->u.bus.x1, e->u.bus.y1,
					x0, y0, x1, y1)) {
				if (e->u.bus.sig == sig) {
					/* Nothing to do... */
				} else if (e->u.bus.sig != NULL) {
					assert(0);
				} else {
					const char *netname;

					e->u.bus.sig = sig;

					netname = lookup(e, "netname");
					if (netname) {
						if (sig->name) {
							assert(strcmp(sig->name, netname) == 0);
						} else {
							sig->name = netname;
						}
					}

					connect_to(comp_type, comp_name,
							level, sig,
							e->u.bus.x0, e->u.bus.y0,
							e->u.bus.x1, e->u.bus.y1,
							first, last);
				}
			}
			break;
		}
	}
}

static void
connect(
	const char *comp_type,
	const char *comp_name,
	unsigned int level,
	struct el *first,
	struct el *last,
	struct sig **sig_firstp,
	struct sig **sig_lastp
)
{
	struct el *e;
	struct sig *sig;

	/*
	 * First round:
	 * connect all *named* nets/busses.
	 */
	for (e = first; e; e = e->next) {
		sig = NULL;
		switch (e->type) {
		case BOX:
			/* Nothing to connect... */
			break;
		case COMP:
			break;
		case NET:
			if (! lookup(e, "netname")) {
				break;
			}
			if (! e->u.net.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				sig->name = lookup(e, "netname");

				connect_to(comp_type, comp_name, level, sig,
						e->u.net.x0, e->u.net.y0,
						e->u.net.x1, e->u.net.y1,
						first, last);
			}
			break;
		case PIN:
			break;
		case TEXT:
			/* Nothing to connect... */
			break;
		case BUS:
			if (! lookup(e, "netname")) {
				break;
			}
			if (! e->u.bus.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				sig->name = lookup(e, "netname");

				connect_to(comp_type, comp_name, level, sig,
						e->u.bus.x0, e->u.bus.y0,
						e->u.bus.x1, e->u.bus.y1,
						first, last);
			}
			break;
		}
		if (sig) {
			assert(sig->name);
			if (! sig->type) {
				fprintf(stderr, "ERROR: signal %s not connected to any pin.\n",
						sig->name);
				sig->type = "boolean";
			}

#if DEBUG
			fprintf(stderr, " -> %s (%s)\n", sig->name, sig->type);
#endif

			sig->prev = *sig_lastp;
			sig->next = NULL;
			if (sig->prev) {
				sig->prev->next = sig;
			} else {
				*sig_firstp = sig;
			}
			*sig_lastp = sig;
		}
	}

	/*
	 * Second round:
	 * Connect all *unnamed* nets/busses.
	 */
	for (e = first; e; e = e->next) {
		sig = NULL;
		switch (e->type) {
		case BOX:
			break;
		case COMP:
			break;
		case NET:
			if (! e->u.net.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				connect_to(comp_type, comp_name, level, sig,
						e->u.net.x0, e->u.net.y0,
						e->u.net.x1, e->u.net.y1,
						first, last);
			}
			break;
		case PIN:
			if (! e->u.pin.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				connect_to(comp_type, comp_name, level, sig,
						e->u.pin.x0, e->u.pin.y0,
						e->u.pin.x1, e->u.pin.y1,
						first, last);
			}
			break;
		case TEXT:
			/* Nothing to connect... */
			break;
		case BUS:
			if (! e->u.bus.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				connect_to(comp_type, comp_name, level, sig,
						e->u.bus.x0, e->u.bus.y0,
						e->u.bus.x1, e->u.bus.y1,
						first, last);
			}
			break;
		}
		if (sig) {
			if (! sig->name) {
				sig->name = ident_tmp();
			}
			if (! sig->type) {
				fprintf(stderr, "ERROR: signal %s not connected to any pin.\n",
						sig->name);
				sig->type = "boolean";
			}

#if DEBUG
			fprintf(stderr, " -> %s (%s)\n", sig->name, sig->type);
#endif

			sig->prev = *sig_lastp;
			sig->next = NULL;
			if (sig->prev) {
				sig->prev->next = sig;
			} else {
				*sig_firstp = sig;
			}
			*sig_lastp = sig;
		}
	}

	/*
	 * Third round:
	 * Connect nets/busses in subcomponents.
	 */
	for (e = first; e; e = e->next) {
		if (e->type != COMP) continue;

		connect(lookup(e, "device"), lookup(e, "refdes"), level + 1,
				e->u.comp.con_first, e->u.comp.con_last,
				sig_firstp, sig_lastp);
	}
}

static struct {
	int pin_x0, pin_y0, pin_x1, pin_y1;
	int seq;
	int name_x, name_y;
	int name_align, name_angle;
	const char *name;
	const char *inout;
	const char *type;
	const char *sig;
} entry[1024];
static unsigned int nentries;

static void
table_init(void)
{
	nentries = 0;
}

static void
table_add(
	int pin_x0,
	int pin_y0,
	int pin_x1,
	int pin_y1,
	const char *pinseq,
	int name_x,
	int name_y,
	int name_align,
	int name_angle,
	const char *name,
	const char *inout,
	const char *type,
	const char *sig
)
{
	entry[nentries].pin_x0 = pin_x0;
	entry[nentries].pin_y0 = pin_y0;
	entry[nentries].pin_x1 = pin_x1;
	entry[nentries].pin_y1 = pin_y1;
	entry[nentries].seq = pinseq ? atoi(pinseq) : -1;
	entry[nentries].name_x = name_x;
	entry[nentries].name_y = name_y;
	entry[nentries].name_align = name_align;
	entry[nentries].name_angle = name_angle;
	entry[nentries].name = name;
	entry[nentries].inout = inout;
	entry[nentries].type = type;
	entry[nentries].sig = sig;
	nentries++;
}

static void
table_sort(void)
{
	unsigned int i;
	int done;

	if (nentries <= 1) {
		/* Nothing to sort... */
		return;
	}

	do {
		done = 1;
		for (i = 0; i < nentries - 1; i++) {
			if (entry[i + 1].seq < entry[i].seq
			 || (entry[i + 1].seq == entry[i].seq
			  && strcmp(entry[i + 1].name, entry[i].name) < 0)) {
				int tint;
				const char *tstr;
#define swap_int(x) \
	{ tint = entry[i].x; entry[i].x = entry[i + 1].x; entry[i + 1].x = tint; }
#define swap_str(x) \
	{ tstr = entry[i].x; entry[i].x = entry[i + 1].x; entry[i + 1].x = tstr; }
				swap_int(pin_x0);
				swap_int(pin_y0);
				swap_int(pin_x1);
				swap_int(pin_y1);
				swap_int(seq);
				swap_int(name_x);
				swap_int(name_y);
				swap_int(name_align);
				swap_int(name_angle);
				swap_str(name);
				swap_str(inout);
				swap_str(type);
				swap_str(sig);
				done = 0;
			}
		}
	} while (! done);
}

static void
translate(int dx, int dy, int rotate, int *xp, int *yp)
{
	int x;
	int y;
	int tmp;

	x = *xp;
	y = *yp;

	/* Rotate */
	switch (rotate) {
	case 0:
		/* Nothing to do... */
		break;
	case 90:
		/*
		 * x' = -y;
		 * y' = x;
		 */
		tmp = x;
		x = -y;
		y = tmp;
		break;
	case 180:
		/*
		 * x' = -x;
		 * y' = -y;
		 */
		x = -x;
		y = -y;
		break;
	case 270:
		/*
		 * x' = y;
		 * y' = -x;
		 */
		tmp = x;
		x = y;
		y = -tmp;
		break;
	default:
		assert(0); /* Mustn't happen. */
	}

	/* Push */
	x += dx;
	y += dy;

	*xp = x;
	*yp = y;
}

static double
scale_x(int x)
{
	return x / 10.0;
}

static double
scale_y(int y)
{
	return -y / 10.0;
}

static void
gen_xml(
	FILE *fp,
	const char *type,
	struct el *first,
	struct el *last,
	struct sig *sig_first,
	struct sig *sig_last
)
{
	struct el *e;
	struct el *e1;
	struct sig *sig;

	fprintf(fp, "<plan>\n");

	/*
	 * Generics
	 */
	fprintf(fp, "  <generics>\n");

	for (e = first; e; e = e->next) {
		char *generic;
		const char *type_;
		const char *value;
		char *equal;

		if (e->type != TEXT
		 || ! e->u.text.str[0]
		 || strncmp(e->u.text.str[0], "generic=", 8) != 0) {
			continue;
		}

		generic = e->u.text.str[0] + strlen("generic=");
		assert(strchr(generic, '='));

		equal = strchr(generic, '=');
		*equal = '\0';
		value = equal + 1;
		type_ = *value == '"' ? "string" : "integer";

		fprintf(fp, "    <generic id=\"%s\">\n", generic);
		fprintf(fp, "      <type id=\"%s\"/>\n", type_);
		fprintf(fp, "      <value>%s</value>\n", value);
		fprintf(fp, "      <text string=\"%s\" x=\"%f\" y=\"%f\" align=\"%d\" angle=\"%d\"/>\n",
				e->u.text.str[0],
				scale_x(e->u.text.x), scale_y(e->u.text.y),
				0, 0);
		fprintf(fp, "    </generic>\n");

		*equal = '=';
	}

	fprintf(fp, "  </generics>\n");

	/*
	 * Ports
	 */
	table_init();
	for (e = first; e; e = e->next) {
		int pin_x0 = -1;
		int pin_y0 = -1;
		int pin_x1 = -1;
		int pin_y1 = -1;
		int pinlabel_x = -1;
		int pinlabel_y = -1;
		int pinlabel_align = -1;
		int pinlabel_angle = -1;
		const char *pinlabel = NULL;
		const char *pinseq = NULL;
		const char *pintype = NULL;
		const char *type_ = NULL;

		if (e->type != PIN) continue;

		pin_x0 = e->u.pin.x0;
		pin_y0 = e->u.pin.y0;
		pin_x1 = e->u.pin.x1;
		pin_y1 = e->u.pin.y1;
		for (e1 = e->child_first; e1; e1 = e1->next) {
			if (e1->type != TEXT) continue;

			if (strncmp(e1->u.text.str[0], "pinseq=", 7) == 0) {
				pinseq = e1->u.text.str[0] + 7;
			} else if (strncmp(e1->u.text.str[0], "pinlabel=", 9) == 0) {
				pinlabel_x = e1->u.text.x;
				pinlabel_y = e1->u.text.y;
				pinlabel_align = e1->u.text.align;
				pinlabel_angle = e1->u.text.angle;
				pinlabel = e1->u.text.str[0] + 9;
			} else if (strncmp(e1->u.text.str[0], "pintype=", 8) == 0) {
				pintype = e1->u.text.str[0] + 8;
			} else if (strncmp(e1->u.text.str[0], "type=", 5) == 0) {
				type_ = e1->u.text.str[0] + 5;
			}
		}
		assert(pin_x0 != -1);
		assert(pin_y0 != -1);
		assert(pin_x1 != -1);
		assert(pin_y1 != -1);
		assert(pinlabel_x != -1);
		assert(pinlabel_y != -1);
		assert(pinlabel_align != -1);
		assert(pinlabel_angle != -1);
		assert(pinlabel);
		// assert(pinseq);
		assert(pintype);
		assert(type_);

		table_add(pin_x0, pin_y0, pin_x1, pin_y1,
				pinseq,
				pinlabel_x, pinlabel_y, pinlabel_align, pinlabel_angle,
				pinlabel,
				pintype, type_, NULL);
	}
	if (nentries) {
		unsigned int i;

		fprintf(fp, "  <ports>\n");

		table_sort();

		for (i = 0; i < nentries; i++) {
			fprintf(fp, "    <port");
			fprintf(fp, " id=\"%s\"", entry[i].name);
			if (entry[i].seq != -1) {
				fprintf(fp, " seq=\"%d\"", entry[i].seq);
			}
			fprintf(fp, ">\n");

			fprintf(fp, "      <inout id=\"%s\"/>\n", entry[i].inout);
			fprintf(fp, "      <signal id=\"%s\"/>\n", entry[i].name);

			fprintf(fp, "      <text string=\"%s\" x=\"%f\" y=\"%f\" align=\"%d\" angle=\"%d\"/>\n",
					entry[i].name,
					scale_x(entry[i].name_x),
					scale_y(entry[i].name_y),
					entry[i].name_align,
					entry[i].name_angle);
			fprintf(fp, "      <line x0=\"%f\" y0=\"%f\" x1=\"%f\" y1=\"%f\"/>\n",
					scale_x(entry[i].pin_x0),
					scale_y(entry[i].pin_y0),
					scale_x(entry[i].pin_x1),
					scale_y(entry[i].pin_y1));
			fprintf(fp, "    </port>\n");
		}
	}

	fprintf(fp, "  </ports>\n");

	/*
	 * Signals
	 */
	fprintf(fp, "  <signals>\n");

	for (sig = sig_first; sig; sig = sig->next) {
		int netname_x = -1;
		int netname_y = -1;
		int netname_align = -1;
		int netname_angle = -1;
		char *netname = NULL;

		assert(sig->name);
		assert(sig->type);

		fprintf(fp, "    <signal id=\"%s\">\n", sig->name);
		fprintf(fp, "      <type id=\"%s\"/>\n", sig->type);

		for (e = first; e; e = e->next) {
			if ((e->type != NET
			  || e->u.net.sig != sig)
			 && (e->type != BUS
			  || e->u.bus.sig != sig)) continue;

			for (e1 = e->child_first; e1; e1 = e1->next) {
				if (e1->type == TEXT
				 && strncmp(e1->u.text.str[0], "netname=", 8) == 0) {
					netname_x = e1->u.text.x;
					netname_y = e1->u.text.y;
					netname_align = e1->u.text.align;
					netname_angle = e1->u.text.angle;
					netname = e1->u.text.str[0] + 8;

					fprintf(fp, "      <text string=\"%s\" x=\"%f\" y=\"%f\" align=\"%d\" angle=\"%d\"/>\n",
							netname,
							scale_x(netname_x),
							scale_y(netname_y),
							netname_align, netname_angle);
				}
			}
		}

		for (e = first; e; e = e->next) {
			if (e->type == NET
			 && e->u.net.sig == sig) {
				fprintf(fp, "      <line x0=\"%f\" y0=\"%f\" x1=\"%f\" y1=\"%f\"/>\n",
						scale_x(e->u.net.x0),
						scale_y(e->u.net.y0),
						scale_x(e->u.net.x1),
						scale_y(e->u.net.y1));
			} else if (e->type == BUS
				&& e->u.bus.sig == sig) {
				fprintf(fp, "      <line x0=\"%f\" y0=\"%f\" x1=\"%f\" y1=\"%f\"/>\n",
						scale_x(e->u.bus.x0),
						scale_y(e->u.bus.y0),
						scale_x(e->u.bus.x1),
						scale_y(e->u.bus.y1));
			}
		}
		fprintf(fp, "    </signal>\n");
	}

	fprintf(fp, "  </signals>\n");

	/*
	 * Components
	 */
	fprintf(fp, "  <comps>\n");

	for (e = first; e; e = e->next) {
		int box_x0 = -1;
		int box_y0 = -1;
		int box_x1 = -1;
		int box_y1 = -1;
		int refdes_x = -1;
		int refdes_y = -1;
		int refdes_align = -1;
		int refdes_angle = -1;
		char *refdes = NULL;
		int device_x = -1;
		int device_y = -1;
		int device_align = -1;
		int device_angle = -1;
		char *device = NULL;
		struct el *ce;
		unsigned int i;

		if (e->type != COMP
		 || lookup(e, "graphical")
		 || ! lookup(e, "refdes"))
			continue;

		for (ce = e->u.comp.con_first; ce; ce = ce->next) {
			if (ce->type != BOX) continue;

			box_x0 = ce->u.box.x;
			box_y0 = ce->u.box.y;
			box_x1 = box_x0 + ce->u.box.width;
			box_y1 = box_y0 + ce->u.box.height;

			translate(e->u.comp.x, e->u.comp.y, e->u.comp.rotate,
					&box_x0, &box_y0);
			translate(e->u.comp.x, e->u.comp.y, e->u.comp.rotate,
					&box_x1, &box_y1);
		}
		for (e1 = e->child_first; e1; e1 = e1->next) {
			if (e1->type == TEXT
			 && strncmp(e1->u.text.str[0], "refdes=", 7) == 0) {
				refdes_x = e1->u.text.x;
				refdes_y = e1->u.text.y;
				refdes_align = e1->u.text.align;
				refdes_angle = e1->u.text.angle;
				refdes = e1->u.text.str[0] + 7;
			} else if (e1->type == TEXT
				&& strncmp(e1->u.text.str[0], "device=", 7) == 0) {
				device_x = e1->u.text.x;
				device_y = e1->u.text.y;
				device_align = e1->u.text.align;
				device_angle = e1->u.text.angle;
				device = e1->u.text.str[0] + 7;
			}
		}

		fprintf(fp, "    <comp id=\"%s\">\n", refdes);
		fprintf(fp, "      <type id=\"%s\"/>\n", device);

		fprintf(fp, "      <text string=\"%s\" x=\"%f\" y=\"%f\" align=\"%d\" angle=\"%d\"/>\n",
				refdes,
				scale_x(refdes_x), scale_y(refdes_y),
				refdes_align, refdes_angle);
		fprintf(fp, "      <text string=\"%s\" x=\"%f\" y=\"%f\" align=\"%d\" angle=\"%d\"/>\n",
				device,
				scale_x(device_x), scale_y(device_y),
				device_align, device_angle);
		fprintf(fp, "      <box x0=\"%f\" y0=\"%f\" x1=\"%f\" y1=\"%f\"/>\n",
				MIN(scale_x(box_x0), scale_x(box_x1)),
				MIN(scale_y(box_y0), scale_y(box_y1)),
				MAX(scale_x(box_x0), scale_x(box_x1)),
				MAX(scale_y(box_y0), scale_y(box_y1)));

		/* Generics */
		for (ce = e->child_first; ce; ce = ce->next) {
			int generic_x = -1;
			int generic_y = -1;
			int generic_align = -1;
			int generic_angle = -1;
			char *generic = NULL;
			char *equal = NULL;
			char *value = NULL;

			if (ce->type != TEXT
			 || strncmp(ce->u.text.str[0], "generic=", 8) != 0) {
				continue;
			}

			generic_x = ce->u.text.x;
			generic_y = ce->u.text.y;
			generic_align = ce->u.text.align;
			generic_angle = ce->u.text.angle;
			generic = ce->u.text.str[0] + strlen("generic=");
			equal = strchr(generic, '=');
			assert(equal);
			*equal = '\0';
			value = equal + 1;

			fprintf(fp, "      <generic id=\"%s\">\n", generic);
			fprintf(fp, "        <value>%s</value>\n", value);
			fprintf(fp, "        <text string=\"%s\" x=\"%f\" y=\"%f\" align=\"%d\" angle=\"%d\"/>\n",
					ce->u.text.str[0],
					scale_x(generic_x), scale_y(generic_y),
					generic_align, generic_angle);
			fprintf(fp, "      </generic>\n");

			*equal = '=';
		}

		/* Ports */
		table_init();
		for (ce = e->u.comp.con_first; ce; ce = ce->next) {
			int pin_x0;
			int pin_y0;
			int pin_x1;
			int pin_y1;
			const char *pinseq;
			int pinlabel_x;
			int pinlabel_y;
			int pinlabel_align;
			int pinlabel_angle;
			const char *pinlabel;
			const char *pintype;
			const char *type_;

			if (ce->type != PIN) continue;

			pin_x0 = ce->u.pin.x0;
			pin_y0 = ce->u.pin.y0;
			pin_x1 = ce->u.pin.x1;
			pin_y1 = ce->u.pin.y1;

			translate(e->u.comp.x, e->u.comp.y, e->u.comp.rotate,
					&pin_x0, &pin_y0);
			translate(e->u.comp.x, e->u.comp.y, e->u.comp.rotate,
					&pin_x1, &pin_y1);

			pinseq = NULL;
			pinlabel_x = -1;
			pinlabel_y = -1;
			pinlabel_align = -1;
			pinlabel_angle = -1;
			pinlabel = NULL;
			pintype = NULL;
			type_ = NULL;
			for (e1 = ce->child_first; e1; e1 = e1->next) {
				if (e1->type != TEXT) continue;

				if (strncmp(e1->u.text.str[0], "pinseq=", 7) == 0) {
					pinseq = e1->u.text.str[0] + 7;
				} else if (strncmp(e1->u.text.str[0], "pinlabel=", 9) == 0) {
					pinlabel_x = e1->u.text.x;
					pinlabel_y = e1->u.text.y;
					pinlabel_align = e1->u.text.align;
					pinlabel_angle = e1->u.text.angle;
					pinlabel = e1->u.text.str[0] + 9;
				} else if (strncmp(e1->u.text.str[0], "pintype=", 8) == 0) {
					pintype = e1->u.text.str[0] + 8;
				} else if (strncmp(e1->u.text.str[0], "type=", 5) == 0) {
					type_ = e1->u.text.str[0] + 5;
				}
			}

			translate(e->u.comp.x, e->u.comp.y, e->u.comp.rotate,
					&pinlabel_x, &pinlabel_y);

			table_add(pin_x0, pin_y0, pin_x1, pin_y1,
					pinseq,
					pinlabel_x, pinlabel_y, pinlabel_align, pinlabel_angle,
					pinlabel,
					pintype, type_, ce->u.pin.sig->name);
		}
		table_sort();
		for (i = 0; i < nentries; i++) {
			fprintf(fp, "      <port id=\"%s\"", entry[i].name);
			if (entry[i].seq != -1) {
				fprintf(fp, " seq=\"%d\"", entry[i].seq);
			}
			fprintf(fp, ">\n");
			fprintf(fp, "        <inout id=\"%s\"/>\n", entry[i].inout);
			fprintf(fp, "        <signal id=\"%s\"/>\n",
					entry[i].sig);
			fprintf(fp, "        <text string=\"%s\" x=\"%f\" y=\"%f\" align=\"%d\" angle=\"%d\"/>\n",
					entry[i].name,
					scale_x(entry[i].name_x), scale_y(entry[i].name_y),
					entry[i].name_align, entry[i].name_angle);
			fprintf(fp, "        <line x0=\"%f\" y0=\"%f\" x1=\"%f\" y1=\"%f\"/>\n",
					scale_x(entry[i].pin_x0), scale_y(entry[i].pin_y0),
					scale_x(entry[i].pin_x1), scale_y(entry[i].pin_y1));
			fprintf(fp, "      </port>\n");
		}

		fprintf(fp, "    </comp>\n");
	}

	fprintf(fp, "  </comps>\n");

	fprintf(fp, "</plan>\n");
}

static void __attribute__((noreturn))
usage(int retval)
{
	fprintf(stderr, "Usage: %s <*.sch-file>\n", progname);
	exit(retval);
}

int
main(int argc, char **argv)
{
	int c;
	FILE *fp;
	struct el *first;
	struct el *last;
	struct el *e;
	struct sig *sig_first;
	struct sig *sig_last;
	char type[1024];
	char outname[1024];
	int ret;
	int i;

	progname = *argv;

	while ((c = getopt(argc, argv, "I:M")) != -1) {
		switch (c) {
		case 'I':
			idir[nidirs++] = optarg;
			break;
		case 'M':
			opt_M = 1;
			break;
		default:
			usage(1);
		}
	}
	argc -= optind;
	argv += optind;

	if (0 < argc) {
		inname = *argv;
		argc--;
		argv++;
	} else {
		usage(1);
	}
	if (argc != 0) {
		usage(1);
	}
	if (! strchr(inname, '.')) {
		usage(1);
	}
	
	if (strchr(inname, '/')) {
		strcpy(type, strrchr(inname, '/') + 1);
	} else {
		strcpy(type, inname);
	}
	if (strchr(type, '.')) {
		*strchr(type, '.') = '\0';
	}

	/*
	 * Read schematic file.
	 */
	fp = fopen(inname, "r");
	if (! fp) {
		fprintf(stderr, "ERROR: %s: %s: %s.\n", progname,
				inname, strerror(errno));
		exit(1);
	}
	assert(fp);

	read_list(fp, &first, &last);

	fclose(fp);

	/*
	 * Read all component files.
	 */
	for (e = first; e; e = e->next) {
		if (e->type != COMP
		 || lookup(e, "graphical")) {
			continue;
		}

#if DEBUG
		fprintf(stderr, "Reading %s...\n", e->u.comp.sym_file);
#endif
		for (i = 0; ; i++) {
			char path[1024];

			if (i == nidirs) {
				/* Not found. */
				fprintf(stderr, "ERROR: %s: %s: Not found.\n",
						progname,
						e->u.comp.sym_file);
				break;
			}
			strcpy(path, idir[i]);
			strcat(path, "/");
			strcat(path, e->u.comp.sym_file);
			fp = fopen(path, "r");
			if (fp) {
				/* Found */
				if (opt_M) {
					printf(" %s", path);
				}
				read_list(fp, &e->u.comp.con_first,
						&e->u.comp.con_last);

				ret = fclose(fp);
				break;
			}
		}
	}

	if (opt_M) {
		printf("\n");
		exit(0);
	}

	/*
	 * Build signal/connection lists.
	 */
	sig_first = NULL;
	sig_last = NULL;
	connect(inname, "toplevel", 0, first, last, &sig_first, &sig_last);

	/*
	 * Generate <type>.avhdl File
	 */
	strcpy(outname, inname);
	strcpy(strrchr(outname, '.'), ".xml");
	fp = fopen(outname, "w");
	if (! fp) {
		fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
				outname, strerror(errno));
		exit(1);
	}

	gen_xml(fp, type, first, last, sig_first, sig_last);

	ret = fclose(fp);
	assert(ret == 0);

	return 0;
}
