/*
 *  =============================================================        
 *  BouncePanel.java : Implements double buffering to avoid problems
 *  with overlapping circles. Overrides update to avoid flickering
 *  problems.
 *
 *  Written by : Vasilios Lagakos                     March, 2001
 *  ==============================================================       
 */
import javax.swing.*;
import java.awt.*;
import java.util.Vector;

public class BouncePanel extends JComponent implements Runnable {

   private Vector circles;
   private int width, height;
   private Thread animationThread = null;  
   boolean Reset = true;
   Image offScreenImage;
   Graphics2D offScreenGraphics2D;
  
   public BouncePanel() {

	setDoubleBuffered(false);
	circles = new Vector();
   }

   //----------------------------------------------------
   /*** Skip the usual screen-clearing step of update so that there is 
      * no "flicker" between each drawing step.
      */
   public void update(Graphics g){
	paint(g);
   }

   //----------------------------------------------------
   /***  Draw all circles onto an offscreen image and    
      *  then copy them to the display area.
      */
   public void paint(Graphics g) {

      width = getSize().width;
      height = getSize().height;

      // Create off screen image when JApplet starts
      if(offScreenImage == null) {
	 offScreenImage = createImage(width, height);	
	 offScreenGraphics2D = (Graphics2D)offScreenImage.getGraphics();
	 offScreenGraphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
		RenderingHints.VALUE_ANTIALIAS_ON);
      }

      /* If you do not check if the off screen graphics is not null before using 
       * it to draw circles you will get a NullPointerException, a Runtime 
       * exception that occurs when a method tries to access a member of an object
       * through a null reference. 
       */
      if(offScreenGraphics2D != null) {
	 offScreenGraphics2D.clearRect(0, 0, width, height);
	 MovingCircle circle;
    	    for(int i=0; i<circles.size(); i++) {
      	       circle = (MovingCircle)circles.elementAt(i);
      	       circle.draw(offScreenGraphics2D);
            }
	 g.drawImage(offScreenImage, 0, 0, null);	 
      }
   }

   //----------------------------------------------------
   /*** When the "start" button is pressed, start the animation thread if it is 
      * not already started. Either way, add a circle to the Vector of circles 
      * that are being bounced.
      */ 
   public void start() {

      if (Reset == true)
         circles.removeAllElements();
      Reset = false;
      if (circles.size() == 0) {
         animationThread = new Thread(this);
         animationThread.start();
      }
      int radius = 25;
      int x = radius + randomInt(width - 2 * radius);
      int y = radius + randomInt(height - 2 * radius);
      int deltaX = 1 + randomInt(10);
      int deltaY = 1 + randomInt(10);
      circles.addElement(new MovingCircle(x, y, radius, deltaX, deltaY));
   }

   //----------------------------------------------------
   /***  When the "stop" button is pressed, stop
      *  the thread and clear the Vector of circles.
      */
   public void stop() {   
      if (animationThread != null) {
           animationThread = null;
	   Reset = true;
      }
   }

   //----------------------------------------------------
   /** Each time around the loop, call paint and then take a short pause.
    *  The paint method will move the circles and draw them.
    */
   public void run() {
      MovingCircle circle;
      Thread myThread = Thread.currentThread();
      // Really while animationThread not null
      while(animationThread==myThread) { 
	 for(int j=0; j<circles.size();j++) {
	    circle = (MovingCircle)circles.elementAt(j);
	    circle.move(width, height);
	 }
      repaint();
      pause(100);
      }
   }

   //----------------------------------------------------
   // Returns an int from 0 to max (inclusive),
   // yielding max + 1 possible values.
   private int randomInt(int max) {
      double x = Math.floor((double)(max + 1) * Math.random());
      return((int)(Math.round(x)));
   }

   //----------------------------------------------------
   // Sleep for the specified amount of time.
   private void pause(int milliseconds) {
      try {
         Thread.sleep((long)milliseconds);
      } catch(InterruptedException ie) {}
   }
} // end of class BouncePanel

