#include <string.h>
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/glocale.h>
#include "rule.h"

static int scan_value(CELL *);
static const char *cur;
static int state;
int default_rule = 0;
int default_to_itself = 0;
char *default_label;
CELL DEFAULT;

int parse(const char *line, RULE ** rules, RULE ** tail, struct Categories *cats)
{
    const char *label;
    const char *save;
    CELL v;
    CELL lo[100], hi[100], new = (CELL) 0;
    int count;
    int i, last_null = 0;

    cur = line;
    state = 0;
    count = 0;
    label = "";

    while (*cur == ' ' || *cur == '\t' || *cur == '\n')
	cur++;
    while (*cur) {
	while (*cur == ' ' || *cur == '\t' || *cur == '\n')
	    cur++;
	if (!*cur)
	    break;

	switch (state) {
	case 0:
	    save = cur;
	    if (!strncmp(cur, "help", 4)) {	/* help text */
		fprintf(stdout, _("Enter a rule in one of these formats:\n"));
		fprintf(stdout, "1 3 5      = 1   %s\n", _("poor quality"));
		fprintf(stdout, "1 thru 10  = 1\n");
		fprintf(stdout, "20 thru 50 = 2   %s\n", _("medium quality"));
		fprintf(stdout, "*          = NULL\n");
		state = 0;
		cur += 4;
		continue;
	    }
	    if (*cur == '*') {	/* default rule */
		default_rule = 1;
		state = 1;
		cur++;
		continue;
	    }
	    if (!scan_value(&v))
		return -1;
	    if (Rast_is_c_null_value(&v)) {
		G_warning(_("Can't have null on the left-hand side of the rule"));
		return -1;
	    }
	    state = 1;
	    cur = save;
	    continue;
	case 1:
	    if (*cur == '=') {
		cur++;
		state = 4;
		continue;
	    }
	    if (default_rule)
		return -1;
	    if (!scan_value(&v))
		return -1;
	    if (Rast_is_c_null_value(&v))
		last_null = 1;
	    else
		last_null = 0;
	    lo[count] = hi[count] = v;
	    count++;
	    state = 2;
	    continue;
	case 2:
	    state = 1;
	    if (strncmp(cur, "thru", 4) != 0)
		continue;
	    if (last_null) {
		G_warning(_("Can't have null on the right-hand side of the rule"));
		return -1;
	    }
	    cur += 4;
	    if (*cur != ' ' && *cur != '\t')
		return -1;
	    state = 3;
	    continue;
	case 3:
	    if (!scan_value(&v))
		return -1;
	    if (Rast_is_c_null_value(&v)) {
		G_warning(_("Can't have null on the right-hand side of the rule"));
		return -1;
	    }

	    if (lo[count - 1] > v) {
		hi[count - 1] = lo[count - 1];
		lo[count - 1] = v;
	    }
	    else
		hi[count - 1] = v;

	    state = 1;
	    continue;
	case 4:
	    if (*cur == '*' && default_rule) {
		cur++;
		new = 0;
		default_to_itself = 1;
		state = 5;
		continue;
	    }

	    if (!scan_value(&v))
		return -1;
	    new = v;
	    state = 5;
	    continue;
	case 5:
	    label = cur;
	    cur = "";		/* force break from while */
	}
    }
    if (state > 0 && state < 5)
	return -1;
    if (default_rule) {
	DEFAULT = new;
	default_label = G_store((*label ? label : ""));
	return 1;
    }

    for (i = 0; i < count; i++) {
	add_rule(tail, lo[i], hi[i], new);
	if (*rules == NULL)
	    *rules = *tail;
	if (*label)
	    Rast_set_c_cat(&new, &new, label, cats);
    }
    return count;
}

static int scan_value(CELL * v)
{
    int i, sign, dec;
    double fv, fd;

    if (strncmp(cur, "null", 4) == 0 || strncmp(cur, "NULL", 4) == 0) {
	cur += 4;
	Rast_set_c_null_value(v, 1);
    }
    else {
	sign = 1;
	if (*cur == '-') {
	    sign = -1;
	    cur++;
	}
	/*
	   if (*cur < '0' || *cur > '9')
	   return 0;
	   *v = *cur++ - '0' ;
	 */
	dec = 0;
	fv = 0.0;

	while ((*cur >= '0' && *cur <= '9') || *cur == '.') {
	    if (*cur == '.') {
		if (!dec)
		    dec++;
		cur++;
		continue;
	    }
	    if (!dec)
		fv = fv * 10 + *cur++ - '0';
	    else {
		fd = 1.0;
		for (i = 0; i < dec; i++)
		    fd *= 0.1;
		dec++;
		fv += (*cur++ - '0') * fd;
	    }
	}

	if (dec)
	    fv += 0.5;
	*v = sign * (CELL) fv;

	if (dec && state)
	    fprintf(stdout, _("%f rounded up to %d\n"), sign * fv, *v);
    }

    switch (*cur) {
    case 0:
    case ' ':
    case '\t':
    case '\n':
    case '=':
	return 1;
    default:
	return 0;
    }
}
