/*
 * Copyright (C) 2015-2016 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.
 */

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

#include "scxml.h"

#define PATH_DOT "path.dot"
#define RED_PATH_DOT "reduced_path.dot"

#define FILENAME "sig_bus_test"

#define MAX_PARAMS 16

struct assign_entry {
	char name[MAX_NAME];
	int expr;
	char flags[MAX_FLAG];
	int logic;
};

struct named_assign {
	char name[MAX_NAME];
	struct assign_entry ass[MAX_DATA];
	int n_ass;
};

struct values {
	char *name[MAX_BOOL / 2];
	char *in_name[MAX_BOOL / 2];
	int value[MAX_BOOL / 2];

	int count;
};

struct state {
	char type[MAX_NAME];
	char name[MAX_NAME];
	struct scxml_state *scxml_state;
	struct assign_entry ass[MAX_DATA];
	int n_ass;

	int if_result[MAX_ELEM];
	struct values *if_values[MAX_ELEM];

	int elif_result[MAX_ELEM];
	struct values *elif_values[MAX_ELEM];

	int trans_result[MAX_ELEM];
	struct values *trans_values[MAX_ELEM];
};

struct funcs {
	struct paths *paths[MAX_FUNCS];
	const char *name[MAX_FUNCS];
	const char *action[MAX_FUNCS];
	char type[MAX_NAME];
	int n;
};

struct paths {
	struct path *paths[MAX_PATH];
	int used[MAX_PATH];
	int n;
};

struct path {
	struct superstate *path[MAX_PATH_LEN];
	int n;
};

struct superstate {
	struct state *active[MAX_ACTIVE];
	int n_active;
	struct assign_entry ass[MAX_DATA];
	int n_ass;
	const char *action;
};

enum var_type {
	v_if,
	v_elif,
	v_trans,
	v_extern
};

struct var_point {
	int path;
	struct superstate *superstate;
	char *active;
	char *orig_active;
	int value;

	enum var_type type;
	int count;

	char *name;
	char *orig;

	int junction;
};

struct var {
	struct paths *paths;
	char name[MAX_NAME][MAX_VAR];
	char orig[MAX_NAME][MAX_VAR];
	char orig_active[MAX_NAME][MAX_VAR];
	int indirect[MAX_VAR];
	int valid[MAX_VAR];
	int n_name;

	struct superstate *cur_superstate;
	struct state *cur_active;
	int cur_path;

	struct var_point varp[MAX_VAR];
	int n_varp;

	const char *func_name;
};

struct intersection {
	int hit[MAX_VAR];
	int bytes[MAX_VAR];
	int n_hit;
};

struct pair {
	int a;
	int b;
};

struct func_param {
	char *type;
	char *name;
	int value;
	int ts;
};

struct func_decl {
	const char *name;
	struct func_param params[MAX_PARAMS];
	int n_params;
};

static void __attribute__((noreturn))
usage(int retval, char *progname)
{
	fprintf(stderr, "Usage: %s <*.xml> [-o output]\n", progname);
	exit(retval);
}

int op_preced(const char c)
{
	switch(c)    {
	case '|':
		return 6;
	case '&':
		return 5;
	case '!':
		return 4;
	case '*':  case '/': case '%':
		return 3;
	case '+': case '-':
		return 2;
	case '=':
		return 1;
	}
	return 0;
}

int op_left_assoc(const char c)
{
	switch(c)    {
		/* left to right */
	case '*': case '/': case '%': case '+': case '-': case '|': case '&':
		return 1;
		/* right to left */
	case '=': case '!':
		return 0;
	}
	return 0;
}

unsigned int op_arg_count(const char c)
{
	switch(c)  {
	case '*': case '/': case '%': case '+': case '-': case '=': case '&': case '|':
		return 2;
	case '!':
		return 1;
	default:
		return c - 'A';
	}
	return 0;
}

#define is_operator(c)  (c == '+' || c == '-' || c == '/' || c == '*' || c == '!' || c == '%' || c == '=' || c == '&' || c == '|')
#define is_function(c)  (c >= 'A' && c <= 'Z')
#define is_ident(c)     ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z'))

int shunting_yard(const char *input, char *output)
{
	const char *strpos = input, *strend = input + strlen(input);
	char c, *outpos = output;

	char stack[64];       /* operator stack */
	unsigned int sl = 0;  /* stack length */
	char     sc;          /* used for record stack element */

	while(strpos < strend)   {
		/* read one token from the input stream */
		c = *strpos;
		if(c != ' ')    {
			/* If the token is a number (identifier), then add it to the output queue. */
			if(is_ident(c))  {
				*outpos = c; ++outpos;
			}
			/* If the token is a function token, then push it onto the stack. */
			else if(is_function(c))   {
				stack[sl] = c;
				++sl;
			}
			/* If the token is a function argument separator (e.g., a comma): */
			else if(c == ',')   {
				int pe = 0;
				while(sl > 0)   {
					sc = stack[sl - 1];
					if(sc == '(')  {
						pe = 1;
						break;
					}
					else  {
						/* Until the token at the top of the stack is a left parenthesis,
						// pop operators off the stack onto the output queue. */
						*outpos = sc;
						++outpos;
						sl--;
					}
				}
				/* If no left parentheses are encountered, either the separator was misplaced
				// or parentheses were mismatched. */
				if(!pe)   {
					printf("Error: separator or parentheses mismatched\n");
					return 0;
				}
			}
			/* If the token is an operator, op1, then: */
			else if(is_operator(c))  {
				while(sl > 0)    {
					sc = stack[sl - 1];
					if(is_operator(sc) &&
					   ((op_left_assoc(c) && (op_preced(c) >= op_preced(sc))) ||
					    (op_preced(c) > op_preced(sc))))   {
						/* Pop op2 off the stack, onto the output queue; */
						*outpos = sc;
						++outpos;
						sl--;
					}
					else   {
						break;
					}
				}
				/* push op1 onto the stack. */
				stack[sl] = c;
				++sl;
			}
			/* If the token is a left parenthesis, then push it onto the stack.
			 * */
			else if(c == '(')   {
				stack[sl] = c;
				++sl;
			}
			/* If the token is a right parenthesis: */
			else if(c == ')')    {
				int pe = 0;
				/* Until the token at the top of the stack is a left parenthesis,
				// pop operators off the stack onto the output queue */
				while(sl > 0)     {
					sc = stack[sl - 1];
					if(sc == '(')    {
						pe = 1;
						break;
					}
					else  {
						*outpos = sc;
						++outpos;
						sl--;
					}
				}
				/* If the stack runs out without finding a left parenthesis, then there are mismatched parentheses. */
				if(!pe)  {
					printf("Error: parentheses mismatched\n");
					return 0;
				}
				/* Pop the left parenthesis from the stack, but not onto the output queue. */
				sl--;
				/* If the token at the top of the stack is a function token, pop it onto the output queue. */
				if(sl > 0)   {
					sc = stack[sl - 1];
					if(is_function(sc))   {
						*outpos = sc;
						++outpos;
						sl--;
					}
				}
			}
			else  {
				printf("Unknown token %c\n", c);
				return 0; /* Unknown token */
			}
		}
		++strpos;
	}
	/* When there are no more tokens to read:
	// While there are still operator tokens in the stack: */
	while(sl > 0)  {
		sc = stack[sl - 1];
		if(sc == '(' || sc == ')')   {
			printf("Error: parentheses mismatched\n");
			return 0;
		}
		*outpos = sc;
		++outpos;
		--sl;
	}
	*outpos = 0; /* Null terminator */
	return 1;
}

static int
cmpstringp(const void *p1, const void *p2)
{
	return strcmp(* (char * const *) p1, * (char * const *) p2);
}

void
cat_sorted_strings(char **sort, int n_sort, char *dest)
{
	int i;
	if (n_sort == 0) {
		return;
	}
	qsort(sort, n_sort, sizeof(char *), cmpstringp);

	for (i = 0; i < n_sort; i++) {
		if (i > 0) {
			strcat(dest, ",  ");
		}
		strcat(dest, sort[i]);
	}
}

struct scxml_state *
get_state_by_name(struct scxml *scxml, const char *name)
{
	struct scxml_state *state;
	for (state = scxml->state_first; state; state = state->next) {
		if (strcmp(state->id, name) == 0) {
			return state;
		}
	}
	return NULL;
}

char
resolve_char_logic(const char a, const char b)
{
	/*
	   'U': uninitialized. This signal hasn't been set yet.
	   'X': unknown. Impossible to determine this value/result.
	   '0': logic 0
	   '1': logic 1
	   'Z': High Impedance
	   'W': Weak signal, can't tell if it should be 0 or 1.
	   'L': Weak signal that should probably go to 0
	   'H': Weak signal that should probably go to 1
	   '-': Don't care.
	   */

	/*
	   ---------------------------------------------------------
	   |  U    X    0    1    Z    W    L    H    -        |   |
	   ---------------------------------------------------------
	   ( 'U', 'U', 'U', 'U', 'U', 'U', 'U', 'U', 'U' ), -- | U |
	   ( 'U', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X' ), -- | X |
	   ( 'U', 'X', '0', 'X', '0', '0', '0', '0', 'X' ), -- | 0 |
	   ( 'U', 'X', 'X', '1', '1', '1', '1', '1', 'X' ), -- | 1 |
	   ( 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', 'X' ), -- | Z |
	   ( 'U', 'X', '0', '1', 'W', 'W', 'W', 'W', 'X' ), -- | W |
	   ( 'U', 'X', '0', '1', 'L', 'W', 'L', 'W', 'X' ), -- | L |
	   ( 'U', 'X', '0', '1', 'H', 'W', 'W', 'H', 'X' ), -- | H |
	   ( 'U', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X' )  -- | - |
	   ---------------------------------------------------------
	   */

	char ret;

	if (! strchr("UX01ZWLH-", a)) {
		fprintf(stderr, "char '%c' is not compatible with std_logic", a);
		assert(0);
	}
	if (! strchr("UX01ZWLH-", b)) {
		fprintf(stderr, "char '%c' is not compatible with std_logic", b);
		assert(0);
	}

	if (a == 'U' || b == 'U') {
		ret = 'U';
	} else if (a == 'X' || b == 'X') {
		ret = 'X';
	} else if (a == '-' || b == '-') {
		ret = 'X';
	} else {
		switch (a) {
		case '0':
			if (b == '1') {
				ret = 'X';
			} else {
				ret = '0';
			}
			break;
		case '1':
			if (b == '0') {
				ret = 'X';
			} else {
				ret = '1';
			}
			break;
		case 'Z':
			ret = b;
			break;
		case 'W':
			if (b == '0' || b == '1') {
				ret = b;
			} else {
				ret = 'W';
			}
			break;
		case 'L':
			if (b == '0' || b == '1') {
				ret = b;
			} else if (b == 'Z' || b == 'L') {
				ret = 'L';
			} else if (b == 'W' || b == 'H') {
				ret = 'W';
			} else {
				assert(0);
			}
			break;
		case 'H':
			if (b == '0' || b == '1') {
				ret = b;
			} else if (b == 'Z' || b == 'H') {
				ret = 'H';
			} else if (b == 'W' || b == 'L') {
				ret = 'W';
			} else {
				assert(0);
			}
			break;
		default:
			fprintf(stderr, "couldnt parse std_logic %c\n", a);
			assert(0);
		}
	}
	fprintf(stderr, "resolved %c and %c to %c\n", a, b, ret);
	return ret;
}

int
resolve(const int a, const int b)
{
	return (int) resolve_char_logic((char) a, (char) b);
}

void
strip_cond(const char *cond, char *dest, char **tokens)
{
	int i;
	int j = 0;

	if (strlen(cond) < 2) {
		fprintf(stderr, "Cond %s too short\n", cond);
		assert(0);
	}

	dest[j] = '\0';
	for (i = 0; cond[i]; i++) {
		if (i > MAX_BOOL -2) {
			fprintf(stderr, "stripping cond %s exceeded MaxBool\n", cond);
			assert(0);
		}
		switch (cond[i]) {
		case '(':
			if (j > 0 && dest[j - 1] != ' ') {
				dest[j++] = ' ';
			}
			dest[j++] = '(';
			dest[j++] = ' ';
			break;
		case '[':
			if (j > 0 && dest[j - 1] != ' ') {
				dest[j++] = ' ';
			}
			dest[j++] = '(';
			dest[j++] = ' ';
			break;
		case '{':
			if (j > 0 && dest[j - 1] != ' ') {
				dest[j++] = ' ';
			}
			dest[j++] = '(';
			dest[j++] = ' ';
			break;
		case ')':
			if (j > 0 && dest[j - 1] != ' ') {
				dest[j++] = ' ';
			}
			dest[j++] = ')';
			dest[j++] = ' ';
			break;
		case ']':
			if (j > 0 && dest[j - 1] != ' ') {
				dest[j++] = ' ';
			}
			dest[j++] = ')';
			dest[j++] = ' ';
			break;
		case '}':
			if (j > 0 && dest[j - 1] != ' ') {
				dest[j++] = ' ';
			}
			dest[j++] = ')';
			dest[j++] = ' ';
			break;
		case '!':
			if (j > 0 && dest[j - 1] != ' ') {
				dest[j++] = ' ';
			}
			dest[j++] = '!';
			dest[j++] = ' ';
			break;
		case '\n':
			if (j > 0 && dest[j - 1] != ' ') {
				dest[j++] = ' ';
			}
			break;
		case '\t':
			if (j > 0 && dest[j - 1] != ' ') {
				dest[j++] = ' ';
			}
			break;
		case ' ':
			if (j > 0 && dest[j - 1] == ' ') {
				break;
			}
		default:
			dest[j++] = cond[i];
		}
	}
	dest[j] = '\0';

	for(i = 0, tokens[i] = strtok(dest, " "); tokens[i]; tokens[++i] = strtok(NULL, " ")) {
	}
}

void print_values(FILE *out, struct values *tmp)
{
	int i;
	fprintf(out, "values\n");
	if (!tmp) {
		fprintf(out, "\tis NULL\n");
		return;
	}
	for (i = 0; i < tmp->count; i++) {
		fprintf(out, "\tname_%i is %s\n", i, tmp->name[i]);
		fprintf(out, "\tinname_%i is %s\n", i, tmp->in_name[i]);
		fprintf(out, "\tvalue_%i is %i\n", i, tmp->value[i]);
	}
}

