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

/* This is a recursive descent parser for reading in data files used in the
   game.  This took me a long time (mainly because I'm not a proficient
   programmer when it comes to this kind of stuff.  I had to do something
   like this for my project at work, so I thought I would give a first stab
   at it in this game to save time when I had to do it for work.  The lexical
   analyzer is actually more reusable than the parser.
   - Aaron Michael Hightower */

tok_info *expecting(FILE *file,TOKEN expect)
{
  tok_info *toki;

  toki = gettoken(file);

  if(toki->tok != expect)
    printf("Expecting token %s, but found token %s instead.\n",
	    tok_string(expect),tok_string(toki->tok));
  return toki;
}

/* Note: you are responsible for freeing memory created by getstring */
static char *getstring(FILE *file)
{
  tok_info *toki;
  char *string;

  toki = expecting(file,T_STRING);
  string = (char *)malloc(strlen(toki->string)+1);
  strcpy(string,toki->string);

  /* Free this later yourself! */
  return string;
}

static char *geteqstring(FILE *file)
{
  expecting(file,T_EQUAL);
  return getstring(file);
}

static float geteqnum(FILE *file)
{
  expecting(file,T_EQUAL);
  return expecting(file,T_NUM)->num;
}

static void fatal(const char *string)
{
  fprintf(stderr,"%s\n",string);
  exit(-1);
}

rock *newrdag(void)
{
  rock *rdag;

  rdag = (rock *)calloc(sizeof(rock),1);
  rdag->rockcount = 1;
  rdag->scale = 1.0;

  return rdag;
}

void free_geometry(rock *rdag)
{
  if(!rdag) return;
  free_geometry(rdag->sibling);
  free_geometry(rdag->child);
  free_rock_geom(rdag);
}

static rock *already_loaded_geom_rdag(char *name,rock *rdag)
{
  rock *t;

  if((!rdag) || (!name)) return NULL;
  if(rdag->name && (!strcmp(rdag->name,name)) && rdag->vertex) return rdag;
  if(t = already_loaded_geom_rdag(name,rdag->next )) return t;
/*   if(t = already_loaded_geom_rdag(name,rdag->sibling)) return t; */
/*   if(t = already_loaded_geom_rdag(name,rdag->child)) return t; */

  return NULL;
}

rock *already_loaded_geometry(char *name)
{
  rock *r;
  int i,j;

  /* Search all levels in all missions for geometry if already loaded */
/*   for(i=0;i<Num_missions;i++) */
/*     for(j=0;j<Mission_list[i]->numlevels;j++) */
/*       if(r = already_loaded_geom_rdag(name,Mission_list[i]->level[j]->rocks)) */
/* 	return r; */
  if(r = already_loaded_geom_rdag(name,Rock_geom_lib)) return r;
  else return NULL;
}

void print_rdag(rock *rdag)
{
  if(!rdag) return;
  else {
    printf("rdag->name = %s\n",rdag->name);
    printf("rdag->rockcount = %d\n",rdag->rockcount);
    printf("rdag->scale = %f\n",rdag->scale);
  }

  print_rdag(rdag->sibling);
  print_rdag(rdag->child);
}

