#define __RAWKS_READ__
#define DBG 0

#include "defines.h"
#include "struct.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "gl.h"
#include "gldevice.h"
#include <string.h>
#include <math.h>
#include <dirent.h>
#include "protos.h"
#include "globals.h"

/* If the user wants out, let them , otherwise absorb (ignore) the events */
static void process_qevent(void)
{
  short val;
  int rval;

  while(qtest()) {
    rval = qread(&val);
    switch(rval) {
      case ESCKEY:
      case WINQUIT:
      case WINSHUT:
	closeall();
	break;
      default:
	break;
    }
  }
}

/* This function really needs to be broken into pieces.  It's too big!
   - Aaron Michael Hightower */
void read_in_rock(rock *Rock,int force_load)
{
  char defaultname[]="default";
  int popened = 0;
  rock *trock;
  struct stat filestat;
  static int font = -1;
  float maxx,minx,maxy,miny;
  FILE *file;
  char *rockdir,*notdone;
  int i,j,k;
  int num_els;
  char buffer[1024];
  int rval;
  short val;
  xyz ***r;

  maxx=maxy=-9999.9;
  minx=miny= 9999.9;

  process_qevent();

/*   if(fork()) printf("Forked\n"); */
/*   else printf("Child\n"); */

  if(!Rock->name) Rock->name = (char *)getenv("RAWK_DATA");
  if(!Rock->name) Rock->name = strdup("default");
  rockdir = (char *)getenv("RAWK_ROCKDIR");
  if(!rockdir) rockdir="./.data/rawks/Rocks";

  if(!Rock->name) syserr("Could not get name for rock");
  else strcpy(buffer,Rock->name);
  strtok(buffer," \t\r\n");

  if(stat(buffer,&filestat) == -1) {
    char *t;
    strcpy(buffer,rockdir);
    strcat(buffer,"/");
    strcat(buffer,amh_basename(Rock->name));
    strtok(buffer," ");
    t = strtok(NULL,"\0");
    if(stat(buffer,&filestat) == -1) {
      strcpy(buffer,rockdir);
      strcat(buffer,"/");
      strcat(buffer,Rock->name);
      strtok(buffer," ");
      t = strtok(NULL,"\0");
      if(stat(buffer,&filestat) == -1) {
	fprintf(stderr,"Could not find file %s or file %s\n",Rock->name,buffer);
	printf("Rock read failed\n");
	Rock->num_frames = 0;
	Rock->num_verts = 0;
	Rock->framedelay = 0.1;
	return ;
      }
    }
    free(Rock->name);
    if(t && t>buffer) t[-1] = ' '; /* Strip trailing spaces */
    Rock->name = malloc(strlen(buffer)+1);
    strcpy(Rock->name,buffer);
  }
  Rock->lastmod = filestat.st_mtime; /* Save what time this version was last
					modified */

  if(Level && Level->rocks) { /* Assume no duplicate geometry for ships */
    /* Copy existing geometry if already loaded */
    if(!force_load && (trock = already_loaded_geometry(Rock->name)))
    {
      Rock->vertex = trock->vertex; /* Reference to geometry */
      Rock->maxx = trock->maxx * Rock->scale / trock->scale;
      Rock->minx = trock->minx * Rock->scale / trock->scale;
      Rock->maxy = trock->maxy * Rock->scale / trock->scale;
      Rock->miny = trock->miny * Rock->scale / trock->scale;
      Rock->num_frames = trock->num_frames;
      Rock->num_verts = trock->num_verts;
      if(!Rock->framedelay) Rock->framedelay = trock->framedelay;
      if(Rock->framedelay<=0) Rock->framedelay = ((1.0/60.0)*100);
      return ;
    }
  }
  if(filestat.st_mode & 0111) { /* If executable */
#ifdef __LINUX__
    char *tmp;

    tmp = malloc(strlen(Rock->name)+strlen("awk -f ")+1);

    strcpy(tmp,"awk -f ");
    strcat(tmp,Rock->name);
    file = popen(tmp,"r");
    free(tmp);

    if(!file) syserr("Couldn't popen file");
    else popened = 1;
#else
    if(!(file = popen(Rock->name,"r"))) syserr("Couldn't popen file");
    else popened = 1;
#endif
  } else if(filestat.st_mode & 0444) { /* If readable */
    if(!(file = fopen(Rock->name,"r"))) syserr("Couldn't fopen file");
  }

  reshapeviewport();
  ortho2(-1,1,-1,1);
  if(font < 0) font = openvecfont(50,30,0.7);
  C_BLACK;
  clear();
  vmov2(font,0.0,0.0);
  vccprint(font,NORMALDRAW,0,0xFFFF,"Loading geometry for");
  vccprint(font,NORMALDRAW,0,0xFFFFFF,amh_basename(Rock->name));
  my_swapbuffers(NORMALDRAW);

  DEBUG("Looking at first line of input");
  do {
    notdone = fgets(buffer,1023,file);
  } while(notdone && buffer[0]=='#');
  DEBUG("Done getting first line of input");
  {
    float temp;
    temp = Rock->framedelay;

    if((sscanf(buffer,"%d %d %f",&Rock->num_frames,&Rock->num_verts,
				 &Rock->framedelay))!=3) {
      printf("Error reading in geometry for rock %s\n",Rock->name);
      printf("Error is in first line of input file\n"
	     "Maybe it should be executable? (IE: chmod +x rockname)");
      printf("Reading in default rock");
      fclose(file);
      popened = 0;
/*       file = make_default_rock_stream(); */
      Rock->num_frames = 0;
      Rock->num_verts = 0;
      Rock->framedelay = 0.1;
      return;
    }

    Rock->framedelay*=100.0;
    
    /* If framedelay was given in the level, keep it, rather than loading
       the framedelay from the geometry file */
    if(temp > 0.0) Rock->framedelay = temp;
    if(Rock->framedelay<=0) Rock->framedelay = ((1.0/60.0)*100);
  }
#if DBG
  printf("Rock->framedelay = %f\n",Rock->framedelay);
#endif
  DEBUG("Done scanning in first line of input");

  DEBUG("Allocating memory for vertices");
  if(Rock->vertex) free_rock_geom(Rock);
  r = Rock->vertex = (xyz ***)make_3d_array(Rock->num_frames,Rock->num_verts);

  DEBUG("Done allocating memory for vertices");
  for(k=0;k<Rock->num_frames;k++) {

    for(j=0;j<Rock->num_verts && notdone;j++) {
      do {
	notdone = fgets(buffer,1023,file);
      } while(notdone && buffer[0]=='#');
      sscanf(buffer,"%ld %f %f %f %x",
	&r[k][j]->flags,
	&r[k][j]->x,
	&r[k][j]->y,
	&r[k][j]->z,
	&r[k][j]->color);
      if(r[k][j]->x > maxx) maxx = r[k][j]->x;
      if(r[k][j]->x < minx) minx = r[k][j]->x;
      if(r[k][j]->y > maxy) maxy = r[k][j]->y;
      if(r[k][j]->y < miny) miny = r[k][j]->y;
#if 0
      fprintf(stderr,"%f %f %d\n",r[k][j]->x,r[k][j]->y,r[k][j]->color);
#endif
    }
  }
  if(popened) pclose(file);
  else fclose(file);
  Rock->maxx = maxx * Rock->scale;
  Rock->minx = minx * Rock->scale;
  Rock->maxy = maxy * Rock->scale;
  Rock->miny = miny * Rock->scale;

  {
    rock *tr;

    if(!(tr = already_loaded_geometry(Rock->name))) {
/*       printf("Adding %s to library\n",Rock->name); */
      tr = (rock *)calloc(sizeof(rock),1);
      memcpy(tr,Rock,sizeof(rock));
      add_to_library(&Rock_geom_lib,tr);
    }
    else memcpy(tr,Rock,sizeof(rock));
  }
}

