#include <time.h>
#include <sys/time.h>
#include <unistd.h>


#include "globals.h"
#include "sound.h"
#include "netvariables.h"
#include "netdata.h"
#include "network.h"
#include "thread.h"
#include "status.h"
#include "object.h"
#include "ship.h"
#include "screen.h"
#include "key.h"


struct timeval LastTime;
struct timeval UpdateTime;


void RunFrame()
{
  int i, j, RunTime;
  struct timezone tz;
  struct timeval now;
  float AgeTime;

  // Play the boom sounds
  NextBoom--;
  if (NextBoom == 0) {
    if (BoomPhase) {
      playsound(Boom1);
      BoomPhase = 0;
    } else {
      playsound(Boom2);
      BoomPhase = 1;
    }
    NextBoom = BoomDelay;
  }

  // Update looping sounds
  if (Ships[myShip]) {
    if (!Ships[myShip]->Alive || !Ships[myShip]->Shield) {
      if (ShieldSoundNum!=-1) {
        stopsoundinloop(ShieldSoundNum);
        ShieldSoundNum=-1;
      }
    } else {
      if (KeyValues.ShieldKey && ShieldSoundNum==-1) {
        ShieldSoundNum=playsoundinloop(ShieldOnSound);
      }
    }
    if (!Ships[myShip]->Alive) {
      if (ThrustSoundNum!=-1) {
        stopsoundinloop(ThrustSoundNum);
        ThrustSoundNum=-1;
      }
    } else {
      if (KeyValues.zForwardKey && ThrustSoundNum==-1) {
        ThrustSoundNum=playsoundinloop(ThrustSound);
      }
    }
  } else {
    if (ShieldSoundNum!=-1) {
      stopsoundinloop(ShieldSoundNum);
      ShieldSoundNum=-1;
    }
    if (ThrustSoundNum!=-1) {
      stopsoundinloop(ThrustSoundNum);
      ThrustSoundNum=-1;
    }
  }

  // Display object and ship sprites
  gettimeofday(&now, &tz);

  // Send out update every second
  if (now.tv_sec!=UpdateTime.tv_sec) {
    SendUpdate();
    memcpy(&UpdateTime, &now, sizeof(struct timeval));
  }

  if (Ships[myShip]) {
    DrawStatus();
    for (i=0; i<MAX_PLAYERS; i++) {
      if (Ships[i] && i!=myShip && Ships[i]->Alive) {
        DrawObject(Ships[i]);
      }
    }

    InitTransparencyQueue();

    for (i=0; i<MAX_OBJECTS; i++) {
      if (Objects[i]) {
        if (Objects[i]->Type==ASTEROID_TYPE || Objects[i]->Type==SHOT_TYPE || Objects[i]->Type==NUKE_TYPE) {
          DrawObject(Objects[i]);
        } else if (Objects[i]->Type==SHIELD_POWERUP_TYPE || Objects[i]->Type==ARMOR_POWERUP_TYPE ||
                   Objects[i]->Type==NUKE_POWERUP_TYPE || Objects[i]->Type==HYPERWARP_POWERUP_TYPE) {
          DrawPowerup(Objects[i]);
          QueueTransparency(&Objects[i]->Position, i, 3);
        } else if (Objects[i]->Type==NUKE_EXPLOSION_TYPE) {
          QueueTransparency(&Objects[i]->Position, i, 4);
        }
      }
    }

    EnableTransparency();

    for (i=0; i<MAX_OBJECTS; i++) {
      if (Objects[i]) {
        if ((Objects[i]->Type==EXPLOSION_TYPE || Objects[i]->Type==BIG_EXPLOSION_TYPE) && Objects[i]->Age < 1.0) {
          QueueTransparency(&Objects[i]->Position, i, 1);
        }
      }
    }

    for (i=0; i<MAX_PLAYERS; i++) {
      if (Ships[i] && i!=myShip && Ships[i]->ShieldOn) {
        QueueTransparency(&Ships[i]->Position, i, 2);
      }
    }

    DrawTransparencies();

    DrawCrosshairs();

    DrawTest();

    DisableTransparency();
  }

#ifdef DEBUG
  printf("Game Thread Sleeping\n");
#endif
  pthread_mutex_unlock(&ClientLock);
  /*
    We need to sleep to accomplish the following:

    Make sure that each frame update takes the same amount of time
    Handle the sound correctly
    Allow other threads to update the data
    Not be too much of a CPU hog
  */
  RunTime=(now.tv_sec-LastTime.tv_sec)*1000000+(now.tv_usec-LastTime.tv_usec);
  LastTime.tv_sec=now.tv_sec;
  LastTime.tv_usec=now.tv_usec;

  // The Cygwin usleep and gettimeofday functions appear to be broken,
  // so this is a workaround
#ifndef CYGWIN
  if (RunTime<FRAME_DELAY*1000 && RunTime>0) {
    usleep(FRAME_DELAY*1000-RunTime);
    AgeTime=0.15;
  } else {
    AgeTime=0.15*((float)RunTime/(float)(FRAME_DELAY*1000));
  }
#else
  usleep(FRAME_DELAY*1000);
  AgeTime=0.15;
#endif
  for (i=0; i<MAX_OBJECTS; i++) {
    if (Objects[i]) {
      if (Objects[i]->Type==EXPLOSION_TYPE) {
        Objects[i]->Age+=AgeTime;
      } else if (Objects[i]->Type==BIG_EXPLOSION_TYPE) {
        Objects[i]->Age+=AgeTime/6.0;
      }
    }
  }

  gettimeofday(&LastTime, &tz);

  pthread_mutex_lock(&ClientLock);
#ifdef DEBUG
  printf("Game Thread Awake\n");
#endif
}
