
#include <stdio.h>
#include <string.h>
#include "app.h"
#include "Simplify.h"
#include "StrUtil.h"

char *simplify_file = "Simplify.txt";

SimplifyRule * new_simplify_rule()
{
	SimplifyRule *rule = app_alloc(sizeof(SimplifyRule));
	rule->match_entire = 0;
	rule->pattern = NULL;
	rule->replace = NULL;
	return rule;
}

void del_simplify_rule(SimplifyRule *rule)
{
	if (rule)
	{
		app_free(rule->pattern);
		/* app_free(rule->replace); no need, same string as pattern */
		app_free(rule);
	}
}

SimplifyList * new_simplify_list()
{
	SimplifyList *list = app_alloc(sizeof(SimplifyList));
	list->field = NULL;
	list->num_rules = 0;
	list->rules = NULL;
	return list;
}

void del_simplify_list(SimplifyList *list)
{
	if (list)
	{
		int i;
		for (i = 0; i < list->num_rules; i++)
			del_simplify_rule(list->rules[i]);
		app_free(list->rules);
		app_free(list->field);
		app_free(list);
	}
}

SimplifyRule * add_simplify_rule(SimplifyList *list)
{
	int n = list->num_rules;
	int blocksize = ((n + 1 + 16) / 16) * 16;
	SimplifyRule **rules = app_realloc(list->rules,
				sizeof(SimplifyRule *) * blocksize);
	if (! rules)
		return NULL;
	list->num_rules++;
	list->rules = rules;
	rules[n] = new_simplify_rule();
	rules[n + 1] = NULL;
	return rules[n];
}

FILE * open_simplify_file(App *app)
{
	FILE *f;

	/* Try opening the file first. */
	f = app_open_file(simplify_file, "r");
	if (f)
		return f;

	/* Try the program's resources last. */
	if (app->has_resources)
	{
		f = app_open_resource(app->program_name, simplify_file, NULL);
		if (f)
			return f;
	}

	return NULL;
}

SimplifySet * new_simplify_set()
{
	SimplifySet *set = app_alloc(sizeof(SimplifySet));
	set->num_lists = 0;
	set->lists = NULL;
	return set;
}

void del_simplify_set(SimplifySet *set)
{
	if (set)
	{
		int i;
		for (i = 0; i < set->num_lists; i++)
			del_simplify_list(set->lists[i]);
		app_free(set->lists);
		app_free(set);
	}
}

SimplifyList * add_simplify_list(SimplifySet *set)
{
	int n = set->num_lists;
	int blocksize = ((n + 1 + 16) / 16) * 16;
	SimplifyList **lists = app_realloc(set->lists,
				sizeof(SimplifyList *) * blocksize);
	if (! lists)
		return NULL;
	set->num_lists++;
	set->lists = lists;
	lists[n] = new_simplify_list();
	lists[n + 1] = NULL;
	return lists[n];
}

SimplifySet * load_simplify_set(App *app)
{
	char *line;
	FILE *f = open_simplify_file(app);
	SimplifySet *set = new_simplify_set();
	SimplifyList *list = NULL;
	SimplifyRule *rule = NULL;

	while (f)
	{
		long nbytes, nchars;
		char *s;
		int length;
		int arrow = 0;
		int equal = 0;
		int is_field = 0;

		line = app_read_utf8_line(f, &nbytes, &nchars);

		/* stop if at end of file */
		if (! line)
			break;
		/* skip empty lines */
		if (line[0] == '\0')
		{
			app_free(line);
			continue;
		}
		/* skip comment lines */
		if (line[0] == '#')
		{
			app_free(line);
			continue;
		}
		/* skip blank lines */
		if ((line[0] == '\n') && (line[1] == '\0'))
		{
			app_free(line);
			continue;
		}
		if ((line[0] == '\r') && (line[1] == '\n') && (line[2] == '\0'))
		{
			app_free(line);
			continue;
		}
		/* skip lines which have only one char */
		if (line[1] == '\0')
		{
			app_free(line);
			continue;
		}

		/* strip any trailing \r or \n */
		length = (int) strlen(line);
		if ((length > 0) && (line[length - 1] == '\n'))
			line[--length] = '\0';
		if ((length > 0) && (line[length - 1] == '\r'))
			line[--length] = '\0';

		/* find -> or => in the line, but skip first char */
		if ((s = strstr(line+1, "->")) != NULL)
		{
			/* if an arrow is found, it must be after first char */
			arrow = (int) (s - line);
			/* thus arrow is now >= 1 */
			/* now terminate the pattern part of the line */
			*s = '\0';
		}
		else if ((s = strstr(line+1, "=>")) != NULL)
		{
			/* if a double arrow is found, this means match entire text */
			equal = (int) (s - line);
			/* thus equal is now >= 1 */
			/* now terminate the pattern part of the line */
			*s = '\0';
		}
		else
		{
			/* hmm, no arrow found, might be a field name */
			if ((length > 0) && (line[length - 1] == ':'))
				is_field = 1;
		}

		if (arrow || equal)
		{
			/* we've found a pattern->replacement (or =>) rule */
			if (! list)
			{
				list = add_simplify_list(set);
				if (list)
					list->field = app_copy_string("");
			}
			if (! list)
			{
				app_free(line);
				break;
			}
			rule = add_simplify_rule(list);
			if (! rule)
			{
				app_free(line);
				break;
			}
			if (equal)
				rule->match_entire = arrow = equal;
			rule->pattern = line;
			rule->replace = line + arrow + 2;

			unescape_string(rule->pattern);
			unescape_string(rule->replace);
		}
		else if (is_field)
		{
			list = add_simplify_list(set);
			if (! list)
			{
				app_free(line);
				break;
			}
			list->field = line;
		}
		else /* skip this line since it isn't a pattern or a field */
		{
			app_free(line);
		}
	}
	
	app_close_file(f);
	return set;
}

/*
 * Return 'data', modified by running all the appropriate rules
 * to find and replace patterns with replacements.
 * The 'field' parameter controls which rule list to run.
 */
char * run_simplify(SimplifySet *set, char *field, char *data)
{
	int l, r;

	if (set == NULL)
		return data;

	for (l = 0; l < set->num_lists; l++)
	{
		SimplifyList *list = set->lists[l];

		if (strcmp(list->field, field) != 0)
		{
			/* no, this isn't the correct simplify list */
			continue;
		}
		/* only work on correct simplify list */
		for (r = 0; r < list->num_rules; r++)
		{
			SimplifyRule *rule = list->rules[r];

			if (rule->match_entire) /* => rule */
			{
				if (!strcmp(data, rule->pattern))
				{
					app_free(data);
					data = app_copy_string(rule->replace);
				}
			}
			else /* -> rule */
			{
				data = find_replace(data, rule->pattern, rule->replace);
			}
		}
	}
	return data;
}