void
print_super(FILE * out, struct superstate *s) {
	int i, j;
	fprintf(out, "super{");
	for (i = 0; i < s->n_active; i++) {
		fprintf(out, "%s", s->active[i]->scxml_state->id);
		if ((i + 1) < s->n_active) {
			fprintf(out, ", ");
		}
	}
	fprintf(out, "}\n");

	for (i = 0; i < s->n_ass; i++) {
		if (strstr(s->ass[i].name, "error")
		    || strstr(s->ass[i].name, "pid")
		    || strstr(s->ass[i].name, "cmd")
		    || strstr(s->ass[i].name, "addr")
		   ) {
			fprintf(out, "\t%s is %i\n", s->ass[i].name, s->ass[i].expr);
		}
	}
	for (i = 0; i < s->n_active; i++) {
		for (j = 0; j < s->active[i]->n_ass; j++) {
			if (strstr(s->active[i]->ass[j].name, "cmd")
			    //|| strstr(s->active[i]->ass[j].name, "pid")
			   ) {
				fprintf(out, "\t%s in %s is %i\n", s->active[i]->ass[j].name, s->active[i]->name, s->active[i]->ass[j].expr);

			}
		}
		/* DEBUG OUTPUT */
		/*struct scxml_if *xif;
		 *for (xif = s->active[i]->scxml_state->if_first; xif; xif = xif->next) {
		 *	int k;
		 *	k = xif->count;
		 *	printf("xif %i (%s) is %i\n", xif->count, xif->cond, s->active[i]->if_result[xif->count]);
		 *	printf("gotten by\n");
		 *	if (!s->active[i]->if_values[xif->count]) {
		 *		printf("couldnt find if_values\n");
		 *	} else {
		 *		for (j = 0; j < s->active[i]->if_values[k]->count; j++) {
		 *			printf("cond # %i\n", j);
		 *			printf("\tname %s\n",  s->active[i]->if_values[k]->name[j]);
		 *			if (s->active[i]->if_values[k]->in_name[j]) {
		 *				printf("\tin_name %s\n",  s->active[i]->if_values[k]->in_name[j]);
		 *			}
		 *			printf("\tvalue %i\n",  s->active[i]->if_values[k]->value[j]);
		 *		}
		 *	}
		 *}*/
		//printf("\n");
	}
}

void
find_variable_data(struct scxml *scxml, struct var *var)
{
	struct scxml_data *data;
	struct scxml_datamodel *model;
	struct scxml_function *func;

	for (func = scxml->func_first; func; func = func->next) {
		if (strcmp(var->func_name, func->name) == 0) {
			for (model = func->model_first; model; model = model->next) {
				for (data = model->data_first; data; data = data->next) {
					if (data->flags
					    && strcmp(data->flags, "ign") != 0) {
						/* this may change the path */
						strcpy(var->orig_active[var->n_name], "master");
						strcpy(var->orig[var->n_name], data->id);
						strcpy(var->name[var->n_name], data->id);
						var->indirect[var->n_name] = 0;
						var->valid[var->n_name] = 1;
						var->n_name++;
					}
				}
			}
		}
	}
}

struct var_point *
add_varp(struct var *var, enum var_type type, char *name, char *orig, char *orig_active, int value, int count)
{
	int i;
	struct var_point* varp;

	for (i = 0; i < var->n_varp; i++) {
		if (var->varp[i].type == type
		    && strcmp(var->varp[i].name, name) == 0
		    && strcmp(var->varp[i].orig, orig) == 0
		    && strcmp(var->varp[i].orig_active, orig_active) == 0
		   ) {
			if (strcmp(var->cur_active->type,
				   var->varp[i].active) == 0) {
				/* this point is already known */
				//printf("ADD_VARP skipping %s with orig %s\n", name, orig);
				/* if values are changing, the value is not fix
				 * anymore*/
				if (value != var->varp[i].value) {
					var->varp[i].value = INT_MIN;
				}
				return NULL;
			}
		}
	}
	varp = &var->varp[var->n_varp];

	varp->path = var->cur_path;
	var->varp[var->n_varp].superstate = var->cur_superstate;
	var->varp[var->n_varp].active = var->cur_active->type;
	var->varp[var->n_varp].orig_active = orig_active;
	var->varp[var->n_varp].type = type;
	var->varp[var->n_varp].count = count;
	var->varp[var->n_varp].name = malloc(strlen(name) + 1);
	strcpy(var->varp[var->n_varp].name, name);
	var->varp[var->n_varp].orig = orig;
	var->varp[var->n_varp].junction = 0;
	var->varp[var->n_varp].value = value;
	//printf("ADD_VARP: adding %s with orig %s, type %i\n", name, var->varp[var->n_varp].orig, type);
	var->n_varp++;

	return varp;
}

int
get_value_from_values(struct state *state, char *value, enum var_type type, int count) {
	int i, ret;
	struct values *tmp;

	ret = INT_MIN;
	switch (type) {
	case v_extern:
		return ret;
	case v_if:
		tmp = state->if_values[count];
		break;
	case v_elif:
		tmp = state->elif_values[count];
		break;
	case v_trans:
		tmp = state->trans_values[count];
		break;
	default:
		fprintf(stderr, "unknown type %i, in get_value_from_values\n", type);
		assert(0);
	}

	if (!tmp) {
		/* this condition was never read - might be dead code
		 * can happen due to not induced errors */
		return ret;
	}

	for (i = 0; i < tmp->count; i++) {
		if (strcmp(value, tmp->name[i]) == 0) {
			ret = tmp->value[i];
		} else if (tmp->in_name[i]
			   && strcmp(value, tmp->in_name[i]) == 0) {
			ret = tmp->value[i];
		}
	}

	return ret;
}

struct var_point *
check_var_cond(struct state *state, const char *cond, struct var *var, enum var_type type, int count) {
	int i, j, value;

	char src[MAX_BOOL];
	char *tokens[MAX_BOOL / 2];

	char *tmp;

	if (!cond) {
		/* no variability possible */
		return NULL;
	}

	for (i = 0; i < var->n_name; i++) {
		/* just a quick precheck */
		if (var->valid[i]) {
			if (strstr(cond, var->name[i])) {
				i = -1;
				break;
			}
		}
	}

	if (i > 0) {
		return NULL;
	}

	strip_cond(cond, src, tokens);

	for (j = 0; tokens[j]; j++) {
		/* sort out '(' ')' 'and' 'or' etc */
		if (strlen(tokens[j]) < 4) {
			continue;
		} else if (strstr(tokens[j], "==")) {
			/* form must be a==b */
			assert(strchr(tokens[j], '='));
			tmp = strchr(tokens[j], '=') + 2;
			*strchr(tokens[j], '=') = '\0';
			if (! strlen(tmp) > 2) {
				fprintf(stderr, "couldnt parse %s\n", tokens[j]);
				assert(0);
			}
			for (i = 0; i < var->n_name; i++) {
				if (var->valid[i]) {
					if (strcmp(var->name[i], tmp) == 0
					    || ( tmp[0] == '$' && strcmp(var->name[i], &tmp[1]) == 0)) {
						value = get_value_from_values(state, var->name[i], type, count);
					} else if (strncmp(var->name[i], tokens[j], strlen(var->name[i])) == 0) {
						value = get_value_from_values(state, tokens[j], type, count);
					} else {
						continue;
					}
					return add_varp(var, type, var->name[i], var->orig[i], var->orig_active[i], value, count);
				}
			}
		} else {
			fprintf(stderr, "parsing error in cond %s with token %s\n", cond, tokens[j]);
			assert(0);
		}
	}
	return NULL;
}

void
find_variable_trans(struct state *state, struct var *var) {

	struct scxml_trans * trans;

	trans = state->scxml_state->trans_first;
	for (;trans; trans = trans->next) {
		check_var_cond(state, trans->cond, var, v_trans, trans->count);
	}
}

void
find_variable_ass(struct scxml_assign *ass, struct var *var, struct var_point *orig, char *active) {
	int i, j;
	char tmp[MAX_NAME];

	for (;ass; ass = ass->next) {
		if (ass->location[0] == '*') {
			/* this is an intern value -> cant be a varp */
			continue;
		}
		tmp[0] = '\0';
		if (ass->location[0] == '&') {
			strcat(tmp, &ass->location[1]);
		} else {
			strcat(tmp, ass->location);
		}


		if (orig) {
			/* this is added in any case */
			/* check if this is already in names */
			for (i = 0; i < var->n_name; i++) {
				if (var->valid[i]) {
					//printf("comparing %s with %s\n", tmp, var->name[i]);
					if (strcmp(tmp, var->name[i]) == 0) {
						/* if this is assigned to sth its no longer a
						 * valid variability point */
						//printf("invalidating %s (%s) due to %s = %s\n", tmp, var->orig[i], ass->location, ass->expr);
						var->valid[i] = 0;
						break;
					}
				}
			}
			/* new variability name at ass */
			//printf("adding %s due to %s = %s\n", tmp, ass->location, ass->expr);
			//printf("with orig name is %s\n", orig->name);
			//printf("with orig orig is %s\n", orig->orig);
			strcpy(var->orig_active[var->n_name], active);
			strcpy(var->name[var->n_name], tmp);
			strcpy(var->orig[var->n_name], orig->name);
			//printf("with orig %s\n", var->orig[var->n_name]);
			//printf("\n");
			var->indirect[var->n_name] = 0;
			var->valid[var->n_name] = 1;
			var->n_name++;
		} else {
			for(i = 0; i < var->n_name; i++) {
				if (! var->valid[i]) {
					continue;
				}
				/* no orig -> check if this creates a new
				 * varpoint */
				if (ass->expr[0] == '$' && strcmp(&ass->expr[1], var->name[i]) == 0) {

					/* check if this is already in names */
					for (j = 0; j < var->n_name; j++) {
						if (var->valid[i]) {
							//printf("comparing %s with %s\n", tmp, var->name[j]);
							if (strcmp(var->name[j], tmp) == 0) {
								//printf("invalidating %s due to %s = %s\n", tmp, ass->location, ass->expr);
								var->valid[j] = 0;
							}
						}
					}
					/* new variability name at ass */
					//printf("adding %s due to %s = %s\n", tmp, ass->location, ass->expr);
					strcpy(var->orig_active[var->n_name], active);
					strcpy(var->name[var->n_name], tmp);
					strcpy(var->orig[var->n_name], var->orig[i]);
					//printf("with orig %s\n", var->orig[var->n_name]);
					var->indirect[var->n_name] = 0;
					var->valid[var->n_name] = 1;
					var->n_name++;
					//printf("\n");
					break;
				}
			}
		}
	}
}

void
add_var_ex(struct scxml_extern *ex, struct var *var, struct state *state)
{
	int i, j, value;

	int origs[MAX_VAR];
	int n_origs = 0;

	if (!ex) {
		return;
	}

	for (; ex; ex = ex->next) {
		if (ex->input) {
			char save_in[strlen(ex->input) + 1];
			char *save_p;

			char *in_token[strlen(ex->input)];

			strcpy(save_in, ex->input);

			for (i = 0, in_token[i] = strtok_r(save_in, ", ", &save_p); in_token[i]; in_token[++i] = strtok_r(NULL, ", ", &save_p)) {
				/* this is a new varp if var->name == token[i] exists */
				for (j = 0; j < var->n_name; j++) {
					if (var->valid[j]
					    && strcmp(in_token[i], var->name[j]) == 0) {
						value = get_value_from_values(NULL, var->name[j], v_extern, 0);
						add_varp(var, v_extern, in_token[i], var->orig[j], var->orig_active[j], value, 0);
						/* save this orig for later */
						origs[n_origs] = j;
						n_origs++;

						j = -1;
						break;
					}
				}
			}
		}

		if (ex->output) {
			char save_out[strlen(ex->output) + 1];
			char *save_p;

			char *token[strlen(ex->output)];

			strcpy(save_out, ex->output);

			for (i = 0, token[i] = strtok_r(save_out, ", ", &save_p); token[i]; token[++i] = strtok_r(NULL, ", ", &save_p)) {
				char *tmp = malloc(strlen(token[i]) + 1);
				tmp[0] = '\0';
				if (token[i][0] == '&') {
					strcat(tmp, &token[i][1]);
				} else {
					strcat(tmp, token[i]);
				}

				/* add to var_name */
				for (j = 0; j < var->n_name; j++) {
					if (var->valid[j]
					    && strcmp(tmp, var->name[j]) == 0) {
						var->valid[j] = 0;
						break;
					}
				}

				strcpy(var->orig_active[var->n_name], state->type);
				strcpy(var->orig[var->n_name], tmp);
				strcpy(var->name[var->n_name], tmp);
				var->indirect[var->n_name] = 1;
				var->valid[var->n_name] = 1;
				var->n_name++;
				free(tmp);
			}
		}
	}
}

void
get_variable_if(struct scxml_if *xif, struct var *var, struct state *tmp)
{
	struct scxml_if *subif;
	struct scxml_elif *elif;
	struct scxml_else *xelse;

	int flag = 0;
	struct var_point *orig;

	orig = check_var_cond(tmp, xif->cond, var, v_if, xif->count);

	if (tmp->if_result[xif->count]) {
		flag = 1;
		add_var_ex(xif->ex_first, var, tmp);
		find_variable_ass(xif->assign_first, var, orig, tmp->type);

		for (subif = xif->if_first; subif; subif = subif->next) {
			get_variable_if(subif, var, tmp);
		}
	} else {
		for (elif = xif->elif_first; elif; elif = elif->next){
			check_var_cond(tmp, elif->cond, var, v_elif, elif->count);
			if (tmp->elif_result[elif->count]) {
				flag = 1;
				find_variable_ass(elif->assign_first, var, orig, tmp->type);
				for (subif = elif->if_first; subif; subif = subif->next) {
					get_variable_if(subif, var, tmp);
				}
				break;
			}
		}
	}
	if (!flag) {
		for (xelse = xif->else_first; xelse; xelse = xelse->next) {
			find_variable_ass(xelse->assign_first, var, orig, tmp->type);
			for (subif = xelse->if_first; subif; subif = subif->next) {
				get_variable_if(subif, var, tmp);
			}
		}
	}
}

void
get_var_superstate(struct scxml *scxml, struct var *var, struct superstate *super) {
	struct scxml_if *xif;
	struct scxml_onentry *entry;
	struct scxml_onexit *exit;
	struct scxml_extern *ex;

	struct scxml_state *state;

	int i;

	var->cur_active = NULL;

	for (i = 0; i < super->n_active; i++) {
		var->cur_active = super->active[i];
		state = super->active[i]->scxml_state;

		for (entry = state->onentry_first; entry; entry = entry->next) {
			find_variable_ass(entry->assign_first, var, NULL, super->active[i]->type);
			for (xif = entry->if_first; xif; xif = xif->next) {
				get_variable_if(xif, var, super->active[i]);
			}
			for (ex = entry->ex_first; ex; ex = ex->next) {
				add_var_ex(ex, var, super->active[i]);
			}
			find_variable_trans(super->active[i], var);
		}
	}

	for (i = 0; i < super->n_active; i++) {
		state = super->active[i]->scxml_state;
		var->cur_active = super->active[i];

		find_variable_ass(state->assign_first, var, NULL, super->active[i]->type);
		for (xif = state->if_first; xif; xif = xif->next) {
			get_variable_if(xif, var, super->active[i]);
		}
		find_variable_trans(super->active[i], var);
	}
	for (i = 0; i < super->n_active; i++) {
		state = super->active[i]->scxml_state;
		var->cur_active = super->active[i];

		for (exit = state->onexit_first; exit; exit = exit->next) {
			find_variable_ass(exit->assign_first, var, NULL, super->active[i]->type);
			for (xif = exit->if_first; xif; xif = xif->next) {
				get_variable_if(xif, var, super->active[i]);
			}
			for (ex = exit->ex_first; ex; ex = ex->next) {
				add_var_ex(ex, var, super->active[i]);
			}
			find_variable_trans(super->active[i], var);
		}
	}
}

