#define __RAWKS_ROCKS__
#define DBG 0

#include "defines.h"
#include "struct.h"
#include "gl.h"
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "protos.h"
#include "globals.h"

#define NUM_ROCK_SIZES 3

static struct timeval tval;
static rock *r;

void init_scene(void)
{
#define NUM_INTROSOUNDS 3
  static int introsound[NUM_INTROSOUNDS] = { SFX_SAD,SFX_MYSTIC,SFX_BELL };
  static int sound_index=NUM_INTROSOUNDS;
  static FILE *file = NULL;
  int i;
  rock *r;

  DEBUG("Freeing all rocks");
  free_all_rocks();

  DEBUG("Freeing rock dag");
#if 0
  /* Taking this out makes loads faster */
  if(Level) free_geometry(Level->rocks);
#endif

  if(Next_level >= Mission_list[Selected_mission]->numlevels) {
/*     Game_over = 1; */
    Next_level = Mission_list[Selected_mission]->numlevels;
    Speed*=1.1; /* Get 10% faster each time replay the last level */
    /* Keep repeating last level over and over until all dead */
  }
  else Level = Mission_list[Selected_mission]->level[Next_level++];

  for(i=Firstbullet_i;i!=Nextbullet_i;i=INC_BULLET(i)) B[i].ttl = -2;

  for(i=1;i<=Num_players;i++) {
    char *message;

    if(P[i].numlives) P[i].numlives += Level->numlives;
    if(Next_level >=2) {
      sprintf(P[i].message,"pts=%d %6.2f%% & %5.2f sec.",
	P[i].score - P[i].startscore,
	(float)((100.0*( P[i].hit - P[i].starthit )/
	( P[i].fired - P[i].startfired + (P[i].fired == P[i].startfired)))),
	P[i].finishtime/ 100.0
      );
    }
    else P[i].message[0] = 0;
    P[i].dead = ((float)DEAD_TIME *drand48());
    P[i].finishtime = 0.0;
    P[i].startscore = P[i].score;
    P[i].startfired = P[i].fired;
    P[i].starthit = P[i].hit;
  }
  Show_high = !Game_over;

  if(!Level) syserr("Level pointer lost\n");
/*   print_rdag(Level->rocks); */
  print_levelname(Level);
  load_geom_rdag(Level->rocks);
  memcpy(&Rock_head,Level->rocks,sizeof(rock));

  newsize();
  update_overlay(OLAY_TEXT | OLAY_IMAGE | OLAY_SCORES);
  clear_normal();

  DEBUG("Making new rocks");
  for(r=Level->rocks;r;r=r->sibling) {
    for(i=0;i<r->rockcount;i++) {
      add_rock(r);
    }
  }
  print_all_rocks();

  if(++sound_index >= NUM_INTROSOUNDS) sound_index = 0;
  sfx(introsound[sound_index]);
}

rock *add_rock(rock *template)
{
  rock *r;

  r = newrock(template);
  if(r->next = Rock_head.next)
    r->next->prev = r;
  Rock_head.next = r;
  r->prev = &Rock_head;

  return r;
}

void kill_rock(rock **r)
{
  static int expl_num = 0;

  if(!r || !*r) return;

  if((*r)->prev) (*r)->prev->next = (*r)->next; /* Delete this rock from the list */
  if((*r)->next) (*r)->next->prev = (*r)->prev;

  if(++expl_num >= NUM_EXPL_SFX) expl_num = 0;

  /* Assumes NUM_EXPL_SFX in a row starting at SFX_EXPL_1 */

  /* If sfxid is non-zero, use custom sound effect else incremental explosion*/
  sfx((*r)->sfxid?(*r)->sfxid:(SFX_EXPL_1 + expl_num));
  explode(10,(trajectory *)(*r));
  Collision_det = 0;

  C_BLACK;
  frontbuffer(FALSE);
  backbuffer(TRUE);
  draw_line_rock((*r));

  {
    int tmp = Frm;

    Frm = Old_frm;

    backbuffer(FALSE);
    frontbuffer(TRUE);
    draw_line_rock((*r));

    Frm = tmp;
  }

  frontbuffer(FALSE);
  backbuffer(TRUE);

  Collision_det = 1;
  if((*r)->child) { /* Split into other rocks */
    int i;
    rock *temp,*child;

    for(child = (*r)->child;child;child = child->sibling) {
      for(i=0;i<child->rockcount;i++) {
	temp = add_rock(child);
	temp->V[Frm].x = (*r)->V[Frm].x;
	temp->V[Frm].y = (*r)->V[Frm].y;
      }
    }
  }

  free((*r));
  *r = NULL;

  if(!Rock_head.next); /* If all rocks gone, set up for next scene */
    set_time(&New_scene_time,&Cur_time,4,0);
}

void free_all_rocks(void)
{
  rock *r;

  DEBUG("Freeing all the rocks");

  for(r=Rock_head.next;r;r = r->next)
  {
    if(r->prev) {
      r->prev->next = NULL;
      if (r->prev != &Rock_head) free(r->prev);
      else bzero(&Rock_head,sizeof(rock));
    }
    if(!r->next) {
      free(r);
      break;
    }
  }
}

