// We're starting this shit NOW
function init() {
  canvas = document.getElementById ('thescreen');
  ctx = canvas.getContext ('2d');

  for (var i = 0; i < 200; i++) {
    Stars.push (new Star (Math.random() * 480, Math.random() * 640,
                          Math.random() * 4 + 0.5));
  }
  for (var i = 0; i < 80; i++) {
    Rift[0].push (new RiftPoint (0, 0, 640 / 79, 2));
  }
  for (var i = 0; i < 35; i++) {
    Rift[1].push (new RiftPoint (0, 0, 640 / 34, 5));
  }
  for (var i = 0; i < 18; i++) {
    Rift[2].push (new RiftPoint (0, 0, 640 / 17, 9));
  }
  
  start_time = setInterval ("timed_count();", msec_frame);
}

// Restart game
function restart_game (wavetostart) {
  Enemies.splice (0, Enemies.length);
  lives = 3;
  state_counter = 0;
  current_score = 0;
  free_life = 1000;
  time_count = 0;
  wave_count = 0;
  power1 = 0;
  power2 = 0;
  shipx = 240;
  shipy = 500;
  current_wave = wavetostart;
}

// Starfield
function draw_bg() {
  var brightStyle = "rgb(96,32,16)";
  var darkStyle = "rgb(16,16,16)";
  var surfStyle = "rgb(48,16,16)";
  var craterfade = 0;
  var craterfade2 = 0;
  var craterfade3 = 0;
  if (background_type == "Rift" || background_type == "Riftoff") {
    ctx.fillStyle = "rgb(16,0,0)";
    ctx.fillRect (0, 0, 480, 640);
  }
  else if (background_type == "Planet Surface") {
    if (current_wave == 3) {
      if (time_count < 300) {
        var colorfade = Math.floor (48 - time_count / 9.375);
        surfStyle = "rgb(48,16," + colorfade + ")";
        craterfade = Math.floor (48 - time_count / 9.375);
        craterfade2 = Math.floor (48 + time_count / 9.375);
        craterfade3 = Math.floor (48 - time_count / 9.375);
        brightStyle = "rgb(" + craterfade2 + ",32," + craterfade + ")";
        darkStyle = "rgb(" + craterfade3 + ",16," + craterfade + ")";
      }
      ctx.fillStyle = surfStyle;
      if (time_count < 1000) approach_size = time_count / 1000;
      else approach_size = 1;
    }
    else ctx.fillStyle = "rgb(48,16,48)";
    ctx.fillRect (0, 0, 480, 640);
    if (time_count < 200) {
      ctx.fillStyle = "rgba(128,64,128,0.03)";
      var cloudy = time_count % 12;
      cloudy = cloudy * 75;
      ctx.fillRect (0, cloudy, 480, Math.random() * 128);
      ctx.fillRect (0, cloudy + 32, 480, Math.random() * 64);
    }
  }

  if (background_type == "Rift" || background_type == "Riftoff") {
    var riftpos = 0;
    if (background_type == "Riftoff") riftpos = time_count - 2605;
    for (var i = 0; i < 3; i++) {
      if (i == 0) ctx.fillStyle = "rgb(26,5,5)";
      else if (i == 1) ctx.fillStyle = "rgb(37,10,10)";
      else ctx.fillStyle = "rgb(48,16,16)";
      for (var j = 0; j < Rift[i].length; j++) {
        if (time_count == 1600) {
          Rift[i][j].x = -20;
          Rift[i][j].y = Rift[i][j].height * j;
        }
        var nextone = j + 1;
        if (nextone == Rift[i].length) nextone = 0;
        if (time_count == 1600) Rift[i][nextone].x = -20;
        ctx.beginPath();
        ctx.moveTo (0, Rift[i][j].y - Rift[i][j].height);
        ctx.lineTo (riftpos + 240 - Rift[i][j].speed * 18 - Rift[i][j].x * Rift[i][j].speed, Rift[i][j].y - Rift[i][j].height);
        ctx.lineTo (riftpos + 240 - Rift[i][j].speed * 18 - Rift[i][nextone].x * Rift[i][j].speed, Rift[i][j].y + 1);
        ctx.lineTo (0, Rift[i][j].y + 1);
        ctx.fill();
        ctx.beginPath();
        ctx.moveTo (riftpos + 240 + Rift[i][j].speed * 18 + Rift[i][j].x * Rift[i][j].speed, Rift[i][j].y - Rift[i][j].height);
        ctx.lineTo (riftpos + 480, Rift[i][j].y - Rift[i][j].height);
        ctx.lineTo (riftpos + 480, Rift[i][j].y + 1);
        ctx.lineTo (riftpos + 240 + Rift[i][j].speed * 18 + Rift[i][nextone].x * Rift[i][j].speed, Rift[i][j].y + 1);
        ctx.fill();
        if (!is_paused) Rift[i][j].y += Rift[i][j].speed;
        if (Rift[i][j].y > 640 + Rift[i][j].height) {
          Rift[i][j].x = Math.random() * 6;
          if (time_count < 1700) {
            Rift[i][j].x = Rift[i][j].x * (time_count - 1600) / 100;
            Rift[i][j].x -= 20;
            Rift[i][j].x += (time_count - 1600) / 5;
          }
          Rift[i][j].y -= 640;
          Rift[i][j].y -= Rift[i][j].height;
        }
      }
    }
  }
  else {
    for (var i = 0; i < Stars.length; i++) {
      if (background_type == "Planet Surface") {
        // Note for future: wave three stays at maximum zoom, wave four will zoom in over 1000 frames
        if (current_wave == 3 && i % 4 == 0) {
          // 1/4 as many craters as stars... and "speed" is used to track crater size rather than movement speed
          
          ctx.fillStyle = brightStyle;
          ctx.beginPath();
          ctx.arc (Stars[i].x, Stars[i].y, Stars[i].speed * approach_size, 0, Math.PI - 0.4, 0);
          ctx.arc (Stars[i].x, Stars[i].y, Stars[i].speed * approach_size * 0.8, Math.PI - 0.6, 0.2, 1);
          ctx.fill();
  
          ctx.fillStyle = darkStyle;
          ctx.beginPath();
          ctx.arc (Stars[i].x, Stars[i].y, Stars[i].speed * approach_size, Math.PI, Math.PI * 2 - 0.4, 0);
          ctx.arc (Stars[i].x, Stars[i].y, Stars[i].speed * approach_size * 0.8, Math.PI * 2 - 0.6, Math.PI + 0.2, 1);
          ctx.fill();

          if (!is_paused) Stars[i].y += approach_size * 9;
          
          if (time_count < 1000 && time_count > 1 && !is_paused) {
            Stars[i].x -= (240 - Stars[i].x) / (150 + time_count / 3);
            Stars[i].y += Stars[i].y / (150 + time_count / 3);
          }
        
          if ((Stars[i].y > 640 + Stars[i].speed ||
              Stars[i].x < 0 - Stars[i].speed * approach_size ||
              Stars[i].x > 480 + Stars[i].speed * approach_size || time_count == 1)
              && background_type2 != "Stop Craters") {
            // Craters that go offscreen are reassigned as the next crater
            Stars[i].speed = Math.random() * 10 + Math.random() * 10 * Math.random() * 9;
            if (time_count < 500) Stars[i].speed *= 1.5;
            if (time_count < 250) Stars[i].speed *= 1.9;
            // First batch of craters is spread randomly immediately
            if (time_count == 1) {
              Stars[i].x = Math.random() * 380 + 50;
              Stars[i].y = Math.random() * 410;
            }
            else {
              Stars[i].y = 0 - Stars[i].speed * approach_size;
              Stars[i].x = Math.random() * (480 + Stars[i].speed) - Stars[i].speed;
            }
          }
        }
      }
      else {
        var fillme = Math.floor (Stars[i].speed * 45) + 50;
    
        if (lives < 1) ctx.fillStyle = "rgb(" + fillme + ",0,0)";
        else if (is_paused) ctx.fillStyle = "rgb(" + fillme + ",0," + fillme + ")";
        else ctx.fillStyle = "rgb(" + fillme + "," + fillme + "," + fillme + ")";
        
        if (background_type == "Warp Two") ctx.fillRect (Stars[i].x, Stars[i].y - 2, 1, 3);
        else ctx.fillRect (Stars[i].x, Stars[i].y, 1, 1);

        if (background_type2 == "Blue Giant") {
          Stars[i].y--;
          if (Stars[i].y < 1) {
            Stars[i].x = Math.random() * 480;
            Stars[i].y += 640;
          }
        }
        else if (background_type == "Horizon") {
          if (Stars[i].y > 640 || time_count == 1) {
            if (time_count == 1) Stars[i].y = Math.random() * 640;
            else Stars[i].y -= 640;
            Stars[i].x = Math.random() * 480;
            Stars[i].speed = Math.random() * 4 + 0.5;
          }
          else Stars[i].y++;
        }
        else {
          Stars[i].y += Stars[i].speed;
          if (background_type == "Warp Two") Stars[i].y += Stars[i].speed;
          if (Stars[i].y > 640) {
            Stars[i].y = 0;
            Stars[i].x = Math.random() * 480;
            Stars[i].speed = Math.random() * 4 + 0.5;
          }
        }
      }
    }
  }
  
  if (background_type == "Horizon") {
    ctx.fillStyle = surfStyle;
    ctx.fillRect (0, time_count, 480, 640);
    ctx.fillStyle = "rgba(128,64,128,0.05)";
    for (var i = 0; i < 14; i++) {
      ctx.fillRect (0, time_count - 8 * i, 480, 8 * i);
    }
  }
  
  if (background_type2 == "Blue Giant") {
    var bluegrad = ctx.createRadialGradient (240, 320, 0, 240, 320, 340);
    bluegrad.addColorStop (0, '#00ccff');
    bluegrad.addColorStop (0.45, '#0099ff');
    bluegrad.addColorStop (1, 'rgba(0,0,0,0)');
    ctx.fillStyle = bluegrad;
    ctx.fillRect (0, 0, 480, 640); 
    
    // First 25 stars double as sunspots
    for (var i = 0; i < 25; i++) {
      var spotx = (Stars[i].x - 240);
      var spoty = (320 - Stars[i].y);
     
      spotx = spotx * Math.cos (Math.abs (spoty) / 240);
      spotx = spotx / 1.55;
      spoty = spoty / 2.1;
      
      var spotdist = Math.sqrt (spotx * spotx + spoty * spoty);
      var spotsize = 7 - spotdist / 30;
      if (spotsize < 1) spotsize = 1;
      var spotgam = 0.2 + spotsize / 20;
      
      ctx.fillStyle = "rgba(0,128,196," + (spotgam / 3) + ")";
      ctx.beginPath();
      ctx.arc (240 + spotx, 320 + spoty, spotsize * (2 + Math.random() * 0.3), 0, Math.PI * 2, 0);
      ctx.fill();
      ctx.fillStyle = "rgba(0,128,196," + (spotgam / 3) + ")";
      ctx.beginPath();
      ctx.arc (240 + spotx, 320 + spoty, spotsize * (1 + Math.random() * 0.3), 0, Math.PI * 2, 0);
      ctx.fill();
    }
    
    if (background_type == "Planet Approach") {
      if (!is_paused) approach_count += (time_count / 300);
      var approach_size = approach_count / (75 - (time_count / 100));
      var approachx = 120 - approach_count / 160;
      var approachy = 100 + approach_count / 100;
      
      ontarget (320, 240, approachx, approachy, approach_size / 20);
      
      ctx.strokeStyle = "rgb(128,64,128)";
      ctx.beginPath();
      ctx.arc (approachx, approachy, approach_size, 0, Math.PI * 2, 0);
      ctx.stroke();
      ctx.fillStyle = "rgb(64,32,64)";
      ctx.beginPath();
      ctx.arc (approachx, approachy, approach_size, 0, Math.PI * 2, 0);
      ctx.fill();
      ctx.strokeStyle = "rgb(52,20,52)";
      ctx.beginPath();
      ctx.arc (approachx + shotx, approachy + shoty, approach_size * .95, 0, Math.PI * 2, 0);
      ctx.stroke();
      ctx.fillStyle = "rgb(48,16,48)";
      ctx.beginPath();
      ctx.arc (approachx + shotx, approachy + shoty, approach_size * .95, 0, Math.PI * 2, 0);
      ctx.fill();
    }
  }
  
  if ((background_type == "Station" || background_type == "Hangar") && Background_elements.length == 0) {
    for (var i = 0; i < 3; i++) {
      Background_elements.push (new Background_element (background_type, 0, 0 - i * 320, 0, 9));
    }
  }
  
  for (var i = 0; i < Background_elements.length; i++) {
    Background_elements[i].draw();
    if (Background_elements[i].update() == 1) dead_bg.push (i);
  }
  for (var i = dead_bg.length - 1; i >= 0; i--) {
    Background_elements.splice (dead_bg[i], 1);
  }
  dead_bg.splice (0, dead_bg.length);
}