void
get_variability(struct scxml *scxml, struct var *var)
{
	int i, j;

	for (i = 0; i < var->paths->n; i++) {
		if (var->paths->used[i] == i) {
			var->cur_path = i;
			for (j = 0; j < var->paths->paths[i]->n; j++) {
				var->cur_superstate = var->paths->paths[i]->path[j];
				get_var_superstate(scxml, var, var->paths->paths[i]->path[j]);
			}
			break;
		}
	}

	if (var->paths->used[i] != i) {
		fprintf(stderr, "unused funcion %s\n", var->func_name);
		assert(0);
	}
}

char *
get_type(int bytes)
{
	switch (bytes) {
	case 1:
		return strdup("char");
	case 8:
		return strdup("uint8_t");
	case 16:
		return strdup("uint16_t");
	case 32:
		return strdup("uint32_t");
	case 64:
		return strdup("uint64_t");
	default:
		fprintf(stderr, "unknown byte size %i\n", bytes);
		assert(0);
	}
}

void
free_function(struct func_decl *d) {
	int i;
	for (i = 0; i < d->n_params; i++) {
		free(d->params[i].type);
	}
	free(d);
}

struct func_decl *
get_functions(struct var *var, struct intersection *inter)
{
	struct var_point *tmp;
	struct func_decl *ret = malloc(sizeof(struct func_decl));
	int j;

	for (j = 0; j < inter->n_hit; j++) {
		tmp = &var->varp[inter->hit[j]];

		/* TODO
		 * if  communication should be bit by bit, split up in
		 * several functions. -> does not work with current
		 * faumachine */
		if ( strcmp(tmp->active, "target") == 0) {
			ret->params[j].type = get_type(inter->bytes[j]);
			ret->params[j].name = tmp->orig;
			ret->params[j].value = tmp->value;
			ret->params[j].ts = 1;
		} else if (strcmp(tmp->active, "master") == 0) {
			if (j == 0) {
				fprintf(stderr, "parsing functions, initial dataflow must come from master\n");
				assert(0);
			}
			ret->params[j].type = get_type(inter->bytes[j]);
			ret->params[j].name = tmp->orig;
			ret->params[j].value = tmp->value;
			ret->params[j].ts = 0;
		}
	}
	ret->n_params = j;
	return ret;
}

void
write_merge_func(FILE *f, struct func_decl *d) {
	int i;
	int j;

	for (j = 0; j < 2; j++) {
		if (j) {
			fprintf(f, "static void\nbus_(s0_%s)(\n", d->name);
		} else {
			fprintf(f, "static void\nbus_(s1_%s)(\n", d->name);
		}
		fprintf(f, "\tvoid *_f,\n");
		for (i = 0; i < d->n_params; i++) {
			fprintf(f, "\t%s ", d->params[i].type);
			if (d->params[i].ts) {
				fprintf(f, "*");
			}
			fprintf(f, "%s", d->params[i].name);
			if (i < (d->n_params - 1)) {
				fprintf(f, ",");
			} else {
				fprintf(f, "\n)");
			}
			fprintf(f, "\n");
		}

		fprintf(f, "{\n");
		fprintf(f, "\tstruct bus_(merge) *f = _f;\n");
		if (j) {
			fprintf(f, "\treturn bus_(%s)(f->s1, f, bs, val);\n", d->name);
		} else {
			fprintf(f, "\treturn bus_(%s)(f->s0, f, bs, val);\n", d->name);
		}
		fprintf(f, "}\n");
		fprintf(f, "\n");
	}
}

void
write_c_func(FILE *f, struct func_decl *d) {
	int i;

	fprintf(f, "int\nbus_(%s)(\n", d->name);
	for (i = 0; i < d->n_params; i++) {
		fprintf(f, "\t%s ", d->params[i].type);
		if (d->params[i].ts) {
			fprintf(f, "*");
		}
		fprintf(f, "%s", d->params[i].name);
		if (i < (d->n_params - 1)) {
			fprintf(f, ",");
		} else {
			fprintf(f, "\n)");
		}
		fprintf(f, "\n");
	}

	fprintf(f, "{\n");

	///* determine how many parameters are transfered in transaction j */
	//int n_params;
	//for (j = 0; j < d->n_params; j += n_params) {
	//	for (n_params = j; n_params < d->n_params; n_params++) {
	//		/* if a certain return value is needed this
	//		 * would be the place to split up in several
	//		 * functions. */
	//		if (d->params[n_params].ts && value != INT_MIN) {
	//			break;
	//		}
	//		/* function has n_params parameter, starting at j */
	//	}
	//	if (n_params == 0) {
	//		fprintf(stderr, "parsed function without parameters\n");
	//		assert(0);
	//	}
	//}

	fprintf(f, "\tunsigned int nr;\n\n");
	fprintf(f, "\tfor (nr = 0; ; nr++) {\n");
	fprintf(f, "\t\tint (*func)(");

	for (i = 0; i < d->n_params; i++) {
		fprintf(f, "%s", d->params[i].type);
		if (d->params[i].ts) {
			fprintf(f, " *");
		}
		if (i < (d->n_params - 1)) {
			fprintf(f, ", ");
		} else {
			fprintf(f, ");\n");
		}
	}

	fprintf(f, "\t\tvoid *func_s;");
	fprintf(f, "\n");
	fprintf(f, "\t\tif (nr == b->member_count) {\n");
	fprintf(f, "\t\t\t/* Not found. */\n");
	fprintf(f, "\t\t\treturn -1;\n");
	fprintf(f, "\t\t}\n");
	fprintf(f, "\t\tif (b->member[nr].s == s) {\n");
	fprintf(f, "\t\t\t/* Don't ask myself. */\n");
	fprintf(f, "\t\t\tcontinue;\n");
	fprintf(f, "\t\t}\n");
	fprintf(f, "\t\tfunc = b->member[nr].f->%s;\n", d->name);

	fprintf(f, "\t\tif (func\n");
	fprintf(f, "\t\t && func(");

	for (i = 0; i < d->n_params; i++) {
		fprintf(f, "%s", d->params[i].name);
		if (i < (d->n_params - 1)) {
			fprintf(f, ", ");
		} else {
			fprintf(f, ") == 0) {\n");
		}
	}

	/* check values that have to be set */
	int explained = 0;
	for (i = 0; i < d->n_params; i++) {
		if (d->params[i].value != INT_MIN) {
			if (!explained) {
				fprintf(f, "\t\t\t/* certain values have to be set according to spec */\n");
				explained = 1;
			}
			fprintf(f, "\t\t\tif (%s != %i) {\n", d->params[i].name, d->params[i].value);
			fprintf(f, "\t\t\t\tcontinue;\n");
			fprintf(f, "\t\t\t}\n");
		}
	}


	fprintf(f, "\t\t\treturn 0;\n");
	fprintf(f, "\t\t}\n");
	fprintf(f, "\t}\n");
	fprintf(f, "}\n");
	fprintf(f, "\n");
}

void
write_c(struct funcs *funcs, const char *filename, struct var **var, struct intersection **inter)
{
	FILE *f;
	char name[strlen(filename) + 3];

	struct func_decl *functions[MAX_FUNCS];
	int func_n;

	int i;

	sprintf(name, "%s.c", filename);
	f = fopen(name, "w");
	if (f == NULL) {
		fprintf(stderr, "Can't open output file %s!\n",
			name);
		exit(EXIT_FAILURE);
	}

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

	/* defines */

	fprintf(f, "#include \"config.h\"\n");
	fprintf(f, "\n");
	fprintf(f, "#include <assert.h>\n");
	fprintf(f, "#include <stdio.h>\n");
	fprintf(f, "\n");
	fprintf(f, "#include \"glue.h\"\n");
	fprintf(f, "\n");
	fprintf(f, "#include \"sig_gen_bus.h\"\n");
	fprintf(f, "\n");

	/* Bus functions */
	for (func_n = 0, i = 0; inter[i]; i++) {
		if (inter[i]->n_hit == 0) {
			continue;
		}
		functions[func_n] = get_functions(var[i], inter[i]);
		functions[func_n]->name = funcs->name[i];

		func_n++;
	}

	for (i = 0; i < func_n; i++) {
		write_c_func(f, functions[i]);
	}

	fprintf(f, "void\n");
	fprintf(f, "bus_(connect)(\n");
	fprintf(f, "\tstruct bus *b,\n");
	fprintf(f, "\tvoid *s,\n");
	fprintf(f, "\tconst struct bus_(funcs) *f\n");
	fprintf(f, ")\n");
	fprintf(f, "{\n");
	fprintf(f, "\tassert(b);\n");
	fprintf(f, "\tassert(b->type == %s);\n", funcs->type);
	fprintf(f, "\tassert(b->member_count < sizeof(b->member) / sizeof(b->member[0]));\n");
	fprintf(f, "\n");
	fprintf(f, "\tb->member[b->member_count].s = s;\n");
	fprintf(f, "\tb->member[b->member_count].f = f;\n");
	fprintf(f, "\tb->member_count++;\n");
	fprintf(f, "}\n");
	fprintf(f, "\n");

	fprintf(f, "void\n");
	fprintf(f, "bus_(split)(struct bus_(merge) *m)\n");
	fprintf(f, "{\n");
	fprintf(f, "\tfixme();\n");
	fprintf(f, "}\n");
	fprintf(f, "\n");

	fprintf(f, "struct bus_(merge) *\n");
	fprintf(f, "bus_(merge)(struct %s *s0, struct %s *s1)\n", funcs->type, funcs->type);
	fprintf(f, "{\n");

	fprintf(f, "\tstatic const struct bus_(funcs) s0_funcs = {\n");
	for (i = 0; i < funcs->n; i++) {
		fprintf(f, "\t\t.%s = bus_(%s),\n", funcs->name[i], funcs->name[i]);
	}
	fprintf(f, "\t};\n");
	fprintf(f, "\tstatic const struct bus_(funcs) s1_funcs = {\n");
	for (i = 0; i < funcs->n; i++) {
		fprintf(f, "\t\t.%s = bus_(%s),\n", funcs->name[i], funcs->name[i]);
	}
	fprintf(f, "\t};\n");
	fprintf(f, "\n");

	fprintf(f, "\tstruct bus_(merge) *m;\n");
	fprintf(f, "\n");
	fprintf(f, "\tm = shm_alloc(sizeof(*m));\n");
	fprintf(f, "\tassert(m);\n");
	fprintf(f, "\n");
	fprintf(f, "\tm->0 = s0;\n");
	fprintf(f, "\tbus_(connect)(s0, m, &s0_funcs);\n");
	fprintf(f, "\tm->s1 = s1;\n");
	fprintf(f, "\tbus_(connect)(s1, m, &s1_funcs);\n");
	fprintf(f, "\n");
	fprintf(f, "\treturn m;\n");
	fprintf(f, "\t}\n");
	fprintf(f, "}\n");
	fprintf(f, "\n");

	for (i = 0; i < func_n; i++) {
		write_merge_func(f, functions[i]);
	}

	fprintf(f, "struct bus *\n");
	fprintf(f, "bus_(create)(const char *name)\n");
	fprintf(f, "{\n");
	fprintf(f, "\tstruct bus *b;\n");
	fprintf(f, "\tb = shm_alloc(sizeof(*b));\n");
	fprintf(f, "\tassert(b);\n");
	fprintf(f, "\n");
	fprintf(f, "\tb->type = %s;\n", funcs->type);
	fprintf(f, "\tb->member_count = 0;\n");
	fprintf(f, "\treturn b;\n");
	fprintf(f, "}\n");
	fprintf(f, "\n");

	fprintf(f, "void\n");
	fprintf(f, "bus_(destroy)(struct bus *b)\n");
	fprintf(f, "{\n");
	fprintf(f, "\tassert(b);\n");
	fprintf(f, "\tassert(b->type == %s);\n", funcs->type);
	fprintf(f, "\n");
	fprintf(f, "\tshm_free(b);\n");
	fprintf(f, "}\n");
	fprintf(f, "\n");

	fprintf(f, "void\n");
	fprintf(f, "bus_(suspend)(struct bus *b, FILE *fSig)\n");
	fprintf(f, "{\n");
	fprintf(f, "\tsize_t size = sizeof(*b);\n");
	fprintf(f, "\n");
	fprintf(f, "\tgeneric_suspend(b, size, fSig);\n");
	fprintf(f, "}\n");
	fprintf(f, "\n");

	fprintf(f, "void\n");
	fprintf(f, "bus_(resume)(struct bus *b, FILE *fSig)\n");
	fprintf(f, "{\n");
	fprintf(f, "\tsize_t size = sizeof(*b);\n");
	fprintf(f, "\n");
	fprintf(f, "\tgeneric_resume(b, size, fSig);\n");
	fprintf(f, "}\n");
	fprintf(f, "\n");

	fclose(f);

	for (i = 0; i < func_n; i++) {
		free_function(functions[i]);
	}
}

