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

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

int parse (char *line,RULE **rules,RULE **tail,struct Categories *cats)
{
    char *label;
    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   poor quality\n");
                fprintf (stdout, "1 thru 10  = 1\n");
                fprintf (stdout, "20 thru 50 = 2   medium quality\n");
                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(G_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(G_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(G_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)
	    G_set_cat (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;
       G_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;
    }
}

