// Hover.java, version 1.04, September 4, 1998.
// Applet to animate a hovering polygon with red points.
// Copyright 1997 and 1998 by Rick Wagner, all rights reserved.

import java.applet.*;
import java.awt.*;

public class Hover extends Applet implements Runnable
{
  // Instance variables (private where possible)
  private String sVerNum = "1.04";                       // Only constructors can run here
  private Polygon Pgon;
  private Polygon OldPgon;
  private int x[];
  private int y[];
  private int iNumPoints = 20;                           // Number of points in the polygon
  private Thread AnimThread = null;
  private Point pPos;                                    // Position of the UFO polygon rectangle
  private Point pCenter;                                 // Center of the polygon
  private Dimension d;                                   // The applet panel size
  private Dimension UFO;                                 // Size of the UFO rectangle
  private Point pGoal;                                   // Goal point for flying to
  private int iDSquared = 0;                             // Square of the distance to the goal
  private boolean bFlying = false;                       // Currently flying to a goal
  private Image offscreen = null;                        // For double buffering
  private int imagewidth = 0;
  private int imageheight = 0;
  private boolean bPleaseStop = false;

  // To allow browsers to get information about the applet:
  public String getAppletInfo()
  {
    return "Hover applet, version " + sVerNum +
           ", by Rick Wagner, copyright 1997 and 1998, all rights reserved.";
  }

  // Initialize the applet (primarily for building the GUI)
  public void init()
  {
    int i;
    setBackground(Color.white);
    x = new int[iNumPoints];                              // Variable array dimensioning,
    y = new int[iNumPoints];                              // can't do this in C++.

    // Here we define the shape of the hovering polygon:
    x[0] = 0; y[0] = 10;
    x[1] = 4; y[1] = 11;
    x[2] = 8; y[2] = 12;
    x[3] = 14; y[3] = 13;
    x[4] = 16; y[4] = 13;
    x[5] = 22; y[5] = 12;
    x[6] = 26; y[6] = 11;
    x[7] = 30; y[7] = 10;
    x[8] = 26; y[8] = 9;
    x[9] = 22; y[9] = 8;
    x[10] = 20; y[10] = 7;
    x[11] = 19; y[11] = 5;
    x[12] = 18; y[12] = 4;
    x[13] = 16; y[13] = 3;
    x[14] = 14; y[14] = 3;
    x[15] = 12; y[15] = 4;
    x[16] = 11; y[16] = 5;
    x[17] = 10; y[17] = 7;
    x[18] = 8; y[18] = 8;
    x[19] = 4; y[19] = 9;

    // Now we scale the polygon to the size we want:
    for (i = 0; i < iNumPoints; i++)
    {
      x[i] = 2 * x[i];
      y[i] = 2 * y[i];
    }
    Pgon = new Polygon(x, y, iNumPoints);                   // Polygon object constructor
    d = this.size();                                        // The applet window size
    UFO = new Dimension(60, 30);                            // The rectangle the object fits in
    for (i = 0; i < Pgon.npoints; i++)
    {
      // Center the polygon in the window:
      Pgon.xpoints[i] += (d.width - UFO.width) / 2;
      Pgon.ypoints[i] += (d.height - UFO.height) / 2;
    }
    // Make a copy of the polygon for later use:
    OldPgon = new Polygon(Pgon.xpoints, Pgon.ypoints, Pgon.npoints);
    pPos = new Point((d.width - UFO.width) / 2, (d.height - UFO.height) / 2);
    pCenter = new Point(d.width / 2, d.height / 2);
    validate();  // Undocumented. Don't know what this is for. Works OK without it.
  }

  // Execute this code after initialization (can be used to launch a thread)
  public void start()
  {
    AnimThread = new Thread(this);
    AnimThread.start();
    this.requestFocus();                  // So we can get keyboard input
  }