void
write_h(struct funcs *funcs, const char *filename, struct var **var, struct intersection **inter)
{
	FILE *f;
	char name[strlen(filename) + 3];

	struct func_decl *functions[MAX_FUNCS];
	int func_n;

	int i, j;

	sprintf(name, "%s.h", filename);

	f = fopen(name, "w");
	if (f == NULL) {
		fprintf(stderr, "Can't open output file %s!\n",
			name);
		exit(EXIT_FAILURE);
	}

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

	fprintf(f, "#ifndef __%s_H_included\n", filename);
	fprintf(f, "#define __%s_H_included\n", filename);

	fprintf(f, "#include <inttypes.h>\n");
	fprintf(f, "\n");
	fprintf(f, "/* Should be configurable! FIXME VOSSI */\n");
	fprintf(f, "#define SIG_HOST_BUS_HZ     (60*1000*1000)\n");
	fprintf(f, "\n");
	fprintf(f, "#include \"sig_gen.h\"\n");
	fprintf(f, "\n");


	fprintf(f, "#define bus     %s_bus\n", funcs->type);
	fprintf(f, "#define %s_(x) %s ## x\n", funcs->type, funcs->type);
	fprintf(f, "\n");


	for (func_n = 0, i = 0; inter[i]; i++) {
		if (inter[i]->n_hit == 0) {
			continue;
		}
		functions[func_n] = get_functions(var[i], inter[i]);
		functions[func_n]->name = funcs->name[i];

		func_n++;
	}

	for (i = 0; i < func_n; i++) {
		fprintf(f, "\tvoid (*%s)(", functions[i]->name);
		for (j = 0; j < functions[i]->n_params; j++) {
			fprintf(f, "%s ", functions[i]->params[i].type);
			if (functions[i]->params[i].ts) {
				fprintf(f, "*");
			}
			fprintf(f, "%s", functions[i]->params[i].name);
			if (j < (functions[i]->n_params - 1)) {
				fprintf(f, ", ");
			}
		}
		fprintf(f, ");\n");
	}
	fprintf(f, "}\n");

	fprintf(f, "\n");

	fprintf(f, "struct %s_bus {\n", funcs->type);
	fprintf(f, "\tenum sig_gen_type type;\n");
	fprintf(f, "\tstruct {\n");
	fprintf(f, "\t\tvoid *s;\n");
	fprintf(f, "\t\tconst struct %s_(funcs) *f;\n", funcs->type);
	fprintf(f, "\t} member[32];\n");
	fprintf(f, "\tunsigned int member_count;\n");
	fprintf(f, "};\n");

	fprintf(f, "\n");

	fprintf(f, "struct %s_(merge) {\n", funcs->type);
	fprintf(f, "\tstruct bus *s0;\n");
	fprintf(f, "\tstruct bus *s1;\n");
	fprintf(f, "};\n");

	fprintf(f, "\n");

	for (i = 0; i < func_n; i++) {
		fprintf(f, "extern int\n%s_%s(", funcs->type, functions[i]->name);
		for (j = 0; j < functions[i]->n_params; j++) {
			fprintf(f, "%s ", functions[i]->params[i].type);
			if (functions[i]->params[i].ts) {
				fprintf(f, "*");
			}
			fprintf(f, "%s", functions[i]->params[i].name);
			if (j < (functions[i]->n_params - 1)) {
				fprintf(f, ", ");
			}
		}
		fprintf(f, ");\n\n");
	}

	fprintf(f, "extern void\n");
	fprintf(f, "bus_(connect)(struct bus *b, void *s, const struct %s_(funcs) *f);\n", funcs->type);
	fprintf(f, "\n");
	fprintf(f, "extern struct %s_(merge) *\n", funcs->type);
	fprintf(f, "extern void\n");
	fprintf(f, "bus_(split)(struct %s_(merge) *m);\n", funcs->type);

	fprintf(f, "extern struct bus *\n");
	fprintf(f, "bus_(create)(const char *name);\n");
	fprintf(f, "extern void\n");
	fprintf(f, "bus_(destroy)(struct bus *b);\n");
	fprintf(f, "\n");
	fprintf(f, "extern void\n");
	fprintf(f, "bus_(suspend)(struct bus *b, FILE *fSig);\n");
	fprintf(f, "extern void\n");
	fprintf(f, "bus_(resume)(struct bus *b, FILE *fSig);\n");

	fprintf(f, "#endif /*__%s_H_included */", filename);
	fclose(f);

	for (i = 0; i < func_n; i++) {
		free_function(functions[i]);
	}
}

int
get_all_varp(struct scxml *scxml, struct funcs *funcs, struct var **var, int n_var)
{
	int i, j;
	struct paths *p;

	/* get all possible variability points */
	for (i = 0; i < funcs->n; i++) {
		p = funcs->paths[i];
		for (j = 0; j < p->n; j++) {
			var[n_var] = malloc(sizeof(struct var));
			assert(var[n_var]);

			var[n_var]->func_name = funcs->name[i];
			var[n_var]->paths = funcs->paths[i];
			var[n_var]->n_name = 0;
			var[n_var]->n_varp = 0;

			find_variable_data(scxml, var[n_var]);

			get_variability(scxml, var[n_var]);

			n_var++;

			break;
		}
	}
	return n_var;
}

void
get_var_results(struct superstate *super, struct var_point *varp, int *ret)
{
	int i;
	struct state *tmp;

	for (i = 0; i < super->n_active; i++) {
		tmp = super->active[i];
		if (strcmp(tmp->type, varp->active) == 0) {
			switch (varp->type) {
			case v_if:
				ret[i] = tmp->if_result[varp->count];
				break;
			case v_elif:
				ret[i] = tmp->elif_result[varp->count];
				break;
			case v_trans:
				ret[i] = tmp->trans_result[varp->count];
				break;
			default:
				fprintf(stderr, "unknown var type %i\n", varp->type);
				assert(0);
			}
		} else {
			ret[i] = -1;
		}
	}
}

void
find_all_junctions(struct scxml * scxml, struct funcs *funcs, struct var **var)
{
	int i, j, k, l;
	int tmp;
	struct paths p;
	struct superstate *super;

	for (i = 0; var[i]; i ++) {
		p.n = 0;
		for (j = 0; j < funcs->n; j++) {
			if (strcmp(funcs->name[j], var[i]->func_name) == 0) {
				for (k = 0; k < funcs->paths[j]->n; k++) {
					if (funcs->paths[j]->used[k] == k) {
						//got one
						p.paths[p.n++] = funcs->paths[j]->paths[k];
					}
				}
			}
		}

		for (j = 0; j < var[i]->n_varp; j++) {
			if (var[i]->varp[j].type == v_extern) {
				var[i]->varp[j].junction = 1;
				continue;
			}
			int results[p.n][p.paths[0]->path[0]->n_active];
			for (k = 0; k < p.n; k++) {
				super = var[i]->varp[j].superstate;
				get_var_results(super, &var[i]->varp[j], results[k]);
				tmp = -2;
				//printf("for %s\nresults are:", var[i]->varp[j].name);
				for (l = 0; l < p.paths[k]->path[0]->n_active; l++) {
					if (strcmp(super->active[l]->type, var[i]->varp[j].active)==0) {
						//printf("\t%i(%s)",  results[k][l],  var[i]->varp[j].active);
						if (tmp == -2) {
							tmp = results[k][l];
						} else if (tmp != results[k][l]) {
							/* this var point
							 * is a junction */
							var[i]->varp[j].junction = 1;
							break;
						}
					}
				}
				//printf("\n");
				if (tmp != -2) {
					/* this varp is already listed */
					break;
				}
			}
		}
	}
}

void
find_intersection(struct var **var, struct intersection **inter)
{
	int i, j;
	for (i = 0; var[i]; i++) {
		inter[i] = malloc(sizeof(struct intersection));
		inter[i]->n_hit = 0;
		for (j = 0; j < var[i]->n_varp; j++) {
			if (var[i]->varp[j].junction
			    && strcmp(var[i]->varp[j].active, var[i]->varp[j].orig_active) != 0) {
				/* add new intersection */
				inter[i]->hit[inter[i]->n_hit++] = j;
			}
		}
	}
}

void
get_bytes_inter(struct scxml *scxml, struct var **var, struct intersection **inter)
{
	int i, j;
	int flag;
	int unknown;
	struct scxml_datamodel *model;
	struct scxml_data *data;

	for (i = 0; inter[i]; i++) {
		for (j = 0; j < inter[i]->n_hit; j++) {
			flag = 0;
			unknown = 0;
			for (model = scxml->model_first; model; model = model->next) {
				if (strcmp(model->name, "global") == 0
				    || strcmp(model->name,
					      var[i]->varp[inter[i]->hit[j]].orig_active) == 0) {
					for (data = model->data_first; data; data = data->next) {
						if (strcmp(var[i]->varp[inter[i]->hit[j]].orig,
							   data->id) == 0) {
							if (!data->bytes) {
								/* maybe defined
								 * otherwise*/
								unknown = 1;
								continue;
							}
							inter[i]->bytes[j] = atoi(data->bytes);
							flag = 1;
							break;
						}
					}
				}
				if (flag) {
					continue;
				}
			}
			if (!flag) {
				/* couldnt find bytes for inter[i] */
				if (unknown) {
					fprintf(stderr, "bytes not set for %s\n", var[i]->varp[inter[i]->hit[j]].orig);
				} else {
					fprintf(stderr, "couldnt find %s\n", var[i]->varp[inter[i]->hit[j]].orig);
				}
				assert(0);
			}
		}
	}

}

void
create_code(struct scxml *scxml, struct funcs *funcs, const char *filename)
{
	int i, j;
	struct var *var[MAX_FUNCS] = {NULL};
	struct intersection *inter[MAX_FUNCS] = {NULL};

	/* get all possible variability points */
	get_all_varp(scxml, funcs, var, 0);

	/* find all junction points */
	find_all_junctions(scxml, funcs, var);

	/* DEBUG OUTPUT */
	for (i = 0; var[i]; i++) {
		printf("\n\nlooking at %s\n", funcs->name[i]);
		for (j = 0; j < var[i]->n_name; j++) {
			printf("\tname %s, valid %i, orig %s\n",
			       var[i]->name[j], var[i]->valid[j], var[i]->orig[j]);
		}
		for (j = 0; j < var[i]->n_varp; j++) {
			if (var[i]->varp[j].junction) {
				printf("varp '%s' ",  var[i]->varp[j].name);
				printf("type %i ", var[i]->varp[j].type);
				printf("with orig '%s' ", var[i]->varp[j].orig);
				printf("is a junction point in '%s, ", var[i]->varp[j].active);
				printf("originate from %s'\n", var[i]->varp[j].orig_active);
			} else {
				printf("varp '%s' with orig '%s' read in '%s', originate from %s\n",
				       var[i]->varp[j].name, var[i]->varp[j].orig,
				       var[i]->varp[j].active, var[i]->varp[j].orig_active);
			}
		}
	}

	/* find necessary interactions between master and target */
	find_intersection(var, inter);

	/* get byte length for intersections */
	get_bytes_inter(scxml, var, inter);

	/* DEBUG OUTPUT */
	for (i = 0; inter[i]; i++) {
		printf("looking at %s\n\n", funcs->name[i]);
		for (j = 0; j < inter[i]->n_hit; j++) {
			printf("\tintersection at varp '%s', type %i, with orig '%s' in active '%s' with orig_active '%s'\n"
			       "\t\t with bytes %i, value %i and indirect %i\n\n",
			       var[i]->varp[inter[i]->hit[j]].name,
			       var[i]->varp[j].type,
			       var[i]->varp[inter[i]->hit[j]].orig,
			       var[i]->varp[inter[i]->hit[j]].active,
			       var[i]->varp[inter[i]->hit[j]].orig_active,
			       inter[i]->bytes[j],
			       var[i]->varp[inter[i]->hit[j]].value,
			       var[i]->indirect[inter[i]->hit[j]]);
		}
	}



	/* all informations are gathered */
	write_h(funcs, filename, var, inter);
	write_c(funcs, filename, var, inter);

	/* cleanup */
	for (i = 0; inter[i]; i++) {
		free(inter[i]);
	}

	for (i = 0; var[i]; i++) {
		for (j = 0; j < var[i]->n_varp; j++) {
			free(var[i]->varp[j].name);
		}
		free(var[i]);
	}
}

void
print_path(FILE *out, struct path *p) {

	int i;

	if (p && p->n) {
		for (i = 0; i < p->n; i++) {
			print_super(out, p->path[i]);
		}
	}
	fprintf(out, "\n");
}

void
print_used(FILE *out, struct paths *p) {
	int i;

	for (i = 0; i < p->n; i++) {
		if (p->used[i] == i) {
			fprintf(out, "path %i is used:\n", i);
			print_path(out, p->paths[i]);
		} else if (p->used[i] == -1) {
			fprintf(out, "path %i is invalid\n", i);
		} else {
			fprintf(out, "path %i is equivalent to path %i\n", i, p->used[i]);
		}
	}
	fprintf(out, "\n\n");
}

void
print_used_funcs(FILE *out, struct funcs *funcs) {
	int i;

	fprintf(out, "print used functions\n");
	for (i = 0; i < funcs->n; i++) {
		fprintf(out, "functions name: %s\n", funcs->name[i]);
		print_used(out, funcs->paths[i]);
	}
}

void
print_funcs(FILE *out, struct funcs *funcs) {

	int i, j;

	fprintf(out, "print functions\n");
	for (i = 0; i < funcs->n; i++) {
		fprintf(out, "function name: %s\n", funcs->name[i]);
		fprintf(out, "paths:\n");
		for (j = 0; j < funcs->paths[i]->n; j++) {
			fprintf(out, "path %i\n", j);
			print_path(out, funcs->paths[i]->paths[j]);
			fprintf(out, "\n\n");
		}
		fprintf(out, "\n\n\n");
	}
}

void
dot_reduced_paths(char **paths, int n_paths)
{
	int i, j, k;
	FILE *f;
	char *tmp[MAX_PATH_LEN];
	char dist[MAX_PATH_LEN];

	if (n_paths < 1) {
		return;
	}

	f = fopen(RED_PATH_DOT, "w");
	if (!f) {
		perror("fopen\n");
		exit(EXIT_FAILURE);
	}


	dist[0] = '\0';
	fprintf(f, "digraph {\n");
	for (i = 0; i < n_paths; i++) {


		fprintf(f, "\tsubgraph g_%i {\n", i);
		fprintf(f, "\t\tlabel=\"Path %i\";\n", i);

		for (j = 0, tmp[j] = strtok(paths[i], "\n"); tmp[j]; tmp[++j] = strtok(NULL, "\n")) {
			/* debug ausgaben */
			//if (j)
			//	fprintf(stderr, "\n");
			//fprintf(stderr, "%s", tmp[j]);
		}
		//fprintf(stderr, "\n\n");
		fprintf(f, "\t\t\"init\" -> \"%s%s\" [label=\"%i\"];\n", dist, tmp[1], 0);
		for (k = 1; k < (j - 1); k++) {
			fprintf(f, "\t\t\"%s%s\" -> \"%s%s\" [label=\"%i\"];\n", dist, tmp[k], dist, tmp[k + 1], k);
		}
		//fprintf(f, "\t\t\"%s%s\" -> \"init\" [label=\"%i\"];\n", dist, tmp[k], k);
		fprintf(f, "\t}\n");
		strcat(dist, " ");
	}
	fprintf(f, "}\n");
}

