/*
 * Copyright (C) 2008 - 2012 Vivien Malerba <malerba@gnome-db.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/*
 * This program generates tokens'ID transformation because the GdaSqlParser object uses 2 Lemon generated
 * parsers at once, but with only one tokenizer (because each Lemon generated parser generates it own IDs for
 * tokens).
 */

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

#define MAX_SYMBOLS 500
#define IMPOSED_HEADER "parser_tokens.h"
#define PARSER_HEADER "parser.h"
#define DELIM_HEADER "delimiter.h"
#define FALSE 0
#define TRUE (!FALSE)

typedef struct {
	char *key;
	int   parser_value;
	int   delim_value;
} HashEntry;

HashEntry entries[MAX_SYMBOLS];
int nb_entries; /* must remain < MAX_SYMBOLS */

typedef enum {
	TYPE_IMPOSED,
	TYPE_PARSER,
	TYPE_DELIM
} SourceType;

static void parse_contents (FILE *stream, SourceType type);
static HashEntry *find_entry_for_token (char *token);
int 
main ()
{
	int i;
	FILE *fd_imposed;
	FILE *fd_parser;
	FILE *fd_delim;
	HashEntry *illegal_entry;
	HashEntry *rawstring_entry;
	char *file_imposed;
	char *file_parser;
	char *file_delimiter;

	memset (entries, 0, sizeof (entries));
#ifdef SRCDIR
  #ifdef __WIN32__
	file_imposed = SRCDIR "\\" IMPOSED_HEADER;
  #else
	file_imposed = SRCDIR "/" IMPOSED_HEADER;
  #endif
#else
	file_imposed = IMPOSED_HEADER;
#endif
#ifdef BUILDDIR
  #ifdef __WIN32__
	file_parser = BUILDDIR "\\" PARSER_HEADER;
	file_delimiter = BUILDDIR "\\" DELIM_HEADER;
  #else
	file_parser = BUILDDIR "/" PARSER_HEADER;
	file_delimiter = BUILDDIR "/" DELIM_HEADER;
  #endif
#else
	file_parser = PARSER_HEADER;
	file_delimiter = DELIM_HEADER;
#endif
	fd_imposed = fopen (file_imposed, "r");
	if (!fd_imposed) {
		printf ("Can't open '%s':%s\n", file_imposed, strerror (errno));
		return 1;
	}
	fd_parser = fopen (file_parser, "r");
	if (!fd_parser) {
		printf ("Can't open '%s':%s\n", file_parser, strerror (errno));
		return 1;
	}
	fd_delim = fopen (file_delimiter, "r");
	if (!fd_delim) {
		printf ("Can't open '%s':%s\n", file_delimiter, strerror (errno));
		return 1;
	}

	nb_entries = 0;
	parse_contents (fd_imposed, TYPE_IMPOSED);
	parse_contents (fd_parser, TYPE_PARSER);
	parse_contents (fd_delim, TYPE_DELIM);

	fclose (fd_imposed);
	fclose (fd_parser);
	fclose (fd_delim);

	/* output notice */
	printf ("/*\n * This file is generated by the gen_def program (see the gen_def.c file \n"
		" * for some explanations)\n"
		" * DO NOT EDIT MANUALLY\n */\n\n\n");

	/* output */
	for (i = 0; i < nb_entries; i++) {
		HashEntry *entry = &(entries[i]);
		printf ("#define L_%s \t\t %d\n", entry->key, i);
	}
	illegal_entry = find_entry_for_token ("ILLEGAL");
	rawstring_entry = find_entry_for_token ("RAWSTRING");
	printf ("gint parser_tokens[] = {\n");
	for (i = 0; i < nb_entries; i++) {
		HashEntry *entry = &(entries[i]);
		if (i!= 0)
			printf (",");
		if (entry->parser_value >= 0)
			printf ("%d", entry->parser_value);
		else
			printf ("%d", illegal_entry->parser_value);
	}
	printf ("};\n");

	printf ("gint delim_tokens[] = {\n");
	for (i = 0; i < nb_entries; i++) {
		HashEntry *entry = &(entries[i]);
		if (i != 0)
			printf (",");
		if (entry->delim_value >= 0)
			printf ("%d", entry->delim_value);
		else
			printf ("%d", rawstring_entry->delim_value);
	}
	printf ("};\n");


	return 0;
}

static HashEntry *
find_entry_for_token (char *token) 
{
	int i;

	for (i = 0; i < nb_entries; i++) {
		HashEntry *e = &(entries[i]);
		if (!strcmp (e->key, token))
			return e;
	}
	return NULL;
}



static void 
parse_line (char *line, SourceType type) 
{
	char *z, *token;
	int value;
	HashEntry *entry;
	
	z = line;
	if (strncmp (z, "#define ", 8)) {
		printf ("Expected '#define', not found: '%s'\n", line);
		exit (0);
	}
	z += 8;
	token = z + 2;
	for (; *z && *z != ' '; z++);
	*z = 0;
	z++;
	for (; *z == ' '; z++);
	value = atoi (z);
	/*printf ("%d Token: /%s/, value=%d\n", type, token, value);*/

	entry = find_entry_for_token (token);
	if (!entry) {
		nb_entries++;
		entry = &(entries[nb_entries - 1]);
		entry->key = malloc (sizeof (char) * (strlen (token) + 1));
		memcpy (entry->key, token, strlen (token) + 1);
		entry->parser_value = -1;
		entry->delim_value = -1;
	}
	if (type == TYPE_PARSER)
		entry->parser_value = value;
	else if (type == TYPE_DELIM)
		entry->delim_value = value;
}

static void
parse_contents (FILE *stream, SourceType type)
{
#define BUFSIZE 500
	char buffer[BUFSIZE];
	int read;
	char *end;

	read = fread (buffer, 1, BUFSIZE, stream);
	end = buffer + read;
	while (read > 0) {
		char *ptr;

		/* read all complete lines in buffer */
		while (end > buffer) {
			char *hold = NULL;
			for (ptr = buffer; (ptr < end) && *ptr && (*ptr != '\n'); ptr++);
			if (ptr == end)
				break;
			if (*ptr)
				hold = ptr+1;
			*ptr = 0;

			/* treat the line */
			parse_line (buffer, type);

			if (hold) {
				int l = end - hold;
				end -= hold - buffer;
				memmove (buffer, hold, l);
			}
			else
				break;
		}

		read = fread (end, 1, BUFSIZE - (end - buffer), stream);
		end += read;
	}
}

