// Circles.java, version 1.01, October 22, 1999.
// Applet to demonstrate multiple threads.
// 
// Copyright 1999 by Rick Wagner, all rights reserved.

// This applet has four threads running simultaneously with different sleep
// intervals. The slowest thread is for the applet repainting. Each of the
// three Circle objects is running on its own internal thread to change its
// radius.

import java.applet.*;
import java.awt.*;

public class Circles extends Applet implements Runnable
{
  // Instance variables (private where possible)
  private String sVerNum = "1.01";                       // Only constructors can run here
  private Dimension dPanel;                              // The applet panel size
  private Image imOffScreen = null;                      // Offscreen image for double buffering
  private Graphics grOffScreen = null;                   // Offscreen graphics for double buffering
  private Circle c0;
  private Circle c1;
  private Circle c2;
  private Thread tCircles = null;                        // Thread for applet animation (repainting).
  private int iSleepInterval = 50;

  // To allow browsers to get information about the applet:
  public String getAppletInfo()
  {
    return "Circles applet, version " + sVerNum +
           ", by Rick Wagner, copyright 1999, all rights reserved.";
  }

  // Initialize the applet (primarily for building the GUI)
  public void init()
  {
    setBackground(Color.white);
    dPanel = this.size();                               // The applet panel size (set in html code)

    // Create three new Circle objects (defined as a class below):
    c0 = new Circle(5, Color.red, dPanel.width / 4, dPanel.height / 2, 10);
    c1 = new Circle(7, Color.green, dPanel.width / 2, dPanel.height / 2, 20);
    c2 = new Circle(3, Color.blue, 3 * dPanel.width / 4, dPanel.height / 2, 40);
  }

  // Execute this code after initialization (can be used to launch a thread)
  public void start()
  {
    tCircles = new Thread(this);
    tCircles.start();
    c0.start();
    c1.start();
    c2.start();
  }

  public void run()
  {
    while(true)
    {
      try
      {
        tCircles.sleep(iSleepInterval);               // Let other threads do their thing
      }                                              
      catch(InterruptedException e1)
      {
      }
      repaint();
    }
  }

  // Execute this code when the browser leaves the page
  public void stop()
  {
    if (tCircles != null) tCircles.stop();
    tCircles = 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)
  {
    g.clearRect(0, 0, dPanel.width, dPanel.height);  // Necessary with double buffering.
    this.setBackground(Color.white);

    // Ask each of the circles to paint themselves:
    c0.paint(g);
    c1.paint(g);
    c2.paint(g);
  }

  // Implements double buffering
  public void update(Graphics g)
  {
    if (imOffScreen == null)
    {
      // Make sure the offscreen and graphics exist
      imOffScreen = this.createImage(dPanel.width, dPanel.height);
      grOffScreen = imOffScreen.getGraphics();
      grOffScreen.clearRect(0, 0, dPanel.width, dPanel.height);
    }
    this.paint(grOffScreen);
    g.drawImage(imOffScreen, 0, 0, null);
  }

  public boolean mouseDown(Event e, int x, int y)    // For mouse events
  {
    return true;
  }

  public boolean keyDown(Event e, int k)             // For keyboard events
  {
    return true;
  }

} // End of applet Circles class

class Circle implements Runnable
{
  private Point pCenter = null;                                   // Center of the circle.
  private int iInitialRadius = 0;                                 // Initial radius of the circle.
  private int iRadius = 0;                                        // Variable radius of the circle.
  private Color cColor = null;                                    // Color of the circle.
  private int iCounter = 0;                                       // Counter for run function.
  private float sfPi = 0;                                         // Ratio of a circle's circ. to dia.
  private int iSleepInterval = 0;                                 // Sleep interval for run() function.
  private Thread tCircle = null;                                  // Thread for the circle to run in.
    
  Circle(int r, Color c, int x, int y, int s)                     // Constructor.
  {
    iInitialRadius = r;
    cColor = c;
    pCenter = new Point(x, y);
    iSleepInterval = s;
    sfPi = (float) (Math.atan(1) * 4.0);                          // Arctan of 1 is pi / 4.
  }

  public void start()
  {
    tCircle = new Thread(this);                                   // Construct thread.
    tCircle.start();                                              // Start the thread.
  }

  public void stop()
  {
    if (tCircle != null) tCircle.stop();
    tCircle = null;
  }

  public void run()
  {
    float sfAlpha = 0;
    float sfSizeFactor = 0;
    while(true)
    {
      try
      {
        tCircle.sleep(iSleepInterval);               // Let other threads do their thing
      }                                              
      catch(InterruptedException e1)
      {
      }
      iCounter += 3;                                 // Advance three degrees at a time.
      iCounter %= 360;
      sfAlpha = ((float) iCounter) / ((float) 180.0) * sfPi;
      sfSizeFactor = (float) Math.sin(sfAlpha);
      iRadius = iInitialRadius + ((int) (iRadius * sfSizeFactor));
    }
  }

  public void paint(Graphics g)
  {
    g.setColor(cColor);
    g.drawOval(pCenter.x - iRadius, pCenter.y - iRadius, iRadius * 2, iRadius * 2);
  }
} // End of Circle class