int
reduce_paths(char **paths, int *used, int n_p)
{
	int n, i, j;

	for (i = 0, n = 0; i < n_p; i++) {
		if (!paths[i]) {
			continue;
		}
		printf("looking at path %s\n", paths[i]);
		if (i == 0) {
			/* use path 0 */
			used[i] = 0;
			printf("set to 1\n");
		} else if (i != n) {
			printf("set to i\n");
			/* use path i? */
			used[i] = i;
			paths[n] = paths[i];
		} else {
			printf("set to 0\n");
			used[i] = 0;
		}
		for (j = i + 1; j < n_p; j++) {
			if (paths[j] && strcmp(paths[n], paths[j]) == 0) {
				used[j] = i;
				free(paths[j]);
				paths[j] = NULL;
			}
		}
		n++;
	}

	return n;
}

void
sort_and_reduce_paths(struct paths *paths)
{
	int i, k, l;
	int n_sort, n_red;
	struct superstate *super;

	// to be sorted
	char *sort[MAX_ACTIVE];
	// sorted, to be compared
	char *comp[paths->n];

	char *type;

	for (i = 0; i < paths->n; i++) {
		paths->used[i] = -1;
		if (!paths->paths[i]) {
			comp[i] = NULL;
			continue;
		}
		comp[i] = malloc(MAX_NAME * MAX_ACTIVE + 1);
		comp[i][0] = '\0';

		for (k = 0; k < paths->paths[i]->n - 1; k++) {
			super = paths->paths[i]->path[k];
			for (l = 0; l < super->n_active; l++) {
				sort[l] = malloc(MAX_NAME + 1);
			}
			for (type = NULL, n_sort = 0, l = 0; l < super->n_active; l++) {
				if (type) {
					if (strcmp(type, super->active[l]->type) != 0) {
						/* new type, sort previous ones */
						cat_sorted_strings(sort, n_sort, comp[i]);
						strcat(comp[i], "\\n");
						n_sort = 0;
					}
				}
				type = super->active[l]->type;

				strcpy(sort[n_sort++], super->active[l]->scxml_state->id);
			}
			cat_sorted_strings(sort, n_sort, comp[i]);
			/* separator between steps */
			strcat(comp[i], "\n");
			for (l = 0; l < super->n_active; l++) {
				free(sort[l]);
			}
		}
	}

	n_red = reduce_paths(comp, paths->used, paths->n);

	dot_reduced_paths(comp, n_red);

	for (i = 0; i < n_red; i++) {
		free(comp[i]);
	}
}

void
dot_path(struct paths *paths)
{
	int i, j, k, l;
	FILE *f = fopen(PATH_DOT, "w");
	struct superstate *super;
	const char *tmp_a, *tmp_b;

	if (!f) {
		perror("fopen\n");
		exit(EXIT_FAILURE);
	}

	fprintf(f, "digraph {\n");

	for (i = 0, j = 0; i < paths->n; i++) {
		if (!paths->paths[i]) {
			continue;
		}
		fprintf(f, "\tsubgraph g_%i {\n", j);
		fprintf(f, "\t\tlabel=\"Path %i\";\n", j);

		for (k = 0; k < paths->paths[i]->n - 1; k++) {
			tmp_a = NULL;
			tmp_b = NULL;

			fprintf(f, "\t\t");
			super = paths->paths[i]->path[k];
			fprintf(f, "\"");
			for (l = 0; l < super->n_active; l++) {
				if(tmp_a) {
					if (strcmp(tmp_a, super->active[l]->type) == 0) {
						fprintf(f, ",  ");
					} else {
						fprintf(f, "\\n");
					}
				}
				tmp_a = super->active[l]->type;

				fprintf(f, "%s", super->active[l]->scxml_state->id);
			}
			fprintf(f, "\"");
			fprintf(f, " -> ");
			k++;
			super = paths->paths[i]->path[k];
			fprintf(f, "\"");
			for (l = 0; l < super->n_active; l++) {
				if(tmp_b) {
					if (tmp_b && strcmp(tmp_b, super->active[l]->type) == 0) {
						fprintf(f, ",  ");
					} else {
						fprintf(f, "\\n");
					}
				}
				tmp_b = super->active[l]->type;
				fprintf(f, "%s", super->active[l]->scxml_state->id);
			}
			fprintf(f, "\"");
			fprintf(f, ";\n");
			k--;
		}
		fprintf(f, "\t}\n");
		j++;
	}
	fprintf(f, "}\n");
}

int
evalString(char * expr)  {
	char output[1028] = {0};
	char * op;
	int tmp = 0;
	char part1[512], part2[512];

	if(!shunting_yard(expr, output))
		return 0;  /* oops can't convert to postfix form */

	while (strlen(output) > 1) {
		op = &output[0];
		while (!is_operator(*op) && *op != '\0')
			op++;
		if (*op == '\0') {
			return 0;  /* oops - zero operators found */
		}
		else if (*op == '!') {
			tmp = !(*(op-1) - 48);
			*(op-1) = '\0';
		}
		else if(*op == '&') {
			tmp = (*(op-1) - 48) && (*(op-2) - 48);
			*(op-2) = '\0';
		}
		else if (*op == '|') {
			tmp = (*(op-1) - 48) || (*(op-2) - 48);
			*(op-2) = '\0';
		}

		memset(part1, 0, sizeof(part1));
		memset(part2, 0, sizeof(part2));
		strcpy(part1, output);
		strcpy(part2, op+1);
		memset(output, 0, sizeof(output));
		strcat(output, part1);
		strcat(output, ((tmp==0) ? "0" : "1"));
		strcat(output, part2);
	}
	return *output - 48;
}

void
replace_name(const char *src, char *dest, char *name)
{
	if (strstr(src, "'name'") == NULL) {
		strcpy(dest, src);
	} else {
		int i, j;
		char *hit;
		char location[MAX_NAME];

		dest[0] = '\0';
		strcpy(location, src);

		i = 0;
		while ((hit = strstr(&location[i], "'name'")) != NULL) {
			j = hit - &location[i];
			strncat(dest, &location[i], j);
			strcat(dest, name);
			i = j + 6;
		}
		strcat(dest, &location[i]);
	}
}

int
copy_assign(struct assign_entry *dest, int d_ass, struct assign_entry *src, int n_src)
{
	int i, j;
	for (i = 0, j = d_ass; i < n_src; j++, i++) {
		strcpy(dest[j].name,  src[i].name);
		dest[j].expr = src[i].expr;
		strcpy(dest[j].flags, src[i].flags);
		dest[j].logic = src[i].logic;
	}
	return i;
}

int
copy_assign_by_name(struct named_assign *named, int n_named, struct assign_entry *ass, const char *name)
{
	int ret = 0;
	int i, j, m;
	char tmp[strlen(name) + 1];
	char *names[strlen(name) / 2 + 1];
	char *save;

	strcpy(tmp, name);
	for (m = 0, names[m] = strtok_r(tmp, ",\n", &save); names[m]; names[++m] = strtok_r(NULL, ",\n", &save));

	for (i = 0; i < n_named; i++) {
		for (j = 0; j < m; j++) {
			if (strcmp(named[i].name, names[j]) == 0) {
				ret += copy_assign(ass, ret, named[i].ass, named[i].n_ass);
			}
		}
	}
	return ret;
}

struct values *
copy_value(struct values *src)
{
	int i;
	struct values *dest;
	if (!src) {
		return NULL;
	}
	dest = malloc(sizeof(struct values));

	for (i = 0; i < src->count; i++) {
		dest->name[i] = strdup(src->name[i]);
		if (src->in_name[i]) {
			dest->in_name[i] = strdup(src->in_name[i]);
		} else {
			dest->in_name[i] = NULL;
		}
		dest->value[i] = src->value[i];
	}
	dest->count = src->count;
	return dest;
}

void
copy_values(struct state *src, struct state *dest)
{
	int i;

	assert(src);
	assert(dest);

	for (i = 0; i < MAX_ELEM; i++) {
		dest->if_values[i] = copy_value(src->if_values[i]);
		dest->elif_values[i] = copy_value(src->elif_values[i]);
		dest->trans_values[i] = copy_value(src->trans_values[i]);
	}
}

void
copy_super(struct superstate *src, struct superstate *dest)
{
	int i;

	if (!src) {
		dest = NULL;
		return;
	}

	dest->n_active = src->n_active;
	dest->n_ass = src->n_ass;
	dest->action = src->action;

	copy_assign(dest->ass, 0, src->ass, src->n_ass);

	for (i = 0; i < dest->n_active; i++) {
		dest->active[i] = malloc(sizeof(struct state));
		dest->active[i]->scxml_state = src->active[i]->scxml_state;
		dest->active[i]->n_ass = src->active[i]->n_ass;
		strcpy(dest->active[i]->name, src->active[i]->name);
		strcpy(dest->active[i]->type, src->active[i]->type);
		copy_assign(dest->active[i]->ass, 0, src->active[i]->ass, src->active[i]->n_ass);

		memcpy(dest->active[i]->if_result, src->active[i]->if_result, sizeof(int) * MAX_ELEM);
		memcpy(dest->active[i]->elif_result, src->active[i]->elif_result, sizeof(int) * MAX_ELEM);
		memcpy(dest->active[i]->trans_result, src->active[i]->trans_result, sizeof(int) * MAX_ELEM);

		copy_values(src->active[i], dest->active[i]);
	}
}

void
copy_path(struct path *src, struct path *dest)
{
	int i;

	dest->n = src->n;

	for(i = 0; i < dest->n; i++) {
		dest->path[i] = malloc(sizeof(struct superstate));
		copy_super(src->path[i], dest->path[i]);
	}
}

void
free_value(struct values *tmp) {
	int i;
	if (!tmp) {
		return;
	}
	for (i = 0; i < tmp->count; i++) {
		free(tmp->name[i]);
		if (tmp->in_name[i]) {
			free(tmp->in_name[i]);
		}
	}
	free(tmp);
	tmp = NULL;
}

void
free_super(struct superstate *super)
{
	int i, j;
	for (i = 0; i < super->n_active; i++) {
		for (j = 0; j < MAX_ELEM; j++) {
			free_value(super->active[i]->if_values[j]);
			super->active[i]->if_values[j] = NULL;
			free_value(super->active[i]->elif_values[j]);
			super->active[i]->elif_values[j] = NULL;
			free_value(super->active[i]->trans_values[j]);
			super->active[i]->trans_values[j] = NULL;
		}
		free(super->active[i]);
	}
	free(super);
}

void
free_path(struct path *path)
{
	int i;
	for (i = 0; i < path->n; i++) {
		free_super(path->path[i]);
	}
	free(path);
}

void
free_paths(struct paths *paths)
{
	int i;
	for (i = 0; i < paths->n; i++) {
		if (paths->paths[i]) {
			free_path(paths->paths[i]);
		}
	}
	free(paths);
}

void
free_funcs(struct funcs *funcs)
{
	int i;
	for (i = 0; i < funcs->n; i++) {
		free_paths(funcs->paths[i]);
	}
	free(funcs);
}

int
get_ass_value(struct superstate *super, struct state *tmp, const char *location) {
	int i, n_ass;
	struct assign_entry *ass;
	if (super) {
		ass = super->ass;
		n_ass = super->n_ass;
		for (i = 0; i < n_ass; i++) {
			if (strcmp(ass[i].name, location) == 0) {
				return ass[i].expr;
			}
		}
	}
	if (tmp) {
		ass = tmp->ass;
		n_ass = tmp->n_ass;
		for (i = 0; i < n_ass; i++) {
			if (strcmp(ass[i].name, location) == 0) {
				return ass[i].expr;
			}
		}
	}

	return INT_MIN;
}

int
set_simple_value(struct assign_entry *ass, int n_ass, const char* location, int expr) {
	int i;
	int ret = INT_MIN;

	for (i = 0; i < n_ass; i++) {
		if (strcmp(ass[i].name, location) == 0) {
			/* TODO if this is std_logic resolve the next
			 * value instead of just setting it */
			//if (ass[i].logic) {
			//	fprintf(stderr, "expr is %i", expr);
			//	fprintf(stderr, "ass expr is %i", ass[i].expr);
			//	ass[i].expr = resolve(ass[i].expr, expr);
			//}
			ass[i].expr = expr;
			//fprintf(stderr, "set_ %s to %s\n", location, expr);
			ret = ass[i].expr;
		}
	}
	return ret;
}

int
set_ass_value(struct superstate *super, struct state *tmp, const char* location, int expr) {
	int n_ass;
	struct assign_entry *ass;
	int ret = INT_MIN;
	int r;
	if (super) {
		ass = super->ass;
		n_ass = super->n_ass;
		r = set_simple_value(ass, n_ass, location, expr);
		if (ret == INT_MIN) {
			ret = r;
		}
	}
	if (tmp) {
		ass = tmp->ass;
		n_ass = tmp->n_ass;
		r = set_simple_value(ass, n_ass, location, expr);
		if (ret == INT_MIN) {
			ret = r;
		}
	}
	return ret;
}

int
set_all_ass_value(struct superstate *super, const char* location, int expr) {
	int tmp, i;
	int ret = INT_MIN;

	ret = set_ass_value(super, NULL, location, expr);
	for (i = 0; i < super->n_active; i++) {
		tmp = set_ass_value(NULL, super->active[i], location, expr);
		if (ret == INT_MIN) {
			ret = tmp;
		}
	}
	return ret;
}

int
compare_assigns(struct assign_entry *a, int n_a, struct assign_entry *b, int n_b)
{
	int i;
	if (n_a != n_b) {
		return -1;
	}

	for (i = 0; i < n_a; i++) {
		if ((strstr(a[i].flags, "ign")) != NULL
		    || (strstr(b[i].flags, "ign")) != NULL) {
			continue;
		}
		if (strcmp(a[i].name, b[i].name) != 0) {
			return -1;
		}
		if (a[i].expr != b[i].expr) {
			//fprintf(stderr, "%s changed from %s to %s\n", a[i].name, a[i].expr, b[i].expr);
			return -1;
		}
		if (strcmp(a[i].flags, b[i].flags) != 0) {
			return -1;
		}
	}
	return 0;
}

int
compare_super(struct superstate *a, struct superstate *b)
{
	assert(a);
	assert(b);
	int i;

	if (a->n_active != b->n_active) {
		return -1;
	}

	for (i = 0; i < a->n_active; i++) {
		if (strcmp(a->active[i]->scxml_state->id, b->active[i]->scxml_state->id) != 0) {
			return -1;
		}
		if (strcmp(a->active[i]->name, b->active[i]->name) != 0) {
			return -1;
		}
		if (compare_assigns(a->active[i]->ass, a->active[i]->n_ass, b->active[i]->ass, b->active[i]->n_ass) != 0) {
			return -1;
		}
	}

	return compare_assigns(a->ass, a->n_ass, b->ass, b->n_ass);
}