// The hero
function draw_ship () {
  if (life_state == "Invincible" && cycle_step % 2 == 0) return;

  // Wings
  ctx.fillStyle = "rgb(200,200,200)";
  ctx.beginPath();
  ctx.moveTo (shipx - 15, shipy + 18);
  ctx.lineTo (shipx - 4, shipy - 12);
  ctx.lineTo (shipx, shipy + 6);
  ctx.moveTo (shipx + 15, shipy + 18);
  ctx.lineTo (shipx + 4, shipy - 12);
  ctx.lineTo (shipx, shipy + 6);
  ctx.fill();
  
  // Body
  ctx.fillStyle = "rgb(255,255,255)";
  ctx.fillRect (shipx - 4, shipy - 12, 8, 23);

  // Jet flame (only when in motion upward, flickers randomly)
  if (vectory < 0 && cycle_step % 2 == 1) {
    ctx.fillStyle = "rgb(255,0,0)";
    ctx.beginPath();
    ctx.moveTo (shipx - 3, shipy + 11);
    ctx.lineTo (shipx + 3, shipy + 11);
    ctx.lineTo (shipx + 5, shipy + 20);
    ctx.lineTo (shipx, shipy + 25 + cycle_step / 2);
    ctx.lineTo (shipx - 5, shipy + 20);
    ctx.fill();
    ctx.fillStyle = "rgb(255,255,0)";
    ctx.beginPath();
    ctx.moveTo (shipx - 2, shipy + 11);
    ctx.lineTo (shipx + 2, shipy + 11);
    ctx.lineTo (shipx + 3, shipy + 15);
    ctx.lineTo (shipx, shipy + 19 + cycle_step / 4);
    ctx.lineTo (shipx - 3, shipy + 15);
    ctx.fill();
  }
  
  // Guns
  ctx.fillStyle = "rgb(128,128,128)";
  ctx.fillRect (shipx - 11, shipy - 10, 2, 20);
  ctx.fillRect (shipx + 9, shipy - 10, 2, 20);
  if (is_firing == 1 && cycle_step % 2 == 0) {
    ctx.fillStyle = "rgb(255,255,128)";
    ctx.fillRect (shipx - 11, shipy - 10, 2, 4);
    ctx.fillRect (shipx + 9, shipy - 10, 2, 4);
    ctx.fillStyle = "rgb(200,200,128)";
    ctx.fillRect (shipx - 11, shipy - 6, 2, 6);
    ctx.fillRect (shipx + 9, shipy - 6, 2, 6);
  }
  
  
  // Moar guns
  ctx.fillStyle = "rgb(192,192,192)";
  ctx.fillRect (shipx - 18, shipy + 5, 2, 14);
  ctx.fillRect (shipx + 16, shipy + 5, 2, 14);
  ctx.fillRect (shipx - 18, shipy + 10, 5, 9);
  ctx.fillRect (shipx + 12, shipy + 10, 5, 9);

  // And another one
  ctx.fillStyle = "rgb(255,255,255)";
  ctx.fillRect (shipx -2, shipy - 22, 4, 17);

  // Cockpit - This is also a graphical representation of the hitbox
  if (challenge_mode == 1) {
    if (cycle_step % 8 < 4) ctx.fillStyle = "rgb(128,0,0)";
    else ctx.fillStyle = "rgb(0,0,0)";
  }
  else ctx.fillStyle = "rgb(200,0,0)";
  ctx.fillRect (shipx - shipwidth, shipy - shipheight, shipwidth * 2, shipheight * 2);
}

