#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "foma.h"

extern int g_verbose;
struct defined_networks   *g_defines;
struct defined_functions  *g_defines_f;

/* Find a defined symbol from the symbol table */
/* Return the corresponding FSM                */
struct fsm *find_defined(struct defined_networks *def, char *string) {
    struct defined_networks *d;
    for (d = def; d != NULL; d = d->next) {
	if (d->name != NULL && strcmp(string, d->name) == 0) {
	    return(d->net);
	}
    }
    return NULL;
}

struct defined_networks *defined_networks_init(void) {
    struct defined_networks *def;
    def = calloc(1, sizeof(struct defined_networks)); /* Dummy first entry, so we can maintain the ptr */
    return def;
}

struct defined_functions *defined_functions_init(void) {
    struct defined_functions *deff;
    deff = calloc(1, sizeof(struct defined_functions)); /* Dummy first entry */
    return deff;
}

/* Removes a defined network from the list                 */
/* Returns 0 on success, 1 if the definition did not exist */
/* Undefines all if NULL is passed as the string argument  */

int remove_defined(struct defined_networks *def, char *string) {
    struct defined_networks *d, *d_prev, *d_next;
    int exists = 0;
    /* Undefine all */
    if (string == NULL) {
	for (d = def; d != NULL; d = d_next) {
	    d_next = d->next;
	    if (d->net != NULL)
	        fsm_destroy(d->net);
	    if (d->name != NULL)
	        free(d->name);
	}
	return 0;
    }
    d_prev = NULL;
    for (d = def; d != NULL; d_prev = d, d = d->next) {
	if (d->name != NULL && strcmp(d->name, string) == 0) {
	    exists = 1;
	    break;
	}
    }
    if (exists == 0) {
	return 1;
    }
    if (d == def) {
	if (d->next != NULL) {
	    fsm_destroy(d->net);
	    free(d->name);
	    d->name = d->next->name;
	    d->net = d->next->net;
	    d_next = d->next->next;
	    free(d->next);
	    d->next = d_next;
	} else {
	    fsm_destroy(d->net);
	    free(d->name);
	    d->next = NULL;
	    d->name = NULL;
	    d->net = NULL;
	}
    } else {
	fsm_destroy(d->net);
	free(d->name);
	d_prev->next = d->next;
	free(d);
    }
    return 0;
}

/* Finds defined regex "function" based on name, numargs */
/* Returns the corresponding regex                       */
char *find_defined_function(struct defined_functions *deff, char *name, int numargs) {
    struct defined_functions *d;
    for (d = deff ; d != NULL; d = d->next) {
        if (d->name != NULL && strcmp(d->name, name) == 0 && d->numargs == numargs) {
            return(d->regex);
        }
    }
    return NULL;
}

/* Add a function to list of defined functions */
int add_defined_function(struct defined_functions *deff, char *name, char *regex, int numargs) {
    struct defined_functions *d;
    for (d = deff; d != NULL; d = d->next) {
	if (d->name != NULL && strcmp(d->name, name) == 0 && d->numargs == numargs) {
	    free(d->regex);
	    d->regex = strdup(regex);
            if (g_verbose)
            {
                fprintf(stderr,"redefined %s@%i)\n", name, numargs);
                fflush(stderr);
            }
	    return 1;
	}
    }
    if (deff->name == NULL) {
	d = deff;
    } else {
	d = malloc(sizeof(struct defined_functions));
	d->next = deff->next;
	deff->next = d;
    }
    d->name = strdup(name);
    d->regex = strdup(regex);
    d->numargs = numargs;
    return 0;
}

/* Add a network to list of defined networks */
/* Returns 0 on success or 1 on redefinition or -1 if name is too long */
/* Always maintain head of list at same ptr */

int add_defined(struct defined_networks *def, struct fsm *net, char *string) {
    struct defined_networks *d;
    if (net == NULL)
	return 0;
    if (strlen(string) > FSM_NAME_LEN) {
      return(-1);
    }

    fsm_count(net);
    for (d = def; d != NULL; d = d->next) {
	if (d->name != NULL && strcmp(d->name, string) == 0) {
	    fsm_destroy(d->net);
	    free(d->name);
	    d->net = net;
	    d->name = strdup(string);
	    return 1;
	}
    }
    if (def->name == NULL) {
	d = def;
    } else {
	d = malloc(sizeof(struct defined_networks));
	d->next = def->next;
	def->next = d;
    }
    d->name = strdup(string);
    d->net = net;
    return 0;
}
