/* pred.c -- routines that deals with predicates */
#include "he.h"

HE_PRED *he_predicates;

/* resetPred -- for setting the he_predicates to point to a pred
   of only group, this is the default when a file is opened */
int resetPred()
{
    if (he_predicates != NULL)
	free(he_predicates);

    he_predicates = (HE_PRED *) calloc(2, sizeof(HE_PRED));
    he_predicates[0].key = HEK_GROUP;
    he_predicates[1].key = 0;

    return HE_OK;
}

struct {
    char *str;
    int key;
} he_keyTab[] =
{
{"!=",		HEK_NEQUAL | HE_COMPARATOR},
{"<",		HEK_LESST | HE_COMPARATOR},
{"<=",		HEK_LEQUAL | HE_COMPARATOR},
{"==",		HEK_EQUAL | HE_COMPARATOR},
{">",		HEK_GRT | HE_COMPARATOR},
{">=",		HEK_GEQUAL | HE_COMPARATOR},
{"all",		HEK_ALL | HE_PREDICATE},
{"any",		HEK_ALL | HE_PREDICATE},
{"ref",		HEK_REF | HE_PREDICATE},
{"succeed",	HEK_SUCCEED | HE_PREDICATE},
{"fail",	HEK_FAIL | HE_PREDICATE},
{"tag",		HEK_TAG | HE_PREDICATE},
{"group",	HEK_GROUP | HE_PREDICATE},
/* Finish this later */
{NULL, 0},			/* Marker */
};

int findKey(word)
    char *word;
{
    register int i;
    int len;
    int found = -1;

    len = strlen(word);

    for (i = 0; he_keyTab[i].str; i++)
	if (!strncmp(he_keyTab[i].str, word, len))
	{
	    /* if this is an exact match, just return */
	    if (strlen(he_keyTab[i].str) == len)
		return he_keyTab[i].key;
	    if (found < 0)
		found = i;
	    else
	    {
		fprintf(stderr, "Ambiguous: %s.\n", word);
		return HE_NOTFOUND;
	    }
	}
    if (found < 0)
    {
	fprintf(stderr, "Predicate/comparator: %s not found.\n", word);
	return HE_NOTFOUND;
    }
    return he_keyTab[found].key;
}

int isNumber(s)
    register char *s;
{
    for (; *s; s++)
	if (!isdigit((int) *s)) return NO;

    return YES;
}

#define HE_PRED_SZ HE_ARG_SZ

HE_PRED *parsePred(argc, argv)
    int argc;
    char *argv[];
{
    HE_PRED *pred;
    int predNum = -1;
    int state = 0;
    int key;
    register int i;
    char *s;
    char *tok;

    pred = (HE_PRED *) calloc(HE_PRED_SZ, sizeof(HE_PRED));

    for ( i = 1; i < argc; i++)
    {
	s = argv[i];
	while (*s)
	{
	    if (state != 2) 
		tok = nextToken(&s);
	    else
		tok = nextWord(&s);
	    if (!tok) break;

	    if (state != 2)
	    {
		if ((key = findKey(tok)) == HE_NOTFOUND)
		{
		    free(pred);
		    return NULL;
		}
		free(tok);
	    }

	    switch(state)
	    {
	    case 0:
		/* Ready to accept a predicate */
		if (!(key & HE_PREDICATE))
		{
		    fprintf(stderr, "Parse error: %s.\n", argv[i]);
		    free(pred);
		    return NULL;
		}
		pred[++predNum].key = key & ~(HE_PREDICATE | HE_COMPARATOR);
		state = 1;
		break;

	    case 1:
		/* Can be a comparator for previous pred or a new predicate */
		if (key & HE_PREDICATE)
		{
		    pred[++predNum].key =
			key & ~(HE_PREDICATE | HE_COMPARATOR);

		}
		else if (key & HE_COMPARATOR)
		{
		    pred[predNum].comp =
			key & ~(HE_PREDICATE | HE_COMPARATOR);
		    state = 2;
		}
		else
		{
		    fprintf(stderr, "Parse error: %s.\n", argv[i]);
		    free(pred);
		    return NULL;
		}
		break;

	    case 2:
		/* Looking for an argument */
		if (isNumber(tok))
		{
		    pred[predNum].argType = HE_NUMBER;
		    pred[predNum].arg.i = atoi(tok);
		}
		else
		{
		    pred[predNum].argType = HE_STRING;
		    pred[predNum].arg.str = copyStr(tok);
		}
		state = 0;
		break;

	    default:
		NOT_REACHED();
		break;
	    }
	}
    }
    pred[++predNum].key = 0;

    return pred;
}

int satPred(desc, pred)
    DFdesc *desc;
    HE_PRED pred[];
{
    int i;

    if (!pred) return YES;
    for (i = 0; pred[i].key; i++)
    {
	switch(pred[i].key)
	{
	case HEK_ALL:
	    return YES;		/* Always satisfied */
	case HEK_GROUP:
	    if (!isGrp(desc->tag))
		return NO;
	    break;
	case HEK_TAG:
	    if (pred[i].argType != HE_NUMBER)
	    {
		fprintf(stderr, "Argument to tag predicate not a number.");
		return NO;
	    }
	    if (!numCompare((int)desc->tag, pred[i].comp, pred[i].arg.i))
		return NO;
	    break;
	case HEK_REF:
	    if (pred[i].argType != HE_NUMBER)
	    {
		fprintf(stderr, "Argument to ref predicate not a number.");
		return NO;
	    }
	    if (!numCompare((int)desc->ref, pred[i].comp, pred[i].arg.i))
		return NO;
	    break;
	case HEK_SUCCEED:
	    return (he_status == 0);
	case HEK_FAIL:
	    return (he_status != 0);
	default:
	    NOT_REACHED();
	    break;
	}
    }

    return 1;
}

char *nextToken(p)
    char **p;
{
    register char *s, *q;
    char *tok;

    if (!(**p)) return NULL;

    s = *p;

    if (isalnum(**p))
	while (isalnum(*s)) s++;
    else
	while (*s && !isalnum(*s)) s++;

    q = tok = (char *) malloc((s-(*p)) + 1);
    while (*p != s) *q++ = *(*p)++;
    *q = '\0';

    return tok;
}
    
int numCompare(n1, comp, n2)
    int n1, comp, n2;
{
    switch(comp)
    {
    case HEK_EQUAL:
	return (n1 == n2);

    case HEK_NEQUAL:
	return (n1 != n2);

    case HEK_GRT:
	return (n1 > n2);

    case HEK_GEQUAL:
	return (n1 >= n2);

    case HEK_LESST:
	return (n1 < n2);

    case HEK_LEQUAL:
	return (n1 <= n2);

    default:
	NOT_REACHED();
	break;
    }
    return NO;
}

/* end of pred.c */