// Draw the laserific bullets of great justice
function draw_bullets() {
  for (var i = 0; i < Bullets.length; i++) {
    Bullets[i].draw();
  }
}

// Draw the evil dastardly villains of nastiness
function draw_baddies() {
  for (var i = 0; i < Enemies.length; i++) {
    Enemies[i].draw();
  }
}

// Draw everything
function draw() {
  ctx.fillStyle = "rgb(0,0,0)";
  ctx.fillRect (0, 0, 480, 640);

  draw_bg();
  if (life_state != "Dead") draw_ship();

  draw_bullets();
  draw_baddies();

  ctx.fillStyle = "rgb(0,128,255)";
  ctx.font = "13px Arial";
  ctx.textBaseline = "top";
  ctx.fillText ("Score: " + current_score, 1, 1);
  ctx.fillText ("High: " + high_score, 1, 14);
  ctx.fillText ("Lives: " + lives, 1, 27);
  if (is_paused == 1) {
    ctx.font = "bold 25px Arial";
    ctx.fillText ("Paused", 190, 310);
  }
  if (challenge_mode == 1) {
    ctx.fillText ("CHALLENGE MODE", 350, 1);
  }
  if (is_paused == 1 || (current_wave == 0 && time_count < 150 && challenge_mode != 1)) {
    show_controls();
  }
}

