import java.util.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import javax.swing.*;

// class for the cross hair we want
class CrossHair extends GameObject {

   public CrossHair() {
      noCollide = true;
      type = 0;   // this is critical!
   }

   void draw(Graphics2D g) {
      g.setColor(Color.black);
      g.drawOval(x - 20, y - 20, 40, 40);
      g.drawLine(x, y - 20, x, y + 20);
      g.drawLine(x - 20, y, x + 20, y);
   }
}

// class for donut weapons
class DonutOfDeath extends GameObject {
   public DonutOfDeath(int nx, int ny, double dir, double speed, Image img) {
      x = nx;   y = ny;
      w = img.getWidth(null);
      h = img.getHeight(null);
      rotation = 0;
      v = new Velocity(dir, speed);
      immovable = false;
      noCollide = false;
      stayOnScreen = false;
      graphic = img;
      type = 1;   // this is critical!
   }

   // we don't want donuts to hit homer, so we override the collide method and
   // tell it to skip homer but collide with everything else
   void collide(GameObject other) {
      if (other.type != 0)
         super.collide(other);
   }
}

// class for enemies
class Enemy extends GameObject {
   int hitPoints = 0;
   int numHits = 0;

   public Enemy(int nx, int ny, double dir, double speed, Image img) {
      x = nx;   y = ny;
      // the width and height could come from the image (like they do normally),
      // but this makes the game much more difficult :)
      w = 20;
      h = 20;
      rotation = 0;
      v = new Velocity(dir, speed);
      immovable = false;
      noCollide = false;
      stayOnScreen = true;
      graphic = img;
      type = 2;   // this is critical!
   }

   // if an enemy hits a donut of death (always of type = 1), reduce health
   // otherwise normal collide routine from GameObject class
   void collide(GameObject other) {
      if (other.type == 1) {
         numHits++;
         other.removeMe = true;
         if (numHits >= hitPoints)
            removeMe = true;
      } else
         super.collide(other);
   }

   // override the normal draw routine to include a health bar
   void draw(Graphics2D g) {
      super.draw(g); // call the GameObject draw method

      // now draw a health bar
      int rectHeight = 5;
      int ulX = x - w / 2;
      int ulY = y - h / 2;

      // draw black outline
      g.setColor(Color.black);
      g.drawRect(ulX, ulY, w + 1, rectHeight + 1);

      // draw red bg
      g.setColor(Color.red);
      g.fillRect(ulX + 1, ulY + 1, w, rectHeight);

      // draw green status bar
      g.setColor(Color.green);
      double percent = (double) (hitPoints - numHits) / (double) hitPoints;
      g.fillRect(ulX + 1, ulY + 1, (int) (w * percent), rectHeight);
   }
}


