/*
 * Copyright (C) 2007-2009 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 MAX_WIDTH	32
#define MAX_HEIGHT	32
#define MAX_CHARS	(64*1024)

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static struct {
	char font[32];
	unsigned char ord;
	unsigned int width;
	unsigned int height;
	unsigned int xmin, xmax, ymin, ymax;
	unsigned char pattern[MAX_WIDTH][MAX_HEIGHT];
} glyph[MAX_CHARS];
static unsigned int nglyphs = 0;

static unsigned int line = 1;

static const char *inputfile = "font_info.dat";

struct edge {
	struct edge *prev;
	struct edge *next;

	struct node *from;
	unsigned int token;
	struct node *to;
};

struct node {
	struct edge *next_first;
	struct edge *next_last;

	int glyph;
};

static struct node start;

static void __attribute__((__noreturn__))
syntax(const char *myfile)
{
	fprintf(stderr, "%s: syntax error in line %d.\n", myfile, line);
	exit(1);
}

static int
font_lex(const char *curfile, FILE *fp, char *font, unsigned char *ordp)
{
	for (;;) {
		int c;

		c = fgetc(fp);
		if (c == EOF) {
			/* End-Of-File */
			return c;

		} else if (c == ' ' || c == '\t' || c == '\r') {
			/* Empty Space */
			continue;

		} else if (c == '#') {
			/* Comment */
			do {
				c = fgetc(fp);
			} while (c != '\n' && c != EOF);
			ungetc(c, fp);
			continue;

		} else if (c == '\n') {
			/* End-Of-Line */
			line++;
			return c;

		} else if (('A' <= c && c <= 'Z')
			|| ('a' <= c && c <= 'z')
			|| c == '_') {
			/* Font Name */
			while (('A' <= c && c <= 'Z')
			    || ('a' <= c && c <= 'z')
			    || ('0' <= c && c <= '9')
			    || c == '_'
			    || c == '-'
			    || c == '.') {
				*font++ = c;
				c = fgetc(fp);
			}
			*font++ = '\0';
			ungetc(c, fp);
			return 'A';

		} else if ('0' <= c && c <= '9') {
			/* Decimal Number */
			*ordp = 0;
			while ('0' <= c && c <= '9') {
				*ordp *= 10;
				*ordp += c - '0';
				c = fgetc(fp);
			}
			ungetc(c, fp);
			return '0';

		} else if (c == '\'') {
			c = fgetc(fp);
			*ordp = (unsigned char) c;
			c = fgetc(fp);
			assert(c == '\'');
			return '0';

		} else if (c == '.') {
			/* . */
			return c;

		} else if (c == '@') {
			/* . */
			return c;

		} else {
			/* Unknown Character */
			syntax(curfile);
			/*NOTREACHED*/
		}
	}
}

static void
font_read(const char *myfile)
{
	FILE *fp_in;

	fp_in = fopen(myfile, "r");
	assert(fp_in != (FILE *) 0);
	
	for (;;) {
		char font[32];
		unsigned char ord;
		int c;

		c = font_lex(myfile, fp_in, font, &ord);
		if (c == EOF) {
			/* End-Of-File */
			break;

		} else if (c == '\n') {
			/* Empty Line */
			continue;

		} else if (c == 'A') {
			/*
			 * Character Definition
			 */
			/* Font Name */
			strcpy(glyph[nglyphs].font, font);

			/* Ord Number */
			c = font_lex(myfile, fp_in, font, &ord);
			if (c != '0') {
				syntax(myfile);
				/*NOTREACHED*/
			}
			glyph[nglyphs].ord = ord;

			/* Pattern (and Width/Height) */
			glyph[nglyphs].width = 0;
			glyph[nglyphs].height = 0;

			/* Skip end-of-line after ord number. */
			c = font_lex(myfile, fp_in, font, &ord);
			if (c != '\n') {
				syntax(myfile);
				/*NOTREACHED*/
			}

			/*
			 * Read first line of pattern.
			 * Get width of pattern.
			 */
			for (;;) {
				c = font_lex(myfile, fp_in, font, &ord);
				if (c == '\n') {
					break;
				} else if (c == '.') {
					glyph[nglyphs].pattern[glyph[nglyphs].width][0] = 0;
				} else if (c == '@') {
					glyph[nglyphs].pattern[glyph[nglyphs].width][0] = 1;
				} else {
					syntax(myfile);
					/*NOTREACHED*/
				}
				glyph[nglyphs].width++;
			}
			glyph[nglyphs].height++;

			/*
			 * Read next lines of pattern.
			 * Get height of pattern.
			 */
			for (;;) {
				unsigned int x;

				c = font_lex(myfile, fp_in, font, &ord);
				if (c == '\n' || c == EOF) {
					break;
				}
				for (x = 0; x < glyph[nglyphs].width; x++) {
					if (x != 0) {
						c = font_lex(myfile, fp_in, font, &ord);
					}
					if (c == '.') {
						glyph[nglyphs].pattern[x][glyph[nglyphs].height] = 0;
					} else if (c == '@') {
						glyph[nglyphs].pattern[x][glyph[nglyphs].height] = 1;
					} else {
						syntax(myfile);
						/*NOTREACHED*/
					}
				}

				/* Skip end-of-line after pattern line. */
				c = font_lex(myfile, fp_in, font, &ord);
				if (c != '\n') {
					syntax(myfile);
					/*NOTREACHED*/
				}

				glyph[nglyphs].height++;
			}
			nglyphs++;

		} else {
			syntax(myfile);
			/*NOTREACHED*/
		}
	}

	(void) fclose(fp_in);
}