static rock *read_rdag(rock **rdagp,FILE *file)
{
  tok_info *toki;
  rock *rdag;

  while(*rdagp) { /* Want rdag to be an unused child pointer */
    rdagp = &((*rdagp)->sibling);
  }
  rdag = *rdagp = newrdag();

  rdag->min_speed = 0.0005; /* Default min and max speed */
  rdag->max_speed = 0.0025;
  rdag->value = 10;         /* default score for a rock */
  rdag->color = 0xFFFFFF;   /* default to white explosion color */
  rdag->distposition = 0.40;
  rdag->xposition = 0.0;
  rdag->yposition = 0.0;

  expecting(file,T_EQUAL);
  expecting(file,T_LBRACK);
  while((toki = gettoken(file)),toki->tok!=T_EOF) {
    switch(toki->tok) {
      case T_EXPLCOLOR:
	/* Use the cpacked color value here */
	rdag->color = (unsigned int) geteqnum(file);
	break;
      case T_XPOSITION:
	rdag->xposition = geteqnum(file);
	break;
      case T_YPOSITION:
	rdag->yposition = geteqnum(file);
	break;
      case T_DISTPOSITION:
	rdag->distposition = geteqnum(file);
	if(rdag->distposition >  1.0) rdag->distposition =  1.0;
	if(rdag->distposition < -1.0) rdag->distposition = -1.0;
	break;
      case T_SPEED:
	rdag->min_speed = geteqnum(file) * 0.0005;
	rdag->max_speed = rdag->min_speed;
	break;
      case T_MINSPEED:
	rdag->min_speed = geteqnum(file) * 0.0005;
	break;
      case T_MAXSPEED:
	rdag->max_speed = geteqnum(file) * 0.0005;
	break;
      case T_FRAMEDELAY:
	rdag->framedelay = geteqnum(file) * 100.0;
	if(rdag->framedelay < 0.001) rdag->framedelay = 0.0;
	break;
      case T_TEXT:
	rdag->text = geteqstring(file);
	break;
      case T_LIFESPAN:
	rdag->minlifespan = 100 * geteqnum(file);
	rdag->maxlifespan = rdag->minlifespan;
	break;
      case T_MINLIFESPAN:
	rdag->minlifespan = 100 * geteqnum(file);
	break;
      case T_MAXLIFESPAN:
	rdag->maxlifespan = 100 * geteqnum(file);
	break;
      case T_POINTS:
	rdag->value = geteqnum(file);
	if(rdag->value < 0) rdag->value = 0;
	break;
      case T_SFXID:
	rdag->sfxid = geteqnum(file);
	if(rdag->sfxid>=SFX_N_SOUNDS || rdag->sfxid < 0) {
	  printf("Sound id is out of range\n");
	  rdag->sfxid = 0;
	}
	break;
      case T_COUNT:
	rdag->rockcount = geteqnum(file);
	break;
      case T_SCALE:
	rdag->scale = geteqnum(file);
	break;
      case T_AUTHOR:
	rdag->author = geteqstring(file);
	break;
      case T_GEOMETRY:
 	rdag->name = geteqstring(file);
	break;
      case T_RBRACK:
	return;
      case T_ROCK:
	read_rdag(&(rdag->child),file);
	break;
      default:
	printf("Unexpected token \"%s\" found in input file\n",tok_string(toki->tok));
	break;
    }
  }
}

void read_level(level *l,FILE *file)
{
  static char name[] = "unnamed";
  tok_info *toki;
  enum {NEUTRAL,INLEVEL} state = NEUTRAL;

  if(!l) return ; /* This expects the level to be malloc'ed ahead of time */

  l->rocks = NULL;

  if((!file) || (!l)) return;
  if(gettoken(file)->tok!=T_LEVEL) return;
  if(expecting(file,T_EQUAL)->tok == T_EOF) return;
  if(expecting(file,T_LBRACK)->tok == T_EOF) return;

  bzero(l,sizeof(level)); /* Clear out the level information */
  l->startboxsize = 1.0;
  l->levelsize = 1.0;
  l->name = name;
  l->bonus = 1000;

  while((toki = gettoken(file)),toki->tok!=T_EOF) switch(toki->tok) {
    case T_STARTBOXX:
      l->startx = geteqnum(file);
      break;
    case T_STARTBOXY:
      l->startx = geteqnum(file);
      break;
    case T_STARTBOXSIZE:
      l->startboxsize = geteqnum(file);
      break;
    case T_BONUS:
      l->bonus = geteqnum(file);
      break;
    case T_NUMLIVES:
      l->numlives = geteqnum(file);
      break;
    case T_AUTHOR:
      l->author = geteqstring(file);
      break;
    case T_NAME:
      l->name = geteqstring(file);
      break;
    case T_ROCK:
      read_rdag(&(l->rocks),file);
      break;
    case T_LEVELSIZE:
      l->levelsize = geteqnum(file);
      break;
    case T_MAXSHOTS:
      l->maxshots = geteqnum(file);
      break;
    case T_TIMELIMIT:
      l->timelimit = geteqnum(file);
      break;
    case T_RBRACK:
      return;
    default:
      printf("\nUnexpected token found: %s\n",tok_string(toki->tok));
      return;
  }
  return;
}
