#include <stdio.h>
#include <string.h>
#include "defines.h"
#include "struct.h"
#include "globals.h"

static char *white_space = " \t\n\r";
typedef int token_t;
static float num;

#define MODE_TOPLEVEL	0
#define MODE_ROCKLIST	1

/* The framework for this lexer started from the gettoken routine provided
   in Marc Rochkind's Advanced UNIX Programming.  I really really like that
   book.  If you don't have it, you might think about buying it.  Thanks
   to Jason Lowdermilk for lending me his copy many years ago until I could
   buy my own (a few months ago!) - There are still limitations that 
   I need to fix in this - like keeping track of line numbers and simple
   things like that.  I'm really too lazy to add it now
   - Aaron Michael Hightower */

char *tok_string(TOKEN tok)
{
  switch(tok) {
    case T_AUTHOR: return "AUTHOR ";
    case T_BONUS: return "BONUS ";
    case T_CHILD: return "CHILD ";
    case T_COMMA: return ", ";
    case T_COUNT: return "COUNT ";
    case T_DISTPOSITION: return "DISTPOSITION ";
    case T_EOF: return "EOF ";
    case T_EQUAL: return "= ";
    case T_EXPLCOLOR: return "EXPLCOLOR ";
    case T_FRAMEDELAY: return "FRAMEDELAY ";
    case T_GEOMETRY: return "GEOMETRY ";
    case T_LBRACK: return "{ ";
    case T_LEVEL: return "LEVEL ";
    case T_LEVELSIZE: return "LEVELSIZE ";
    case T_LIFESPAN: return "LIFESPAN ";
    case T_MAXLIFESPAN: return "MAXLIFESPAN ";
    case T_MAXSHOTS: return "MAXSHOTS ";
    case T_MAXSPEED: return "MAXSPEED ";
    case T_MINLIFESPAN: return "MINLIFESPAN ";
    case T_MINSPEED: return "MINSPEED ";
    case T_NAME: return "NAME ";
    case T_NL: return "\n";
    case T_NUM: return "NUM ";
    case T_NUMLIVES: return "NUMLIVES ";
    case T_POINTS: return "POINTS";
    case T_RBRACK: return "} ";
    case T_ROCK: return "ROCK ";
    case T_SCALE: return "SCALE ";
    case T_SEMI: return "; ";
    case T_SFXID: return "SFXID ";
    case T_SPEED: return "SPEED ";
    case T_STARTBOXSIZE: return "STARTBOXSIZE ";
    case T_STARTBOXX: return "STARTBOXX ";
    case T_STARTBOXY: return "STARTBOXY ";
    case T_STRING: return "STRING ";
    case T_TEXT: return "TEXT ";
    case T_TIMELIMIT: return "TIMELIMIT ";
    case T_WORD: return "WORD ";
    case T_XPOSITION: return "XPOSITION ";
    case T_YPOSITION: return "YPOSITION ";
    default: return "<token not defined in printtoken()> ";
  }
}

void print_token(tok_info *toki)
{
  switch(toki->tok) {
    case T_WORD: printf("_%s_ ",toki->string);break;
    case T_STRING: printf("\"%s\" ",toki->string);break;
    case T_NUM: printf("%f",toki->num);break;
    default: printf(tok_string(toki->tok));break;
  }
}