rock *newrock(rock *template)
{
  float x,y,r;
  int direction;
  rock *trock,*tr;
  float speed;
  float distpos;
  
  DEBUG("Allocating a new rock");
  trock = (rock *)calloc(1,sizeof(rock));

  DEBUG("Assigning random position & speeds");

  direction = fast_rand()*255;

  if(template->distposition <= 0.0)
    r = fast_rand() * (-(template->distposition));
  else r = template->distposition + fast_rand();

  x = -Sintable[direction]*r;
  y =  Costable[direction]*r;

  trock->V[!Frm].x = trock->V[Frm].x = x + template->xposition;
  trock->V[!Frm].y = trock->V[Frm].y = y + template->yposition;

  trock->min_speed = template->min_speed;
  trock->max_speed = template->max_speed;

  speed = trock->min_speed+(trock->max_speed-trock->min_speed)*fast_rand();
  direction = fast_rand()*255;
  trock->inertiax=-Sintable[direction]*speed;
  trock->inertiay= Costable[direction]*speed;

  trock->scale = template->scale;
  trock->value = template->value;
  trock->num_frames = template->num_frames;
  trock->num_verts  = template->num_verts;
  trock->vertex     = template->vertex;
  trock->framedelay = template->framedelay;
  trock->color      = template->color;
  trock->frame_ctr[0]= (int)(((trock->num_frames-1) * fast_rand()));
  trock->frame_ctr[1]= trock->frame_ctr[0];
  trock->minx = template->minx;
  trock->maxx = template->maxx;
  trock->miny = template->miny;
  trock->maxy = template->maxy;
  trock->sfxid= template->sfxid;
  trock->child= template->child;
  trock->lifespan = template->maxlifespan -
		   (template->maxlifespan-template->minlifespan) * fast_rand();
  trock->text = template->text;

  return trock;
}

float my_rand_float(float min,float max)
{
  return (float) ((drand48()*(max-min)) + min);
}

void print_all_rocks(void)
{
#if DBG
  rock *rp;

  for(rp = Rock_head.next;rp; rp = rp->next)
  {
    printf("Rock-> %f %f %f %f\n",rp->V[0].x,rp->V[0].y,
      rp->inertiax,rp->inertiay);
  }
#endif
}

void update_rocks(void)
{
  rock *r,*n;

  for(r=Rock_head.next;r;r=n)
  {
    int num_frames_to_advance = 0;

    n = r->next; /* Have to presave this to prevent looking at outdated memory */
		 /* Because draw_rock calls kill_rock calls free(rock)!!! */
		 /* This was a long standing bug - (Bad me, bad bad me) */

    C_BLACK; /* Do this just once instead of per vertex */
    draw_rock(&r);
    if(!r) continue; /* r shouldn't be freed here (but error checking==good) */

    update_trajectory((trajectory *)r);

    r->age+=D_time;
    r->frame_age+=D_time;

    while(r->frame_age >= r->framedelay) {
/*       printf("frame_age = %f , framedelay = %f\n",r->frame_age,r->framedelay); */
      r->frame_age-=r->framedelay;
      num_frames_to_advance++;
    }
    r->frame_ctr[Frm]=r->frame_ctr[Old_frm]+num_frames_to_advance;

    while(r->num_frames && r->frame_ctr[Frm]>=r->num_frames) r->frame_ctr[Frm]-=r->num_frames;

    Collision_det = 1;
    C_WHITE;
    draw_rock(&r);
    Collision_det = 0;
  }
}

int v2f_line_ship(int p,float *t)
{
  static float prev[2];

  v2f(t);

  if(Flag && Collision_det) {
    int result;

    if(result = intersect_all(prev,t,p)) {
      if(result<0) { /* Player -result is Dead */
	result = -result;
/* 	P[result].score += P[p].value; */
/* 	P[p].score += P[result].value; */
/* 	P[result].numlives--; */
/* 	P[p].numlives--; */
	explode(100,(trajectory *)&P[result]);
	explode(100,(trajectory *)&P[p]);
/* 	if(!P[result].numlives) strcpy(P[result].message,"Game Over"); */
	P[result].dead = DEAD_TIME; /* Kill both of them */
	P[p].dead = DEAD_TIME;
      }
      else if(result != p) {
/* 	if(P[result].numlives) P[result].score += P[p].value; */
/* 	if(P[p].text) strcpy(P[result].message,P[p].text); */
	explode(100,(trajectory *)&P[p]);
	P[p].dead = DEAD_TIME;
	return -1; /* Player result has killed rock r */
      }
    }
  }
  else Flag = 1; /* Don't do intersection on first vertex */

  prev[0] = t[0];
  prev[1] = t[1];

  return 0;
}