  // Execute this code as an applet thread
  public void run()
  {
    Dimension d = this.size();
    int i = 0;
    int x = 1;
    int y = 1;
    int dx = 0;
    int dy = 0;
    while (!bPleaseStop)
    {
      if (bFlying)
      {
        // Make a translation toward the goal:
        dx = pGoal.x - pCenter.x;
        dy = pGoal.y - pCenter.y;
        iDSquared = dx * dx + dy * dy;
        if (iDSquared > 50)
        {
          // We still need to fly
          x = (int) Math.round(5 * ((double) dx) / Math.sqrt((double) iDSquared));
          y = (int) Math.round(5 * ((double) dy) / Math.sqrt((double) iDSquared));
        }
        else
        {
          // Stop flying, we're close enough
          bFlying = false;
        }
      }
      else
      {
        // Make random increments for translation:
        x = (int) Math.round(Math.random() * 2 - 1);
        y = (int) Math.round(Math.random() * 2 - 1);
      }

      // Translate the polygon:
      OldPgon = new Polygon(Pgon.xpoints, Pgon.ypoints, Pgon.npoints);
      Rectangle oldrect = new Rectangle(pPos.x, pPos.y, UFO.width, UFO.height);
      for (i = 0; i < Pgon.npoints; i++)
      {
        Pgon.xpoints[i] += x;
        Pgon.ypoints[i] += y;
      }
      pPos.x = pPos.x + x;
      pPos.y = pPos.y + y;
      pCenter.x = pCenter.x + x;
      pCenter.y = pCenter.y + y;
      if ((pPos.x > (d.width - UFO.width)) || (pPos.x < 0))
      {
        // Return the polygon to the center:
        if (!bFlying)
        {
          x = d.width / 2 - pCenter.x;
          y = 0;
          for (i = 0; i < Pgon.npoints; i++)
          {
            Pgon.xpoints[i] += x;
            Pgon.ypoints[i] += y;
          }
          pPos.x = pPos.x + x;
          pCenter.x = pCenter.x + x;
        }
        bFlying = false;
      }
      if ((pPos.y > (d.height - UFO.height)) || (pPos.y < 0))
      {
        // Return the polygon to the center:
        if (!bFlying)
        {
          x = 0;
          y = d.height / 2 - pCenter.y;
          for (i = 0; i < Pgon.npoints; i++)
          {
            Pgon.xpoints[i] += x;
            Pgon.ypoints[i] += y;
          }
          pPos.y = pPos.y + y;
          pCenter.y = pCenter.y + y;
        }
        bFlying = false;
      }
      // Make sure the offscreen image is created and is the right size
      if ((offscreen == null) || ((imagewidth != d.width) || (imageheight != d.height)))
      {
        if (offscreen != null) offscreen.flush();
        offscreen = this.createImage(d.width, d.height);
        imagewidth = d.width;
        imageheight = d.height;
      }
      // Set up clipping
      Rectangle newrect = new Rectangle(pPos.x, pPos.y, UFO.width, UFO.height);
      Rectangle r = newrect.union(oldrect);
      Graphics g = offscreen.getGraphics();
      g.clipRect(r.x, r.y, r.width, r.height);
      // Draw into the offscreen image:
      this.paint(g);
      // Copy it all at once to the screen, using clipping:
      g = this.getGraphics();
      g.clipRect(r.x, r.y, r.width, r.height);      
      g.drawImage(offscreen, 0, 0, this);
      try
      {
        AnimThread.sleep(5);                                     // So it doesn't go too fast
      }
      catch(InterruptedException e1)
      {
      }
    }
    AnimThread = null;
  }

  // Execute this code when the browser leaves the page
  public void stop()
  {
    if (AnimThread != null) AnimThread.stop();
    AnimThread = null;
  }

  // Execute this code when applet is removed from memory (rarely used)
  public void destroy()
  {
  }

  // The applet runtime interpreter passes g to this applet frame painting function
  public void paint(Graphics g)
  {
    // Code for displaying images or drawing in the applet frame
    // Erase the old polygon:
    g.setColor(getBackground());   // Set color to the background to erase
    g.fillPolygon(OldPgon);

    // Draw the new polygon:
    g.setColor(Color.black);
    g.fillPolygon(Pgon);

    // Draw the saucer lights:
    g.setColor(Color.red);
    int x = pPos.x;
    int y = pPos.y + 20;
    g.drawLine(x, y, x, y);
    x = x + 2;
    g.drawLine(x, y, x, y);
    x = x + 5;
    g.drawLine(x, y, x, y);
    x = x + 9;
    g.drawLine(x, y, x, y);
    x = x + 13;
    g.drawLine(x, y, x, y);
    x = x + 13;
    g.drawLine(x, y, x, y);
    x = x + 9;
    g.drawLine(x, y, x, y);
    x = x + 5;
    g.drawLine(x, y, x, y);
    x = x + 2;
    g.drawLine(x, y, x, y);
  }

  public boolean mouseDown(Event e, int x, int y)
  {
    if (x < UFO.width)
    {
      x = UFO.width;
    }
    if (x > d.width - UFO.width)
    {
      x = d.width - UFO.width;
    }
    if (y < UFO.height)
    {
      y = UFO.height;
    }
    if (y > d.height - UFO.height)
    {
      y = d.height - UFO.height;
    }
    pGoal = new Point(x, y);
    bFlying = true;
    return true;
  }

  public boolean keyDown(Event e, int k)
  {
    // On spacebar, if running, stop it. Otherwise, start it:
    if (k == 32)
    {
      if (AnimThread != null)
      {
        bPleaseStop = true;
        this.showStatus("Hover stopped.");
      }
      else
      {
        bPleaseStop = false;
        start();
        this.showStatus("Hover started.");
      }
    }
    return true;
  }

} // End of applet Hover class