int
evaluate_cond(const char *cond, int cnt, enum var_type type, struct superstate *super, struct state *tmp)
{
	struct scxml_state *state;
	struct values *values;
	char src[MAX_BOOL];
	char dest[MAX_BOOL];
	char *tokens[MAX_BOOL / 2];
	int val;
	int i, j, k, tag;
	int ret = 1;

	if(!cond) {
		return ret;
	}

	//fprintf(stderr, "cond is %s\n", cond);

	strip_cond(cond, src, tokens);

	values = NULL;

	for (i = 0, j = 0; tokens[i]; i++) {
		if (strcmp(tokens[i], "In") == 0) {
			i += 2;
			assert(strrchr(tokens[i], '\''));
			*strrchr(tokens[i], '\'') = '\0';

			for (k = 0; k < super->n_active; k++) {
				state = super->active[k]->scxml_state;
				if(strcmp(&tokens[i][1], state->id) == 0) {
					dest[j++] = '1';
					break;
				}
			}
			if (k == super->n_active) {
				/* not active */
				dest[j++] = '0';
			}
			i++;
			/*printf("%s\n", tokens[i]); */
		} else if (strcmp(tokens[i], "(") == 0) {
			dest[j++] = '(';
		} else if (strcmp(tokens[i], ")") == 0) {
			dest[j++] = ')';
		} else if (strcmp(tokens[i], "!") == 0) {
			dest[j++] = '!';
		} else if (strcmp(tokens[i], "or") == 0) {
			dest[j++] = ' ';
			dest[j++] = '|';
			dest[j++] = ' ';
		} else if (strcmp(tokens[i], "and") == 0) {
			dest[j++] = ' ';
			dest[j++] = '&';
			dest[j++] = ' ';
		} else if (strchr(tokens[i], '=')) {
			char name[MAX_NAME];
			int indi;
			int new_value;
			char *value;

			if (!values) {
				values = malloc(sizeof(struct values));
				values->count = 0;
			}
			fprintf(stderr, "token is %s\n", tokens[i]);

			/* name==expr */
			tag = strchr(tokens[i], '=') - tokens[i];
			if (!tag) {
				fprintf(stderr, "parsing error, couldnt find == in comparison\n");
				assert(0);
			}
			/* split tokens in name\0value */
			*strchr(tokens[i], '=') = '\0';
			value = &tokens[i][tag + 2];

			/* check for name tag */
			replace_name(tokens[i], name, tmp->name);
			values->name[values->count] = strdup(name);

			/* check for indirection */
			if (value[0] == '$') {
				indi = get_ass_value(super, tmp, &value[1]);
				if(indi == INT_MIN) {
					fprintf(stderr, "couldnt find %s from indirection %s in cond %s\n", &value[1], value, cond);
					assert(0);
				}
				new_value = indi;
				values->in_name[values->count] = strdup(&value[1]);
			} else {
				values->in_name[values->count] = NULL;
				new_value = atoi(value);
			}

			val = get_ass_value(super, tmp, name);
			if (val == INT_MIN) {
				fprintf(stderr, "couldnt find %s from cond %s\n", name, cond);
				assert(0);
			}
			/* TODO if stdlogic, several things evaluate to 0, 1 or
			 * others */
			values->value[values->count] = val;
			values->count++;
			fprintf(stderr, "name is %s value is %i\n", name, val);
			if (new_value == val) {
				dest[j++] = '1';
			} else {
				dest[j++] = '0';
			}
		} else {
			fprintf(stderr, "unknown token %s in cond %s\n", tokens[i], cond);
			assert(0);
		}
	}
	dest[j] = '\0';

	ret = evalString(dest);
	//fprintf(stderr, "ret is %i\n\n", ret);

	switch (type) {
	case v_if:
		tmp->if_result[cnt] = ret;
		tmp->if_values[cnt] = values;
		break;
	case v_elif:
		tmp->elif_result[cnt] = ret;
		tmp->elif_values[cnt] = values;
		break;
	case v_trans:
		tmp->trans_result[cnt] = ret;
		tmp->trans_values[cnt] = values;
		break;
	default:
		fprintf(stderr, "invalid type %i in eval cond\n", type);
	}

	return ret;
}

int
evaluate_indirect_ass(struct superstate *super, struct state *tmp, const char *expression)
{
	int len = strlen(expression);
	char flag = 0;
	char new_name[MAX_NAME];
	char expr[len + 1];
	int val;
	int ret;

	strcpy(expr, expression);

	/* check for ++ or -- */
	if (expr[len - 1] == '-' && expr[len - 2] == '-') {
		flag = '-';
		expr[len - 2] = '\0';
	} else if (expr[len - 1] == '+' && expr[len - 2] == '+') {
		flag = '+';
		expr[len - 2] = '\0';
	}

	replace_name(expr, new_name, tmp->name);

	val = get_ass_value(super, tmp, new_name);
	if (val == INT_MIN) {
		fprintf(stderr, "assignment location %s not found\n", new_name);
		assert(0);
	}

	if (flag) {
		if (flag == '-') {
			val--;
		} else {
			val++;
		}
	}
	ret = set_ass_value(super, tmp, new_name, val);
	if (ret == INT_MIN) {
		fprintf(stderr, "assignment %s not found\n", new_name);
		assert(0);
	}
	return ret;
}

void
perform_assign(struct scxml_assign *assign, struct superstate *super, struct state *tmp)
{
	int val;
	int value;
	char name[MAX_NAME];
	int cnt = 0;
	int ret;

	replace_name(assign->location, name, tmp->name);

	/* set val to new value */
	if (assign->expr[0] == '$') {
		/* indirect assignment */
		val = evaluate_indirect_ass(super, tmp, &assign->expr[1]);
	} else {
		val = get_ass_value(super, tmp, name);
		if (val == INT_MIN) {
			fprintf(stderr, "assignment location %s in state %s not found\n"
				, name, tmp->scxml_state->id);
			assert(0);
		}
		val = atoi(assign->expr);
	}
	ret = set_ass_value(super, tmp, name, val);
	if (ret == INT_MIN) {
		fprintf(stderr, "couldnt set %s to %i, name not found\n", name, val);
		assert(0);
	}

	/* take care of OE flag */
	if (name[cnt] == '&') {
		/* if an OE flag is set for this change value on pin */
		cnt++;
		char loc[strlen(name)];
		sprintf(loc, "*%s", &name[cnt]);

		value = get_ass_value(super, tmp, loc);
		if (value == INT_MIN) {
			fprintf(stderr, "assignment location %s in state %s not found\n",
				loc, tmp->scxml_state->id);
		}
		if (value == 1) {
			ret = set_ass_value(super, tmp, &name[1], val);
			if (ret == INT_MIN) {
				fprintf(stderr, "OE couldnt find %s\n", &name[cnt]);
				assert(0);
			}
		}
	} else if (val == 1 && name[cnt] == '*') {
		/* if OE flag has been set, put intern value on pin */
		cnt++;
		char loc[strlen(name)];
		sprintf(loc, "&%s", &name[cnt]);
		value = get_ass_value(super, tmp, name);
		if (value == INT_MIN) {
			fprintf(stderr, "assignment location %s not found\n", name);
			assert(0);
		}

		ret = set_ass_value(super, tmp, &name[cnt], value);
		if (ret == INT_MIN) {
			fprintf(stderr, "OE couldnt find %s\n", &name[cnt]);
			assert(0);
		}
	}

	if (name[cnt] == '%') {
		/* set value on all possible other ends */
		set_all_ass_value(super, &name[cnt], val);
		cnt++;
	}
}

void
perform_if(struct scxml_if *xif, struct superstate *super, struct state *tmp)
{
	struct scxml_if *subif;
	struct scxml_assign *assign;
	struct scxml_elif *elif;
	struct scxml_else *xelse;

	evaluate_cond(xif->cond, xif->count, v_if, super, tmp);

	if (!tmp->if_result[xif->count]) {
		for (elif = xif->elif_first; elif; elif = elif->next) {
			if (evaluate_cond(elif->cond, elif->count, v_elif, super, tmp)) {
				for (subif = elif->if_first; subif; subif = subif->next) {
					perform_if(subif, super, tmp);
				}
				for (assign = elif->assign_first; assign; assign = assign->next) {
					perform_assign(assign, super, tmp);
				}
				return;
			}
		}
		for (xelse = xif->else_first; xelse; xelse = xelse->next) {
			for (subif = xelse->if_first; subif; subif = subif->next) {
				perform_if(subif, super, tmp);
			}
			for (assign = xelse->assign_first; assign; assign = assign->next) {
				perform_assign(assign, super, tmp);
			}
		}
		return;
	}


	for (assign = xif->assign_first; assign; assign = assign->next) {
		perform_assign(assign, super, tmp);
	}
	for (subif = xif->if_first; subif; subif = subif->next) {
		perform_if(subif, super, tmp);
	}
}

void
perform_onentry(struct superstate *super)
{
	struct scxml_state *state;
	struct scxml_if *xif;
	struct scxml_onentry *onentry;
	struct scxml_assign *assign;
	int i;

	/* apply changes */
	/* onentry */
	for (i = 0; i < super->n_active; i++) {
		state = super->active[i]->scxml_state;
		for (onentry = state->onentry_first; onentry; onentry = onentry->next) {
			for (assign = onentry->assign_first; assign; assign = assign->next) {
				perform_assign(assign, super, super->active[i]);
			}
			for (xif = onentry->if_first; xif; xif = xif->next) {
				perform_if(xif, super, super->active[i]);
			}
		}
	}
}

void
perform_main(struct superstate *super) {
	struct scxml_state *state;
	struct scxml_assign *assign;
	struct scxml_if *xif;
	int i;

	/* main */
	for (i = 0; i < super->n_active; i++) {
		state = super->active[i]->scxml_state;
		for (assign = state->assign_first; assign; assign = assign->next) {
			perform_assign(assign, super, super->active[i]);
		}
		for (xif = state->if_first; xif; xif = xif->next) {
			perform_if(xif, super, super->active[i]);
		}
	}
}

void
perform_onexit(struct superstate *super)
{
	struct scxml_state *state;
	struct scxml_if *xif;
	struct scxml_onexit *onexit;
	struct scxml_assign *assign;
	int i;

	/* onexit */
	for (i = 0; i < super->n_active; i++) {
		state = super->active[i]->scxml_state;
		for (onexit = state->onexit_first; onexit; onexit = onexit->next) {
			for (xif = onexit->if_first; xif; xif = xif->next) {
				perform_if(xif, super, super->active[i]);
			}
			for (assign = onexit->assign_first; assign; assign = assign->next) {
				perform_assign(assign, super, super->active[i]);
			}
		}
	}
}

int
get_next_superstate(struct scxml *scxml, struct superstate *super, struct superstate *next)
{
	struct scxml_state *state;
	struct scxml_trans *trans;

	int i, j, k;

	int found = 0;

	next->n_active = 0;

	k = 0;

	for (i = 0; i < super->n_active; i++) {
		state = super->active[i]->scxml_state;
		for (trans = state->trans_first; trans; trans = trans->next) {
			if (evaluate_cond(trans->cond, trans->count, v_trans, super, super->active[i])) {
				if (!found) {
					found++;
				} else {
					fprintf(stderr, "found more than one possible next state for state %s\n",
						super->active[i]->scxml_state->id);
					fprintf(stderr, "%s and %s\n", trans->target, next->active[i]->scxml_state->id);
				}
				/* set next actives */
				next->active[i] = malloc(sizeof(struct state));

				for (j = 0; j < super->active[i]->n_ass; j++) {
					strcpy(next->active[i]->ass[j].name,  super->active[i]->ass[j].name);
					next->active[i]->ass[j].expr = super->active[i]->ass[j].expr;
					strcpy(next->active[i]->ass[j].flags, super->active[i]->ass[j].flags);
				}
				next->active[i]->n_ass = j;

				memcpy(next->active[i]->if_result, super->active[i]->if_result, sizeof(int) * MAX_ELEM);
				memcpy(next->active[i]->elif_result, super->active[i]->elif_result, sizeof(int) * MAX_ELEM);
				memcpy(next->active[i]->trans_result, super->active[i]->trans_result, sizeof(int) * MAX_ELEM);

				copy_values(super->active[i], next->active[i]);

				next->active[i]->scxml_state = get_state_by_name(scxml, trans->target);
				if (!next->active[i]->scxml_state) {
					fprintf(stderr, "state %s not found\n", trans->target);
					abort();
				}
				strcpy(next->active[i]->name, super->active[i]->name);
				strcpy(next->active[i]->type, super->active[i]->type);
				next->n_active++;
			}
		}
		if (!found) {
			/* easy break condition -> some state has no next */
			fprintf(stderr, "no next for %s\n", state->id);
			return -1;
		}
		found = 0;
	}
	return 0;
}

int
process_flag(struct superstate *super, struct superstate **ret, int n_ret, const char *flag) {
	int i, j;
	int cnt = 0;
	struct pair pair[MAX_DATA];
	char *tmp;
	char new_flags[MAX_FLAG];

	for (i = 0; i < super->n_ass; i++) {
		if (strstr(super->ass[i].flags, flag)) {
			/* remove flag */
			tmp = super->ass[i].flags;
			memmove(new_flags, tmp + strlen(flag),
				1 + strlen(tmp + strlen(flag)));
			strcpy(super->ass[i].flags, new_flags);

			pair[cnt].a = i;
			pair[cnt].b = -1;
			cnt++;
		}
	}
	for (i = 0; i < super->n_active; i++) {
		for (j = 0; j < super->active[i]->n_ass; j++) {
			if (strstr(super->active[i]->ass[j].flags, flag)) {
				/* remove flag */
				tmp = super->active[i]->ass[j].flags;
				memmove(new_flags, tmp + strlen(flag),
					1 + strlen(tmp + strlen(flag)));
				strcpy(super->active[i]->ass[j].flags, new_flags);

				pair[cnt].a = i;
				pair[cnt].b = j;
				cnt++;
			}
		}
	}

	/* set all to 0 once */
	ret[n_ret] = malloc(sizeof(struct superstate));
	copy_super(super, ret[n_ret]);
	for (j = 0; j < cnt; j++) {
		if (pair[j].b < 0) {
			ret[n_ret]->ass[pair[j].a].expr = 0;
		} else {
			ret[n_ret]->active[pair[j].a]->ass[pair[j].b].expr = 0;
		}
	}
	n_ret++;

	/* set each pair to 1 once and start a new path for it */
	for (i = 0; i < cnt; i++) {
		/* create a new path */
		ret[n_ret] = malloc(sizeof(struct superstate));
		copy_super(super, ret[n_ret]);
		for (j = 0; j < cnt; j++) {
			struct pair p = pair[j];
			/* look at pair i and set to 1 if cnt = j */
			if (p.b < 0) {
				if (i == j) {
					ret[n_ret]->ass[p.a].expr = 1;
				} else {
					ret[n_ret]->ass[p.a].expr = 0;
				}
			} else {
				if (i == j) {
					ret[n_ret]->active[p.a]->ass[p.b].expr = 1;
				} else {
					ret[n_ret]->active[p.a]->ass[p.b].expr = 0;
				}
			}
		}
		n_ret++;
	}

	return n_ret;
}