///////////////////////////////////////////////////////////////////////////////
// Main Applet Class
// This is the class that's loaded and controls the program.
///////////////////////////////////////////////////////////////////////////////
public class AngryHomer extends Applet
   implements KeyListener, MouseListener, MouseMotionListener, Runnable { 

   // properties...
   GameEngine game;
   GameObject homer;
   CrossHair cross;
   Enemy abe, burns, ned;
   Image imgHomer, imgDonut, imgAbe, imgBurns, imgNed;

   // This is the thread we use to auto-magically execute the "run" method
   // side-by-side with the rest of the applet.
   Thread runner = null;


   // Method: init (called when applet is first loaded by browser)
   public void init() {

      // load all images used
      imgHomer = GameUtils.loadImage(this, "homer.gif");
      imgDonut = GameUtils.loadImage(this, "donut-small.gif");
      imgAbe   = GameUtils.loadImage(this, "abe.gif");
      imgBurns = GameUtils.loadImage(this, "burns.gif");
      imgNed   = GameUtils.loadImage(this, "ned.gif");


      // Create space area and add it to the display
      game = new GameEngine(this, getSize().width, getSize().height);

      // add homer to center of game scene
      homer = new GameObject(getWidth() / 2, getHeight() / 2, 0, 0, imgHomer);
      homer.immovable = true;
      game.add(homer);

      // add the cross hair
      cross = new CrossHair();
      game.add(cross);

      // now add abe, burns, and ned
      abe   = new Enemy(50,   60,  Math.toRadians(20),  50, imgAbe);
      burns = new Enemy(450, 250, Math.toRadians(200), 150, imgBurns);
      ned   = new Enemy(200, 400,  Math.toRadians(45), 250, imgNed);

      // setup the hit points for each enemy
      abe.hitPoints = 5;
      burns.hitPoints = 10;
      ned.hitPoints = 20;

      game.add(abe);
      game.add(burns);
      game.add(ned);

      // Start listening for events
      addKeyListener(this);
      addMouseListener(this);
      addMouseMotionListener(this);

      // Create the thread (this is what updates the game)
      runner = new Thread(this);
      runner.start();   // this starts the "run" method below
   }


   ///////////////////////////////////////////////////////////////////////////
   // the main game loop...
   ///////////////////////////////////////////////////////////////////////////
   public void run() {

      int count = 0;
      long timeNow, timeBefore;

      // simply loop forever, updating the game
      timeBefore = System.currentTimeMillis();
      while (true) {

         // update the game state (moves all objects and repaints them)
         timeNow = System.currentTimeMillis();
         game.update(getGraphics(),timeNow - timeBefore);
         timeBefore = timeNow;

         // sleep for some time
         try { runner.sleep(30); } catch (Exception e) { }
      }
   }

   ///////////////////////////////////////////////////////////////////////////
   // Handlers for the "KeyListener" interface.  These are the methods that
   // are called anytime the user presses any keys on the keyboard.
   ///////////////////////////////////////////////////////////////////////////

   // Method: this is called whenever the user hits a key on the keyboard
   public void keyTyped(KeyEvent e) { 

      // handle the keybindings appropriately by updating homer's position
      switch (e.getKeyChar()) {
         case 's':
            homer.y += 10;
            break;

         case 'w':
            homer.y -= 10;
            break;

         case 'a':
            homer.x -= 10;
            break;

         case 'd':
            homer.x += 10;
            break;
      }

   }

   // Although we don't use these, we *must* implement them b/c of the
   // KeyListener interface that we "implement".  We don't want to actually
   // do anything for these events, so we leave them empty.
   public void keyPressed(KeyEvent e) { }
   public void keyReleased(KeyEvent e) { }

   ///////////////////////////////////////////////////////////////////////////
   // Handlers for the "MouseMotionListener" interface.  These are the methods that
   // are called anytime the user moves the mouse.
   ///////////////////////////////////////////////////////////////////////////

   // Method: this is called whenever the user moves the mouse.
   public void mouseMoved(MouseEvent e) {

      // Update the location of the crosshair
      cross.x = e.getX();
      cross.y = e.getY();

      // ooooh, eye candy
      homer.rotation = GameUtils.getDirection(homer.x, homer.y, e.getX(), e.getY());
   }

   // We don't use this method, but we must implement it b/c of the
   // MouseMostionListener interface that we implement.
   public void mouseDragged(MouseEvent e) { }


   ///////////////////////////////////////////////////////////////////////////
   // Handlers for the "MouseListener" interface.  These are the methods that
   // are called anytime the user clicks the mouse
   ///////////////////////////////////////////////////////////////////////////

   public void mouseClicked(MouseEvent e) {

      DonutOfDeath donut = new DonutOfDeath(
         homer.x, homer.y,
         GameUtils.getDirection(homer.x, homer.y, e.getX(), e.getY()),
         200,
         imgDonut
      );
      game.add(donut);
   }

   public void mousePressed(MouseEvent e) { }
   public void mouseReleased(MouseEvent e) { }
   public void mouseEntered(MouseEvent e) { }
   public void mouseExited(MouseEvent e) { }
}