// Calculate enemy AI routines, and then update all entity positions, clear dead dudes and dead bullets and dead you
function enemy_ai() {
  for (var i = 0; i < Enemies.length; i++) {
    if (Enemies[i].update() == 1) {
      dead_baddies.push (i);
    }
    else if (Enemies[i].width != 0 && life_state != "Dead") {
      if (Enemies[i].x + Enemies[i].width > shipx - shipwidth &&
          Enemies[i].x - Enemies[i].width < shipx + shipwidth &&
          Enemies[i].y + Enemies[i].height > shipy - shipheight &&
          Enemies[i].y - Enemies[i].height < shipy + shipheight) {
        if (Enemies[i].type == "Power1") {
          if (challenge_mode != 1 && power1 < 2) {
            if (!is_safari) sfx[3].play();
            power1++;
          }
          else {
            if (!is_safari) sfx[4].play();
            for (var b = 4; b <= 11; b++) {
              Bullets.push (new Bullet (Enemies[i].x, Enemies[i].y, b, 0));
            }
          }
          dead_baddies.push (i);
        }
        else if (Enemies[i].type == "Power2") {
          if (challenge_mode != 1 && power2 < 2) {
            if (!is_safari) sfx[3].play();
            power2++;
          }
          else {
            if (!is_safari) sfx[4].play();
            for (var b = 4; b <= 11; b++) {
              Bullets.push (new Bullet (Enemies[i].x, Enemies[i].y, b, 0));
            }
          }
          dead_baddies.push (i);
        }
        else if (life_state == "Alive") {
          life_state = "Dead";
          is_firing = 0;
          lives--;
          
          if (!is_safari) sfx[2].play();
          if (lives < 1) {
            wait_clear = 1;
            // Game over, man!
            
            createCookie ("high_score", high_score, 365);
            
            Enemies.push (new Enemy ("You Suck", 0, 0, 0, 0));
            if (!is_safari) {
              if (bg_music != 0) tunez[bg_music].pause();
              bg_music = 0;
            }
          }
          Enemies.push (new Enemy ("Splosion", shipx, shipy, vectorx / 2, vectory / 2));
          make_splosion (shipx, shipy, vectorx / 2, vectory / 2, 20, 10, "Splodelet");
          
          if (power1 > 0) Enemies.push (new Enemy ("Power1", shipx, shipy, 0, -10 - Math.random() * 3));
          if (power2 > 0) Enemies.push (new Enemy ("Power2", shipx, shipy, 0, -10 - Math.random() * 3));
          power1 = 0;
          power2 = 0;
        }
      }
    }
  }
  
  for (var i = dead_baddies.length - 1; i >= 0; i--) {
    Enemies.splice (dead_baddies[i], 1);
  }
  dead_baddies.splice (0, dead_baddies.length);

  for (var i = 0; i < Bullets.length; i++) {
    if (Bullets[i].update() == 1) {
      dead_bullets.push (i);
    }
  }

  for (var i = dead_bullets.length - 1; i >= 0;  i--) {
    Bullets.splice (dead_bullets[i], 1);
  }
  dead_bullets.splice (0, dead_bullets.length);

  for (var i = dead_baddies.length - 1; i >= 0; i--) {
    Enemies.splice (dead_baddies[i], 1);
  }
  dead_baddies.splice (0, dead_baddies.length);
  
  shipx += vectorx;
  shipy += vectory;
  if (shipx < shipwidth) shipx = shipwidth;
  else if (shipx > 480 - shipwidth) shipx = 480 - shipwidth;
  if (shipy < shipheight) shipy = shipheight;
  else if (shipy > 640 - shipheight) shipy = 640 - shipheight;
}