int
check_active_flags(struct superstate *super, struct superstate **ret, int n_ret, int state_n)
{
	int i, j;

	for (i = 0; i < super->active[state_n]->n_ass; i++) {
		for (j = 0; j < super->n_active; j++) {
			if (strstr(super->active[state_n]->ass[i].flags, super->active[j]->scxml_state->id)) {
				/* got one */
				//fprintf(stderr, "need to call func for %s in %s\n", super->active[state_n]->ass[i].name, super->active[j]->scxml_state->id);
				n_ret = process_flag(super, ret, n_ret, super->active[j]->scxml_state->id);
			}
		}
	}
	return n_ret;
}

int
check_flags(struct superstate *super, struct superstate **ret, int n_ret)
{
	int i, j;

	/* check flags global for unknown variables */
	for (i = 0; i < super->n_ass; i++) {
		for (j = 0; j < super->n_active; j++) {
			if (strstr(super->ass[i].flags, super->active[j]->scxml_state->id)) {
				fprintf(stderr, "need to call func for %s in %s\n",
					super->active[j]->ass[i].name, super->active[j]->scxml_state->id);
				n_ret = process_flag(super, ret, n_ret, super->active[j]->scxml_state->id);
			}
			/* find flags in other datamodels */
			n_ret = check_active_flags(super, ret, n_ret, j);
		}
	}
	if (n_ret == 0) {
		/* no flags found -> no paths are created */
		ret[n_ret] = malloc(sizeof(struct superstate));
		copy_super(super, ret[n_ret++]);
	}
	return n_ret;
}

void
dfs_step(struct scxml *scxml, struct superstate *end, struct paths *paths, int act_path)
{
	int i, j, n, tmp, err, act;
	struct superstate *next, *super;
	struct path *tmp_path;
	/* container for possible splits due to unknown variables */
	struct superstate *sub[MAX_PATH];

	tmp_path = paths->paths[act_path];
	if (tmp_path->n == MAX_PATH_LEN) {
		fprintf(stderr, "Maxpathlen too short. loop?\n");
		assert(0);
	}
	n = check_flags(tmp_path->path[tmp_path->n - 1], sub, 0);

	for (tmp = n - 1; tmp >= 0; tmp--) {
		super = sub[tmp];

		if (tmp == 0) {
			act = act_path;
		} else {
			act = paths->n++;
			if (paths->n >= MAX_PATH) {
				fprintf(stderr, "MAX_PATH exceeded (%i)\n", MAX_PATH);
				assert(0);
			}
			paths->paths[act] = malloc(sizeof(struct path));
			copy_path(tmp_path, paths->paths[act]);
		}

		/* resetting condition results */
		for (i = 0; i < super->n_active; i++) {
			memset(super->active[i]->if_result, -1, sizeof(int) * MAX_ELEM);
			memset(super->active[i]->elif_result, -1, sizeof(int) * MAX_ELEM);
			memset(super->active[i]->trans_result, -1, sizeof(int) * MAX_ELEM);
			for (j = 0; j < MAX_ELEM; j++) {
				free_value(super->active[i]->if_values[j]);
				super->active[i]->if_values[j] = NULL;
				free_value(super->active[i]->elif_values[j]);
				super->active[i]->elif_values[j] = NULL;
				free_value(super->active[i]->trans_values[j]);
				super->active[i]->trans_values[j] = NULL;
			}
		}

		perform_onentry(super);
		perform_main(super);

		next = malloc(sizeof(struct superstate));
		assert(next);

		err = get_next_superstate(scxml, super, next);

		perform_onexit(super);

		for (i = 0; i < super->n_ass; i++) {
			strcpy(next->ass[i].name, super->ass[i].name);
			next->ass[i].expr = super->ass[i].expr;
			strcpy(next->ass[i].flags, super->ass[i].flags);
		}
		next->n_ass = super->n_ass;
		next->action = super->action;

		free_super(super);

		if (err == -1) {
			fprintf(stderr, "break due to no next\n");
			fprintf(stderr, "path is:\n");
			for(j = 0; j < paths->paths[act]->n; j++) {
				print_super(stderr, paths->paths[act]->path[j]);
			}
			free_path(paths->paths[act]);
			paths->paths[act] = NULL;
			free_super(next);
			continue;
		}

		/* loop detection */
		for (i = 1; i < paths->paths[act]->n; i++) {
			if (compare_super(paths->paths[act]->path[i], next) == 0) {
				//fprintf(stderr, "\nabbruch weil loop\n");
				//fprintf(stderr, "path is:\n");
				//for(j = 0; j < paths->paths[act]->n; j++) {
				//	print_super(stderr, paths->paths[act]->path[j]);
				//}
				//fprintf(stderr, "\n\n\n");
				free_path(paths->paths[act]);
				paths->paths[act] = NULL;
				free_super(next);
				break;
			}
		}
		if (!paths->paths[act]) {
			continue;
		}

		paths->paths[act]->path[paths->paths[act]->n++] = next;

		/* reached initial state? */
		err = next->n_active;
		if (paths->paths[act]->n >2) {
			for (i = 0; i < next->n_active; i++) {
				if (strcmp(next->active[i]->scxml_state->id, end->active[i]->scxml_state->id) == 0) {
					err--;
				} else {
					break;
				}
			}
			if (err == 0) {
				continue;
			}
		}
		dfs_step(scxml, end, paths, act);
	}
}

void
fix_results(struct paths *paths)
{
	struct path *p;
	struct superstate *super;
	struct superstate *next_super;
	struct state *tmp;
	struct state *next;
	int i, j, k, l;

	for (i = 0; i < paths->n; i++) {
		p = paths->paths[i];
		for (j = 0; p && j < p->n - 1; j++) {
			super = p->path[j];
			next_super = p->path[j + 1];
			for (k = 0; k < super->n_active; k++) {
				tmp = super->active[k];
				next = next_super->active[k];

				memcpy(tmp->if_result, next->if_result, sizeof(int) * MAX_ELEM);
				memcpy(tmp->elif_result, next->elif_result, sizeof(int) * MAX_ELEM);
				memcpy(tmp->trans_result, next->trans_result, sizeof(int) * MAX_ELEM);

				for (l = 0; l < MAX_ELEM; l++) {
					free_value(tmp->if_values[l]);
					tmp->if_values[l] = copy_value(next->if_values[l]);
					free_value(tmp->elif_values[l]);
					tmp->elif_values[l] = copy_value(next->elif_values[l]);
					free_value(tmp->trans_values[l]);
					tmp->trans_values[l] = copy_value(next->trans_values[l]);
				}
			}
		}
	}
}

struct paths *
start_dfs(struct scxml *scxml, struct scxml_function *func, struct named_assign *named, int n_named)
{
	struct scxml_state *state;

	struct superstate *super;
	struct superstate *end = NULL;
	struct paths *paths;
	struct scxml_datamodel *model;
	struct scxml_data *data;

	int i, j, flagc, ret;

	char copy[strlen(scxml->initial)];
	char *initials[strlen(scxml->initial) / 2];
	const char *delim = " \n\t";
	char *b;

	strcpy (copy, scxml->initial);

	super = malloc(sizeof(struct superstate));
	super->action = NULL;

	/* find initial states */
	for (i = 0, initials[i] = strtok(copy, delim); initials[i]; initials[++i] = strtok(NULL, delim)) {
		super->active[i] = malloc(sizeof(struct state));

		b = strchr(initials[i], ':');
		if (!b) {
			fprintf(stderr, "syntax error in initial %s\n", initials[i]);
		}
		*b = '\0';

		/* set states scxml-state */
		state = get_state_by_name(scxml, b + 1);
		if (state) {
			super->active[i]->scxml_state = state;
		} else {
			fprintf(stderr, "initial state '%s' not found\n", b + 1);
			assert(0);
		}

		/* get states model */
		super->active[i]->n_ass = copy_assign_by_name(named, n_named, super->active[i]->ass, initials[i]);

		/* set states type */
		if ((b = strchr(initials[i], ',')) != NULL) {
			if (!b) {
				fprintf(stderr, "syntax error in initials %s\n", initials[i]);
			}
			*b = '\0';
			strcpy(super->active[i]->type, b + 1);

			/* get states name */
			strcpy(super->active[i]->name, initials[i]);
		} else {
			strcpy(super->active[i]->type, initials[i]);
			strcpy(super->active[i]->name, "\0");
		}
		memset(super->active[i]->if_result, -1, sizeof(int) * MAX_ELEM);
		memset(super->active[i]->elif_result, -1, sizeof(int) * MAX_ELEM);
		memset(super->active[i]->trans_result, -1, sizeof(int) * MAX_ELEM);

		for (j = 0; j < MAX_ELEM; j++) {
			super->active[i]->if_values[j] = NULL;
			super->active[i]->elif_values[j] = NULL;
			super->active[i]->trans_values[j] = NULL;
		}
	}
	if (i == 0) {
		fprintf(stderr, "no initial state given\n");
		assert(0);
	}
	super->n_active = i;

	/* get global assignments */
	super->n_ass = copy_assign_by_name(named, n_named, super->ass, "global");

	end = malloc(sizeof(struct superstate));
	copy_super(super, end);

	paths = malloc(sizeof(struct paths));
	for (i = 0; i < MAX_PATH; i++) {
		paths->used[i] = -1;
	}
	paths->n = 0;

	/* check func flags */
	flagc = 0;

	for(model = func->model_first; model; model = model->next) {
		for(data = model->data_first; data; data = data->next) {
			if(data->flags) {
				char cpy[strlen(data->flags) + 2];
				char *token, *save;

				strcpy(cpy, data->flags);
				for(token = strtok_r(cpy, ",", &save); token; token = strtok_r(NULL, ",", &save)) {

					paths->paths[paths->n] = malloc(sizeof(struct path));
					paths->paths[paths->n]->path[0] = malloc(sizeof(struct superstate));
					copy_super(super, paths->paths[paths->n]->path[0]);
					paths->paths[paths->n]->n = 1;

					struct path *p = paths->paths[paths->n];
					ret = set_all_ass_value(p->path[0], data->id, atoi(token));
					if (ret == INT_MIN) {
						fprintf(stderr, "couldnt set %s\n", data->id);
						assert(0);
					}
					paths->n++;
					dfs_step(scxml, end, paths, paths->n-1);
					flagc++;
				}
			}
		}
	}

	if (!flagc) {
		paths->paths[0] = malloc(sizeof(struct path));
		paths->paths[0]->path[0] = super;
		paths->paths[0]->n = 1;
		paths->n++;

		dfs_step(scxml, end, paths, 0);
	} else {
		free_super(super);
	}
	free_super(end);

	fix_results(paths);

	dot_path(paths);
	sort_and_reduce_paths(paths);

	//for (i = 0; i < paths->n; i++) {
	//	if (paths->paths[i]) {
	//	int j;
	//		fprintf(stderr, "path %i:\n", i);
	//		for (j = 0; j < paths->paths[i]->n; j++) {
	//			print_super(stderr, paths->paths[i]->path[j]);
	//		}
	//		fprintf(stderr, "\n");
	//	}
	//}
	//fprintf(stderr, "done\n");

	return paths;
}

const char *
find_action(struct paths *paths) {
	int i, j;
	struct path *tmp;

	const char *ret = NULL;

	for (i = 0; i < paths->n; i++) {
		tmp = paths->paths[i];
		for (j = 0; tmp && j < tmp->n; j++) {
			if (tmp->path[j] && tmp->path[j]->action) {
				if (ret && strcmp(ret, tmp->path[j]->action) != 0) {
					fprintf(stderr, "multiple actions for one function found: %s and %s\n", ret, tmp->path[j]->action);
					assert(0);
				}
				ret = tmp->path[j]->action;
			}
		}
	}
	return ret;
}

struct funcs*
get_funcs(struct scxml *scxml) {
	struct scxml_function *func;
	struct scxml_datamodel *model;
	struct scxml_data *data;
	struct scxml_datamodel *f_model;
	struct scxml_data *f_data;

	struct named_assign named[MAX_STATES];

	struct funcs *funcs = malloc(sizeof(struct funcs));
	funcs->n = 0;
	strcpy(funcs->type, scxml->type);

	int i, j;

	for (func = scxml->func_first; func; func = func->next) {
		if(funcs->n == MAX_FUNCS) {
			fprintf(stderr, "Max funcs exceeded!\n");
			assert(0);
		}
		for (i = 0, named[i].n_ass = 0, model = scxml->model_first; model; i++, model = model->next) {
			strcpy(named[i].name, model->name);
			for(j = 0, data = model->data_first; data; j++, data = data->next) {
				/* check if this is set in function */
				f_data = NULL;
				for(f_model = func->model_first; f_model; f_model = f_model->next) {
					strcpy(named[i].ass[j].name, data->id);
					for(f_data = f_model->data_first; f_data; f_data = f_data->next) {
						if(strcmp(f_data->id, data->id) == 0) {
							/* set to functions value */
							named[i].ass[j].expr = f_data->expr;
							if (f_data->flags) {
								strcpy(named[i].ass[j].flags, f_data->flags);
								//TODO set logic
							} else {
								strcpy(named[i].ass[j].flags, "");
							}
							break;
						}
					}
				}
				/* if not set in function */
				if (!f_data) {
					/* set to default values */
					named[i].ass[j].expr = data->expr;

					if (data->flags) {
						strcpy(named[i].ass[j].flags, data->flags);
					} else {
						strcpy(named[i].ass[j].flags, "");
					}
				}
			}
			named[i].n_ass = j;
		}
		funcs->name[funcs->n] = func->name;

		assert(funcs->name[funcs->n]);

		funcs->paths[funcs->n] = start_dfs(scxml, func, named, i);

		assert(funcs->paths[funcs->n]);
		funcs->action[funcs->n] = find_action(funcs->paths[funcs->n]);

		funcs->n++;
	}
	return funcs;
}

void dot_connections(FILE *f, struct scxml *scxml)
{
	struct scxml_state *state;
	struct scxml_trans *trans;

	for (state = scxml->state_first; state; state = state->next) {
		for (trans = state->trans_first; trans; trans = trans->next) {
			fprintf(f, "\t%s:%s\t->%s:name;\n",
				state->id, trans->target, trans->target);
		}
	}
}

void
dot_assign(FILE *f, const char* allign, struct scxml_assign *assign)
{
	fprintf(f, "\t\t\t<tr><td border=\"0\" align=\"left\"> %s%s = %s</td></tr>\n",
		allign, assign->location, assign->expr);
}

