#include <ctype.h>
#include "stringops.h"
#include "stim.h"

static char io[1024];

static char *find_token (char *start, char *end, char token)
{
	int parencount = 0;
	char *scan;

	scan = start;

	while ((scan <= end) && ((*scan != token) || (parencount > 0)))
	{
		if (*scan == '(') parencount++;
		if (*scan == ')') parencount--;
		scan++;
	}

	if (scan <= end)
		return scan;
	else
		return NULL;
}

stim_elt *stim_tree_parse (char *start, char *end)
{
	stim_elt *ret = NULL;
	char *token;

	#ifdef FUNCTIONS
	puts ("<stim_tree_parse>");
	#endif

	while (isspace (*start)) start++;
	while (isspace (*end)) end++;

	if (token = find_token (start, end, '|'))
	{
		ret = allocate (stim_elt);

		ret->left = stim_tree_parse (start, token - 1);
		ret->right = stim_tree_parse (token + 1, end);
		ret->s = "|";

		return ret;
	}
	else
	{
		if (token = find_token (start, end, '&'))
		{
			ret = allocate (stim_elt);

			ret->left = stim_tree_parse (start, token - 1);
			ret->right = stim_tree_parse (token + 1, end);
			ret->s = "&";

			return ret;
		}
		else
		{
			if (*start == '!')
			{
				ret = allocate (stim_elt);

				ret->left = NULL;
				ret->right = stim_tree_parse (start + 1, end);
				ret->s = "!";

				return ret;
			}
			else
			{
				if ((*start == '(') && (*end == ')'))
				{
					return stim_tree_parse
						(start + 1, end - 1);
				}
				else
				{
					ret = allocate (stim_elt);

					ret->left = NULL;
					ret->right = NULL;
					ret->s = clip (start, end);

					return ret;
				}
			}
		}
	}
}

void stim_output (stim_elt *se, globals *g)
{
	#ifdef FUNCTIONS
	puts ("**stim_output");
	#endif

	if (se->left)
	{
		if (!strcasecmp (se->left->s, "|") && !strcasecmp (se->s, "&"))
		{
			socket_write_noret (g->socket, "(");
			stim_output (se->left, g);
			socket_write_noret (g->socket, ")");
		}
		else stim_output (se->left, g);
	}

	socket_write_noret (g->socket, se->s);

	if (se->right)
	{
		if ((!strcasecmp (se->right->s, "|") &&
			!strcasecmp (se->s, "&")) ||
			((!strcasecmp (se->right->s, "&") ||
			!strcasecmp (se->right->s, "|")) &&
			!strcasecmp (se->s, "!")))
		{
			socket_write_noret (g->socket, "(");
			stim_output (se->right, g);
			socket_write_noret (g->socket, ")");
		}
		else stim_output (se->right, g);
	}
}

static void stim_tree_burn (stim_elt *se)
{
	#ifdef FUNCTIONS
	puts ("**stim_tree_burn");
	#endif

	if (se != NULL)
	{
		stim_tree_burn (se->left);
		stim_tree_burn (se->right);
		free (se->s);
		free (se);
	}
}

static void stim_elements_burn (stim *s)
{
	#ifdef FUNCTIONS
	puts ("**stim_elements_burn");
	#endif

	if (s != NULL)
	{
		stim_elements_burn (s->next);
		stim_tree_burn (s->tree);
		free (s);
	}
}

void stim_burn (stim_list *sl)
{
	#ifdef FUNCTIONS
	puts ("**stim_burn");
	#endif

	if (sl != NULL)
	{
		stim_elements_burn (sl->head);
		free (sl);
	}
}

void stim_add (stim_list *sl, char *l)
{
	stim *new;

	#ifdef FUNCTIONS
	puts ("**stim_add");
	#endif

	new = allocate (stim);

	new->tree = stim_tree_parse (l, l + strlen(l) - 1);
	new->next = NULL;

	if (sl->tail != NULL) sl->tail->next = new;
	sl->tail = new;
	if (sl->head == NULL) sl->head = new;

	(sl->size)++;
}

void stim_delete (stim_list *sl, stim *elt)
{
	stim *scan, *dead;

	#ifdef FUNCTIONS
	puts ("**stim_delete");
	#endif

	if (sl->head = elt)
	{
		dead = sl->head;
		sl->head = dead->next;
		if (sl->tail == dead)
			sl->tail = sl->head;

		stim_tree_burn (dead->tree);
		free (dead);
		(sl->size)--;

		return;
	}

	for
	(
		scan = sl->head;
		(scan->next != NULL) && (scan->next != elt);
		scan = scan->next
	);

	if (scan != NULL)
	{
		if (sl->tail == scan->next)
			sl->tail = scan;

		dead = scan->next;
		scan->next = dead->next;

		stim_tree_burn (dead->tree);
		free (dead);
		(sl->size)--;
	}
}

stim *stim_find_num (stim_list *sl, int n)
{
	stim *scan;

	for
	(
		scan = sl->head;
		(n > 1) && (scan != NULL);
		scan = scan->next, n--
	);

	return scan;
}

void stim_file_output (stim_elt *s, FILE *f)
{
	#ifdef FUNCTIONS
	puts ("**stim_file_output");
	#endif

	if (s->left)
	{
		if (!strcasecmp (s->left->s, "|") && !strcasecmp (s->s, "&"))
		{
			putc ('(', f);
			stim_file_output (s->left, f);
			putc (')', f);
		}
		else stim_file_output (s->left, f);
	}

	fputs (s->s, f);

	if (s->right)
	{
		if ((!strcasecmp (s->right->s, "|") &&
			!strcasecmp (s->s, "&")) ||
			((!strcasecmp (s->right->s, "&") ||
			!strcasecmp (s->right->s, "|")) &&
			!strcasecmp (s->s, "!")))
		{
			putc ('(', f);
			stim_file_output (s->right, f);
			putc (')', f);
		}
		else stim_file_output (s->right, f);
	}
}

static int stim_tree_evaluate (stim_elt *tree, name_list *nl, char *l)
{
	int result_left, result_right;

	#ifdef FUNCTIONS
	puts ("<stim_tree_evaluate>");
	#endif

	if (tree)
	{
		if (tree->right)
		{
			result_left = stim_tree_evaluate (tree->left, nl, l);
			result_right = stim_tree_evaluate (tree->right, nl, l);
			if (!strcasecmp (tree->s, "|"))
				return (result_left || result_right);
			if (!strcasecmp (tree->s, "&"))
				return (result_left && result_right);
			if (!strcasecmp (tree->s, "!"))
				return (!result_right);
		}
		else
		{
			if (!strcasecmp (tree->s, "^"))
				return (name_test (nl, l));
			return ((int) find (l, tree->s));
		}
	}
	else return 0;
}

int stim_evaluate (stim_list *sl, name_list *nl, char *l)
{
	stim *scan;
	int ret = 0;

	#ifdef FUNCTIONS
	puts ("**stim_evaluate");
	#endif

	for (scan = sl->head; scan != NULL; scan = scan->next)
		ret |= stim_tree_evaluate (scan->tree, nl, l);

	return ret;
}