void load_geom_rdag(rock *rdag)
{
  if(!(rdag)) return;

  read_in_rock(rdag,0);

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

int filter_dotstar(const struct dirent *dep)
{
  return (dep->d_name[0]!='.');
}

int filter_dotdot(const struct dirent *dep)
{
  /* Don't let "." and ".." slip through */
  return (strcmp(dep->d_name,".") && strcmp(dep->d_name,".."));
}

static rock **loadships(char *dir)
{
  rock **rpp;
  struct dirent **dirinfo;
  int i;

  Num_ships = scandir(dir,&dirinfo,filter_dotdot,alphasort);
  if(Num_ships <= 0) {
    fprintf(stderr,"Warning! Cannot locate any ship data files in dir: %s\n",
		  dir);
  }
  rpp = (rock **)calloc(sizeof(rock *),Num_ships);
  for(i=0;i<Num_ships;i++) {
    rpp[i] = (rock *)calloc(sizeof(rock),1);
    rpp[i]->name = malloc(strlen(dir)+1+strlen(dirinfo[i]->d_name)+1);
    strcpy(rpp[i]->name,dir);
    strcat(rpp[i]->name,"/");
    strcat(rpp[i]->name,dirinfo[i]->d_name);
#if DBG
    printf("Loading ship %s\n",dirinfo[i]->d_name);
#endif
    read_in_rock(rpp[i],1);
  }
  return rpp;
}

void loadgeometry(void)
{
  char *dir;

  if(Ships) return;

  drawmode(PUPDRAW);
  clear_overlay();
  vmov2(Largefont,0.0,0.90);
  mapcolor(1,0,0x80,0);
  mapcolor(2,0,0xff,0);
  gflush();
  color(1);
  vcprint(Largefont,"\01 Space craft \01");
  color(2);
  vcprint(Largefont,"preparing for immediate use");

  drawmode(NORMALDRAW);

  if(!(dir = getenv("RAWK_SHIPDIR"))) dir = "./.data/rawks/Ships";
  Ships = loadships(dir);
  clear_overlay();
}

void reload_mission(void)
{
  /* This should only be called when the game is over */
  if(!Game_over) return;

  free_all_rocks(); /* Get rid of memory for rocks and everything on screen */
#if 0
  Now this should already have freed the outdated rocks (only)
  free_geometry(Level->rocks);
#endif

  Next_level--;
  init_scene();
  Speed = INIT_SPEED; /* Hack! */
}

/* This function should examine all rocks being displayed and
   if any of the files for the rock's geometry is newer than that
   indicated at load time, then re-load the geometry by that name */

static void reload_ifnewer_rdag(rock *r)
{
  char buffer[256];
  struct stat filestat;

  if(!r) return;

  if(r->name) {
    strcpy(buffer,r->name);
    strtok(buffer," \r\t\n");
    if(stat(buffer,&filestat) == -1) printf("Error: stat(%s)\n",r->name);
    if(filestat.st_mtime != r->lastmod) {
      free_all_geometry_by_name(r->name);
      read_mission_levels(Mission_list[Selected_mission]);
      reload_mission();
      return;
    }
  }
  reload_ifnewer_rdag(r->next);
}

static void reload_ifnewer_mission(mission *mp)
{
  struct stat filestat;

  if(!mp) return;
  
  if(mp->name) {
    if(stat(mp->name,&filestat) == -1) {
      printf("File stat failed on mission %s\n",mp->name);
      return ;
    } else if(get_mission_checksum(mp->name) != mp->miss_checksum) {
      read_mission_levels(mp);
      reload_mission();
    }
  }
}

void reload_ifnewer(void)
{
  if(!Level) return;

  reload_ifnewer_rdag(Rock_geom_lib);
  reload_ifnewer_mission(Mission_list[Selected_mission]);
}

void free_all_geometry_by_name(char *name)
{
  rock *r;

  while(r=already_loaded_geometry(name)) {
    free_rock_geom(r);
    if(r->name) free(r->name);
    if(r->author) free(r->author);
    if(r->text) free(r->text);
    free(r);
  }
}