void
dot_trans(FILE *f, const char* allign, struct scxml_trans *trans)
{
	if (trans->cond) {
		fprintf(f, "\t\t\t<tr><td port=\"%s\" border=\"1\" align=\"left\">"
			"if (%s)</td></tr>\n", trans->target, trans->cond);
	} else {
		fprintf(f, "\t\t\t<tr><td port=\"%s\" border=\"1\" align=\"left\">"
			"always </td></tr>\n", trans->target);
	}
}

/* forward declarations */


void dot_if(FILE *, const char*, struct scxml_if *);
void dot_elif(FILE *, const char*, struct scxml_elif *);

void
dot_else(FILE *f, const char* allign, struct scxml_else *xelse)
{
	struct scxml_if *xif;
	struct scxml_assign *assign;

	char new_allign[strlen(allign) + 7];
	sprintf(new_allign, "%s. . . ", allign);

	fprintf(f, "\t\t\t<tr><td border=\"0\" align=\"left\"> %s} else {</td></tr>\n",
		allign);

	for (xif = xelse->if_first; xif; xif = xif->next) {
		dot_if(f, new_allign, xif);
	}
	for (assign = xelse->assign_first; assign; assign = assign->next) {
		dot_assign(f, new_allign, assign);
	}
}

void
dot_elif(FILE *f, const char* allign, struct scxml_elif *elif)
{
	struct scxml_if *xif;
	struct scxml_assign *assign;

	char new_allign[strlen(allign) + 7];
	sprintf(new_allign, "%s. . . ", allign);

	fprintf(f, "\t\t\t<tr><td border=\"0\" align=\"left\"> %s} elseif (%s) {</td></tr>\n",
		allign, elif->cond);

	for(xif = elif->if_first; xif; xif = xif->next) {
		dot_if(f, new_allign, xif);
	}
	for(assign = elif->assign_first; assign; assign = assign->next) {
		dot_assign(f, new_allign, assign);
	}
}

void
dot_if(FILE *f, const char* allign, struct scxml_if *xif)
{
	struct scxml_if *subif;
	struct scxml_elif *elif;
	struct scxml_else *xelse;
	struct scxml_assign *assign;

	char new_allign[strlen(allign) + 7];
	sprintf(new_allign, "%s. . . ", allign);

	fprintf(f, "\t\t\t<tr><td border=\"0\" align=\"left\"> %sif (%s) {</td></tr>\n",
		allign, xif->cond);

	for(assign = xif->assign_first; assign; assign = assign->next) {
		dot_assign(f, new_allign, assign);
	}
	for(subif = xif->if_first; subif; subif = subif->next) {
		dot_if(f, new_allign, subif);
	}

	for(elif = xif->elif_first; elif; elif = elif->next) {
		dot_elif(f, allign, elif);
	}
	for(xelse = xif->else_first; xelse; xelse = xelse->next) {
		dot_else(f, allign, xelse);
	}
	fprintf(f, "\t\t\t<tr><td border=\"0\" align=\"left\"> "
		"%s}</td></tr>\n", allign);
}

void
dot_onexit(FILE *f, const char* allign, struct scxml_onexit *onexit) {
	struct scxml_if *xif;
	struct scxml_assign *assign;

	fprintf(f, "\t\t\t<tr><td border=\"0\" align=\"left\"> { /* onexit */</td></tr>\n");

	for (xif = onexit->if_first; xif; xif = xif->next) {
		dot_if(f, allign, xif);
	}
	for (assign = onexit->assign_first; assign; assign = assign->next) {
		dot_assign(f, allign, assign);
	}
	fprintf(f, "\t\t\t<tr><td border=\"0\" align=\"left\"> ");
	fprintf(f, "} /* /onexit */ </td></tr>\n");

}

void
dot_onentry(FILE *f, const char* allign, struct scxml_onentry *onentry) {
	struct scxml_if *xif;
	struct scxml_assign *assign;

	fprintf(f, "\t\t\t<tr><td border=\"0\" align=\"left\"> { /* onentry */</td></tr>\n");

	for (xif = onentry->if_first; xif; xif = xif->next) {
		dot_if(f, allign, xif);
	}
	for (assign = onentry->assign_first; assign; assign = assign->next) {
		dot_assign(f, allign, assign);
	}
	fprintf(f, "\t\t\t<tr><td border=\"0\" align=\"left\"> ");
	fprintf(f, "} /* /onentry */</td></tr>\n");
}

void
dot_states(FILE *f, struct scxml *scxml)
{
	struct scxml_state *state;
	struct scxml_trans *trans;
	struct scxml_if *xif;
	struct scxml_assign *assign;
	struct scxml_onentry *onentry;
	struct scxml_onexit *onexit;

	for(state = scxml->state_first; state; state = state->next) {
		/* start state */
		fprintf(f, "\t%s\n\t[\n", state->id);
		fprintf(f, "\t\tshape = none\n");
		fprintf(f, "\t\tlabel = <<table border=\"1\" cellspacing=\"0\">\n");
		fprintf(f, "\t\t\t<tr><td port=\"name\" border=\"1\""
			" bgcolor=\"lightgoldenrodyellow\">"
			"%s</td></tr>\n", state->id);
		for(onentry = state->onentry_first; onentry; onentry = onentry->next) {
			dot_onentry(f, "", onentry);
		}
		for(assign = state->assign_first; assign; assign = assign->next) {
			dot_assign(f, "", assign);
		}
		for(xif = state->if_first; xif; xif = xif->next) {
			dot_if(f, "", xif);
		}
		for(onexit = state->onexit_first; onexit; onexit = onexit->next) {
			dot_onexit(f, "", onexit);
		}
		for(trans = state->trans_first; trans; trans = trans->next) {
			dot_trans(f, "", trans);
		}
		/* close state */
		fprintf(f, "\t\t</table>>\n");
		fprintf(f, "\t]\n");
	}
	fprintf(f, "\n");
}

void
dot_data(FILE *f, struct scxml *scxml)
{

	struct scxml_datamodel *model;
	struct scxml_data *data;

	for(model = scxml->model_first; model; model = model->next) {
		fprintf(f, "\tports\n\t[\n");
		fprintf(f, "\t\tshape = none\n");
		fprintf(f, "\t\tlabel = <<table border=\"1\" "
			"align=\"left\" cellspacing=\"0\">\n");
		fprintf(f, "\t\t\t<tr><td port=\"ports\" border=\"1\""
			" bgcolor=\"aliceblue\"> ports</td></tr>\n");
		fprintf(f, "\t\t\t<tr><td port=\"ports\" border=\"1\"> ");
		for(data = model->data_first; data; data = data->next) {
			fprintf(f, "%s<br />", data->id);
		}
		fprintf(f, "</td></tr>\n");
		fprintf(f, "\t\t</table>>\n");
		fprintf(f, "\t]\n");
	}
	fprintf(f, "\n");
}

void
dot_scxml(char *name, struct scxml *scxml)
{

	FILE *f = fopen("dotprint.dot", "w");
	if (!f) {
		perror("fopen\n");
		exit(EXIT_FAILURE);
	}

	fprintf(f, "digraph test\n{\n");
	fprintf(f, "\tlabel=\"%s\"\n", name);
	fprintf(f, "\tnode [shape=record,width=.1,height=.1];\n");
	fprintf(f, "\tnodesep=1;\n");
	fprintf(f, "\trankdir=LR;\n\n");

	dot_data(f, scxml);

	dot_states(f, scxml);

	dot_connections(f, scxml);

	fprintf(f, "}\n");

}

void
pretty_trans(FILE *f, const char* allign, struct scxml_trans *trans)
{
	fprintf(f, "%s<transition event=\"%s\"\n%s\tcond=\"%s\"\n%s\ttarget=\"%s\"/>\n",
		allign, trans->event, allign, trans->cond, allign, trans->target);
}

void
pretty_assign(FILE *f, const char* allign, struct scxml_assign *assign)
{
	fprintf(f, "%s<assign location=\"%s\" expr=\"%s\"/>\n"
		, allign, assign->location, assign->expr);
}

/* forward declaration */
void pretty_if(FILE *, const char *, struct scxml_if *);
void pretty_else(FILE *, const char *, struct scxml_else *);

void
pretty_elif(FILE *f, const char* allign, struct scxml_elif *elif)
{
	struct scxml_if *xif;
	struct scxml_assign *assign;

	char new_allign[strlen(allign) + 2];
	sprintf(new_allign, "%s\t", allign);

	fprintf(f, "%s<elseif cond=\"%s\">\n", allign, elif->cond);
	for(xif = elif->if_first; xif; xif = xif->next) {
		pretty_if(f, new_allign, xif);
	}
	for(assign = elif->assign_first; assign; assign = assign->next) {
		pretty_assign(f, new_allign, assign);
	}
	fprintf(f, "%s</elseif>\n", allign);
}

void
pretty_else(FILE *f, const char* allign, struct scxml_else *xelse)
{
	struct scxml_if *xif;
	struct scxml_assign *assign;

	char new_allign[strlen(allign) + 2];
	sprintf(new_allign, "%s\t", allign);

	fprintf(f, "%s<else>\n", allign);
	for(xif = xelse->if_first; xif; xif = xif->next) {
		pretty_if(f, new_allign, xif);
	}
	for(assign = xelse->assign_first; assign; assign = assign->next) {
		pretty_assign(f, new_allign, assign);
	}
	fprintf(f, "%s</else>\n", allign);

}

void
pretty_if(FILE *f, const char* allign, struct scxml_if *xif)
{
	struct scxml_if *subif;
	struct scxml_elif *elif;
	struct scxml_assign *assign;

	char new_allign[strlen(allign) + 2];
	sprintf(new_allign, "%s\t", allign);

	fprintf(f, "%s<if cond=\"%s\">\n", allign, xif->cond);
	for(subif = xif->if_first; subif; subif = subif->next) {
		pretty_if(f, allign, subif);
	}
	for(elif = xif->elif_first; elif; elif = elif->next) {
		pretty_elif(f, new_allign, elif);
	}
	if(xif->else_first) {
		pretty_else(f, new_allign, xif->else_first);
	}
	for(assign = xif->assign_first; assign; assign = assign->next) {
		pretty_assign(f, new_allign, assign);
	}
	fprintf(f, "%s</if>\n", allign);
}

void
pretty_onexit(FILE *f, const char* allign, struct scxml_onexit *onexit)
{
	struct scxml_if *xif;
	struct scxml_assign *assign;

	char new_allign[strlen(allign) + 2];
	sprintf(new_allign, "%s\t", allign);

	fprintf(f, "%s<onexit>\n", allign);
	for(xif = onexit->if_first; xif; xif = xif->next) {
		pretty_if(f, new_allign, xif);
	}
	for(assign = onexit->assign_first; assign; assign = assign->next) {
		pretty_assign(f, new_allign, assign);
	}
	fprintf(f, "%s</onexit>\n", allign);

}

void
pretty_onentry(FILE *f, const char* allign, struct scxml_onentry *onentry)
{
	struct scxml_if *xif;
	struct scxml_assign *assign;

	char new_allign[strlen(allign) + 2];
	sprintf(new_allign, "%s\t", allign);

	fprintf(f, "%s<onentry>\n", allign);
	for(xif = onentry->if_first; xif; xif = xif->next) {
		pretty_if(f, new_allign, xif);
	}
	for(assign = onentry->assign_first; assign; assign = assign->next) {
		pretty_assign(f, new_allign, assign);
	}
	fprintf(f, "%s</onentry>\n", allign);

}

void
pretty_print(struct scxml *scxml)
{
	struct scxml_datamodel *model;
	struct scxml_data *data;
	struct scxml_state *state;
	struct scxml_trans *trans;
	struct scxml_if *xif;
	struct scxml_assign *assign;
	struct scxml_onentry *onentry;
	struct scxml_onexit *onexit;

	FILE *f = fopen("prettyprint.scxml", "w");

	fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
	fprintf(f, "<%s>\n", scxml->name);

	for(model = scxml->model_first; model; model = model->next) {
		fprintf(f, "\t<datamodel>\n");
		for(data = model->data_first; data; data = data->next) {
			fprintf(f, "\t\t<data id=\"%s\" expr=\"%i\"/>\n", data->id, data->expr);
		}
		fprintf(f, "\t</datamodel>\n");
	}
	fprintf(f, "\n");

	for(state = scxml->state_first; state; state = state->next) {
		fprintf(f, "\t<state id=\"%s\">\n", state->id);
		for(onentry = state->onentry_first; onentry; onentry = onentry->next) {
			pretty_onentry(f, "\t\t", onentry);
		}
		for(assign = state->assign_first; assign; assign = assign->next) {
			pretty_assign(f, "\t\t", assign);
		}
		for(xif = state->if_first; xif; xif = xif->next) {
			pretty_if(f, "\t\t", xif);
		}
		for(trans = state->trans_first; trans; trans = trans->next) {
			pretty_trans(f, "\t\t", trans);
		}
		for(onexit = state->onexit_first; onexit; onexit = onexit->next) {
			pretty_onexit(f, "\t\t", onexit);
		}
		fprintf(f, "\t</state>\n");
	}
	fprintf(f, "</%s>\n", scxml->name);
}

int
main(int argc, char **argv)
{
	char *inname;
	char* progname = *argv;
	const char *filename = FILENAME;
	int opt_o = 0;
	char output[MAX_NAME];

	FILE *fp;
	struct xml *xml;
	struct scxml *scxml;

	struct funcs *funcs;

	int c;

	argv++;
	argc--;

	/*
	 * Get options.
	 */
	c = getopt(argc, argv, "o:");
	if (c != -1) {
		if (c == 'o') {
			strncpy(output, optarg, MAX_NAME);
			opt_o = 1;
		} else {
			usage(1, progname);
		}
	} else {
		strcpy(output, ".");
	}
	if (argc == 1 ||
	    (argc == 3 && opt_o) ) {
		inname = argv[0];
	} else {
		usage(1, progname);
	}

	if (! strchr(inname, '.')) {
		usage(1, progname);
	}

	if (strchr(inname, '/')) {
		strcat(output, strrchr(inname, '/'));
	} else {
		strcat(output, "/");
		strcat(output, inname);
	}
	*strrchr(output, '.') = '\0';

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

	xml = xml_parse(fp);

	scxml = scxml_read(xml);

	fclose(fp);

	//pretty_print(scxml);

	//dot_scxml(output, scxml);

	funcs = get_funcs(scxml);

	//print_used_funcs(stdout, funcs);

	//print_funcs(stdout, funcs);

	create_code(scxml, funcs, filename);

	free_funcs(funcs);

	scxml_free(scxml);

	xml_free(xml);

	return 0;
}