static void
pm_gen(void)
{
	unsigned int n;

	start.next_first = NULL;
	start.next_last = NULL;

	for (n = 0; n < nglyphs; n++) {
		struct edge *first;
		struct edge *last;
		struct edge *edge;
		struct node *node;
		unsigned int last_count;
		unsigned int y;
		unsigned int x;
		unsigned int count;

#if 1
		if (glyph[n].ord < ' ' || 127 <= glyph[n].ord) {
			continue;
		}
#endif

#if 0
		if (' ' <= glyph[n].ord && glyph[n].ord < 127) {
			printf("'%c' ", glyph[n].ord);
		}
#endif

		count = 0;

		edge = malloc(sizeof(*edge));
		edge->from = NULL;
		edge->token = count;
		edge->to = NULL;

		edge->prev = NULL;
		edge->next = NULL;
		first = edge;
		last = edge;

		printf("%u", count);
		last_count = count;

		for (y = 0; y < glyph[n].height; y++) {
			count = 0;
			for (x = 0; x < glyph[n].width; x++) {
				if (glyph[n].pattern[x][y] == 1
				 && (x == 0
				  || glyph[n].pattern[x - 1][y] == 0)) {
					count++;
				}
			}
			if (count != last_count) {
				node = malloc(sizeof(*node));
				node->next_first = NULL;
				node->next_last = NULL;
				node->glyph = -1;

				last->to = node;

				edge = malloc(sizeof(*edge));
				edge->from = node;
				edge->to = NULL;

				last = edge;

				printf(", %u", count);
				last_count = count;
			}
		}
		count = 0;
		if (count != last_count) {
			printf(", %u", count);
		}

		node = malloc(sizeof(*node));
		node->next_first = NULL;
		node->next_last = NULL;
		node->glyph = n;

		last->to = node;

		first->from = &start;
		first->prev = start.next_last;
		first->next = NULL;
		if (first->prev) {
			first->prev->next = first;
		} else {
			start.next_first = first;
		}
		start.next_last = first;

		printf(" | ");

		count = 0;
		printf("%u", count);
		last_count = count;

		for (x = 0; x < glyph[n].width; x++) {
			count = 0;
			for (y = 0; y < glyph[n].height; y++) {
				if (glyph[n].pattern[x][y] == 1
				 && (y == 0
				  || glyph[n].pattern[x][y - 1] == 0)) {
					count++;
				}
			}
			if (count != last_count) {
				printf(", %u", count);
				last_count = count;
			}
		}
		count = 0;
		if (count != last_count) {
			printf(", %u", count);
		}

#if 1
		if (' ' <= glyph[n].ord && glyph[n].ord < 127) {
			printf(" '%c'", glyph[n].ord);
		}
#endif
#if 0
		printf(" %s %d", glyph[n].font, glyph[n].ord);
#endif
		printf("\n");
	}
}

int
main(int argc, const char * argv[])
{
	if (argc>=2) inputfile=argv[1];

	font_read(inputfile);

	if (argc>=3) inputfile=argv[2];

	font_read(inputfile);

	pm_gen();
	
	return 0;
}