// Main engine loop
function timed_count() {
  if (is_paused == 1) draw();
  else {
    draw();

    if (life_state == "Invincible" || life_state == "Dead") {
      state_counter++;
      if (state_counter > 60) {
        if (life_state == "Invincible") life_state = "Alive";
        else if (life_state == "Dead" && lives > 0) {
          life_state = "Invincible";
          shipx = 240;
          shipy = 500;
        }
        state_counter = 0;
      }
    }
    
    cycle_step++;
    if (cycle_step == 32) cycle_step = 0;
    enemy_ai();
    if (wait_clear) {
      if (Enemies.length == 0) {
        wait_clear = 0;
        if (wave_count == Waves[current_wave].length) {
          current_wave++;
          wave_count = 0;
          time_count = 0;
          createCookie ("high_score", high_score, 365);
          createCookie ("challenge_mode_unlocked", challenge_mode_unlocked, 365);
          if (current_wave == Waves.length) {
            current_wave = 0;
          }
        }
      }
    }
    else {
      time_count++;
      while (wave_count < (Waves[current_wave].length) && Waves[current_wave][wave_count].count == time_count) {
        Waves[current_wave][wave_count].generate();
        wave_count++;
      }
      if (wave_count == Waves[current_wave].length) wait_clear = 1;
    }
    if (life_state == "Restart") {
      life_state = "Dead";
      restart_game (5);
    }
    else get_controls();
    if (current_score > high_score) high_score = current_score;
    if (current_score > free_life) {
      free_life += 1000;
      if (challenge_mode != 1) lives++;
    }
  }
}