tok_info *gettoken(FILE *file) {
  static tok_info toki;
  enum {NEUTRAL,INQUOTE,INWORD,INNUM} state = NEUTRAL;
  int c;
  int decimal;
  int divisor;
  int multiplier;
  char *w,*word;

  bzero(&toki,sizeof(tok_info));

  word = w = toki.string;

  while((c=fgetc(file)) != EOF) {
    switch(state) {
      case NEUTRAL:
	if(c>='0' && c<='9' || c=='.' || c=='-') {
	  state = INNUM;
	  decimal = 0;
	  divisor = 1;
	  multiplier = 10;
 	  if(c=='-') divisor = -1;
	  else if(c=='.') ungetc(c,file),toki.num = 0.0;
	  else toki.num = c - '0';
	  continue;
        }
	switch(c) {
	  case '\n': /* Ignore these silly things */
	  case ';': 
	  case ' ':
	  case '\t':
	  case '\b': continue;
	  case '=': toki.tok = T_EQUAL;		return &toki;
	  case '{': toki.tok = T_LBRACK;	return &toki;
	  case '}': toki.tok = T_RBRACK;	return &toki;
	  case ',': toki.tok = T_COMMA;		return &toki;
	  case '\"':
	    state = INQUOTE;
	    continue;
	  case '#': do c=fgetc(file); while(c!='\n' && c!= EOF);
	    continue;
	  default:
	    state = INWORD;
	    *w++ = c;
	    continue;
	}
	break;
      case INQUOTE:
	switch(c) {
	  case '\"':
	    *w = '\0';
	    toki.tok = T_STRING;
	    return &toki;
	  default:
	    *w++ = c;
	    continue;
	}
	break;
      case INWORD:
	switch(c) {
	  case ';':
	  case '{':
	  case '}':
	  case ' ':
	  case '\n':
	  case '\t':
	  case ',':
	  case '=':
	    ungetc(c,file);
	    *w = '\0';
#define wordck(a,b) \
	if(!strcmp(toki.string,a))\
	{toki.tok = b;return &toki;}

	    wordck("startboxx",T_STARTBOXX);
	    wordck("startboxy",T_STARTBOXY);
	    wordck("startboxsize",T_STARTBOXSIZE);
	    wordck("author",T_AUTHOR);
	    wordck("explcolor",T_EXPLCOLOR);
	    wordck("xposition",T_XPOSITION);
	    wordck("yposition",T_YPOSITION);
	    wordck("distposition",T_DISTPOSITION);
	    wordck("speed",T_SPEED);
	    wordck("minspeed",T_MINSPEED);
	    wordck("maxspeed",T_MAXSPEED);
	    wordck("maxlifespan",T_MAXLIFESPAN);
	    wordck("minlifespan",T_MINLIFESPAN);
	    wordck("lifespan",T_LIFESPAN);
	    wordck("framedelay",T_FRAMEDELAY);
	    wordck("numlives",T_NUMLIVES);
	    wordck("text",T_TEXT);
	    wordck("levelsize",T_LEVELSIZE);
	    wordck("points",T_POINTS);
	    wordck("maxshots",T_MAXSHOTS);
	    wordck("sfxid",T_SFXID);
	    wordck("timelimit",T_TIMELIMIT);
	    wordck("geometry",T_GEOMETRY);
	    wordck("count",T_COUNT);
	    wordck("scale",T_SCALE);
	    wordck("level",T_LEVEL);
	    wordck("rock",T_ROCK);
	    wordck("name",T_NAME);
	    wordck("child",T_CHILD);

	    toki.tok = T_WORD;
	    return &toki;
	  default:
	    *w++ =c;
	    continue;
	}
      case INNUM:
	if((tolower(c)=='x') || ((!isdigit(c)) &&
	   ((tolower(c)<'a' || tolower(c)>'f') || multiplier==10))) {
	  if((tolower(c)=='x')) multiplier = 16;
	  else if((c=='.') && (!decimal)) decimal = 1;
	  else {
	    ungetc(c,file);
	    toki.num /= ((float)divisor);
	    toki.tok = T_NUM;
	    return &toki;
	  }
        }
	else {
	  if(isdigit(c)) toki.num = toki.num * multiplier + (c - '0');
	  else toki.num = toki.num * multiplier + (tolower(c) + 10 - 'a');
	  if(decimal) divisor *= 10;
        }

	break;
    }
  }
  toki.tok = T_EOF;
  return &toki;
}