int v2f_line(rock *r,xyz *v)
{
  static float prev[2],t[2];

  t[0] = v->x*r->scale + r->V[Frm].x;
  t[1] = v->y*r->scale + r->V[Frm].y;


  if(v->flags & VERTEX_NODRAW) {
    endline();
    bgnline();
  }
  if(Collision_det) cpack(v->color);
  v2f(t);

  if(Flag && Collision_det && (!(v->flags & VERTEX_HARMLESS))) {
    int result;

    if(result = intersect_all(prev,t,0)) {
      if(result<0) { /* Player -result is Dead */
	result = -result;
	P[result].numlives--;
	explode(100,(trajectory *)&P[result]);
	if(!P[result].numlives) strcpy(P[result].message,"Game Over");
	P[result].dead = DEAD_TIME;

	if(v->flags & VERTEX_BULLETPROOF) return 0;
	else {
	  P[result].score += r->value;
	  return 1;
        }
      }
      else {
	if(!(v->flags & VERTEX_BULLETPROOF)) {
	  if(P[result].numlives) P[result].score += 
	     P[result].exempt?r->value:r->value+1;
	     /* Break tie in favor of kamikaze */

	  if(r->text) strcpy(P[result].message,r->text);
	  return -1; /* Player result has killed rock r */
	} else {
/* I got rid of the following because it looked stupid */
/*	  explode(10,(trajectory *)r);  Explosion, but no rock kill */
	  if(r->text) strcpy(P[result].message,r->text);
	}
      }
    }
  }
  else Flag = 1; /* Don't do intersection on first vertex */

  prev[0] = t[0];
  prev[1] = t[1];

  return 0;
}

int draw_line_rock(rock *r)
{
  int hit;
  int i;
  float tx,ty;
  float x,y;
  float ox,oy,sx,sy;

  tx = r->V[Frm].x; /* save xy */
  ty = r->V[Frm].y;

  sx = ( tx < 0.0 ) ? (0.0) : (-2.0);
  sy = ( ty < 0.0 ) ? (0.0) : (-2.0);

  for(ox=sx;ox<sx+3;ox+=2) {

    x = r->V[Frm].x = tx + ox;
    if(((x + r->maxx) < -1.0) || ((x + r->minx) > 1.0)) continue;

    for(oy=sy;oy<sy+3;oy+=2) {

      y = r->V[Frm].y = ty + oy;
      if(((y + r->maxy) < -1.0) || ((y + r->miny) > 1.0)) continue;

      if(Collision_det) {
	float px,py;
	int f2xltf0x,f2yltf0y;

	for(i=1;i<=Num_players;i++) { /* Check exemptions via bbox for ships */
	  px = P[i].V[Frm].x;
	  py = P[i].V[Frm].y;

	  if(r->minx + x > P[i].maxx + px ||
	     r->maxx + x < P[i].minx + px ||
	     r->miny + y > P[i].maxy + py ||
	     r->maxy + y < P[i].miny + py
	    )
	    P[i].exempt = 1;
	  else P[i].allclear = P[i].exempt = 0;
	}
	/* The idea here is to check each rock against each bullet to
	   cull out most rocks before having to check each line against each
	   bullet */
	for(i=Firstbullet_i;i!=Nextbullet_i;i=INC_BULLET(i)) {
	  /* 2+Frm = Frm + 2*inertia */
	  f2xltf0x = (B[i].inertiax > 0.0)?0:2;
	  f2yltf0y = (B[i].inertiay > 0.0)?0:2;
	  
	  if(r->minx + x > B[i].V[Frm+2-f2xltf0x ].x ||
	     r->maxx + x < B[i].V[Frm+  f2xltf0x ].x ||
	     r->miny + y > B[i].V[Frm+2-f2yltf0y ].y ||
	     r->maxy + y < B[i].V[Frm+  f2yltf0y ].y
	    )
	    B[i].exempt=1;
	  else B[i].exempt=0;
	}
      }
#if DBG
      sbox(r->minx+x,r->miny+y,r->maxx+x,r->maxy+y);
#endif
      bgnline();
      hit = Flag = 0;
      for(i=0;i<r->num_verts;i++) {
	hit += v2f_line(r,r->vertex[r->frame_ctr[Frm]][i]);
      }
      endline();
      if(hit) break;
    }
    if(hit) break;
  }
  r->V[Frm].x = tx; /* restore xy */
  r->V[Frm].y = ty;

  return hit;
}

/* Warning! draw_rock may free memory at *r! */
void draw_rock(rock **r)
{
#if DBG
  DEBUG("draw_rock()");
#endif
  if(draw_line_rock(*r)) {
#if DBG
  DEBUG("rock was hit, killing rock");
#endif
    kill_rock(r);
  }
  else {
#if DBG
  DEBUG("rock draw, checking lifespan");
#endif
    if(Collision_det && (*r)->lifespan && (*r)->age > (*r)->lifespan) {
#if DBG
  DEBUG("lifespan expired: killing rock");
#endif
      kill_rock(r);
    }
  }
#if DBG
  DEBUG("draw_rock complete");
#endif
}
