// bouncingBall02.java   last mod 26 JAN 2012 by Ed James
// NOTE: ball doesn't recognize that screen size changes.
// NOTE: serves as example of thread code

import java.awt.*;
import java.awt.event.*;          // for the window adapter
import javax.swing.JFrame;        // top level GUI widget
import javax.swing.JToolBar;      // toolbar/buttonbar for these buttons:
import javax.swing.JButton;       // for pause, resume, and reverese buttons
import javax.swing.border.*;      // for getBorder()

class bouncingBall02Frame extends JFrame implements Runnable {
   public static final long serialVersionUID = 448L; // not currently used

   Thread bouncingBall02Thread;

   BorderLayout myBorderLayout;

   JToolBar myToolBar;
   JButton myPauseButton;   // pause the animation
   JButton myResumeButton;  // resume the animation
   JButton myReverseButton; // reverse the animation

   bouncingBall02Canvas myCanvas;

   Image myImage;
   Graphics myGraphics;  //offscreen
   Insets myInsets;

   public bouncingBall02Frame () {  // JFrame constructor
      super ();  // need to explain why I need this, or if I do.

      setTitle ("Bouncing Ball Demo");
      setSize (500,300);

      setBackground (Color.black);
      setForeground (Color.red);

      new Thread (this).start();  // create a new Thread, start my run() method
      setVisible (true);
      }

   void buildGUI () {
      myBorderLayout = new BorderLayout ();
      setLayout (myBorderLayout);

      myCanvas = new bouncingBall02Canvas ();
      add ("Center", myCanvas);

      myToolBar = new JToolBar();

      myPauseButton   = new JButton ("Pause");
      myResumeButton  = new JButton ("Resume");
      myReverseButton = new JButton ("Reverse");

      myToolBar.add (myPauseButton);
      myToolBar.add (myResumeButton);
      myToolBar.add (myReverseButton);

      add ("South", myToolBar);
      }

   void addHierarchyBoundsListener () {
      getContentPane().addHierarchyBoundsListener (new HierarchyBoundsListener () {
         @Override
         public void ancestorMoved (HierarchyEvent HE1) {
			
            }
         @Override
         public void ancestorResized (HierarchyEvent HE2) {
            myCanvas.makeImage ();   // kludge - what about old image?
            }
         });
      }

   void addWindowListener () {
      try {
         addWindowListener (new WindowAdapter() {
            public void windowClosing (WindowEvent myEvent) {
           // maybe call destroy() here?
               System.exit (0);
               }
            });
         }
      catch (java.security.AccessControlException ACE2) {
         System.out.println("Error 2: " + ACE2.getMessage());
         }
      }

   public void run() {
      System.out.println ("bouncingBall02Frame.run() started\n");
      Thread thisThread = Thread.currentThread();

      while (bouncingBall02Thread == thisThread) {
         myCanvas.repaint ();
         try { 
            Thread.sleep(10);
            }
         catch (InterruptedException IE1) {
            System.out.println ("ERROR 1: " + IE1.getMessage());
            }
         }
      System.out.println ("bouncingBall02Frame.run() done.\n");
      }

   public void start() {
      System.out.println ("bouncingBall02Frame.start() started\n");
      if (bouncingBall02Thread == null) {
         bouncingBall02Thread = new Thread (this);
         bouncingBall02Thread.start();
         System.out.println("new bouncingBallThread created and started\n");
         }
      }

   } //===== end of class bouncingBall02Frame

class bouncingBall02Canvas extends Canvas {
   public static final long serialVersionUID = 439L; // not currently used
      Image myOffscreenImage;
      Graphics myOffscreenGraphics;

      int minX =   0;  // left limit for ball location
      int minY =   0;  // upper limit for ball location
      int maxX = 500;  // right limit for ball location - should tie to frame size
      int maxY = 300;  // lower limit for ball location - should tie to frame size
      int curX =  20;  // current ball location x-coordinate
      int curY =  20;  // current ball location y-coordinate

      int minR =   1;  // minimum ball size allowed
      int maxR =  30;  // maximum ball size allowed
      int curR =   1;  // current ball size

      int incX =   1;  // change in ball x-coordinate per thread cycle
      int incY =   1;  // change in ball y-coordinate per thread cycle
      int incR =   1;  // change in ball size per thread cycle

   bouncingBall02Canvas () {
      System.out.println ("bouncingBall02Canvas constructor started");
      setBackground (Color.black);
      }

   public void update (Graphics g) {
      paint (g);
      }

   public void paint (Graphics g) {
    //System.out.println ("bouncingBall02Canvas.paint() started");
      if (myOffscreenImage == null)  // maybe move to start()?
         makeImage ();

//    blank out the current ball at the current position:
      myOffscreenGraphics.setColor (Color.black);
      myOffscreenGraphics.fillOval (curX, curY, curR, curR);

//    compute the next ball position:
      if      (curX+curR >= maxX) incX = -1;
      else if (curX      <= minX) incX = 1;
      curX += incX;

      if      (curY+curR >= maxY) incY = -1;
      else if (curY      <= minY) incY = 1;
      curY += incY;

//    compute the next ball size:
      if      (curR >= maxR) incR = -1;
      else if (curR <= minR) incR = 1;
      curR += incR;

//    draw the new ball at the new position:
      myOffscreenGraphics.setColor (Color.red);
      myOffscreenGraphics.fillOval (curX, curY, curR, curR);

      g.drawImage (myOffscreenImage, 0, 0, this);
    //myOffscreenGraphics.dispose();
      }

   void makeImage () {
   // This is called when the program starts, and when the main Frame size changes
      maxX = getSize().width;
      maxY = getSize().height;
      myOffscreenImage    = createImage (maxX, maxY);
      myOffscreenGraphics = myOffscreenImage.getGraphics ();
      }

   } //===== end of class bouncingBall02Canvas

public class bouncingBall02 extends java.applet.Applet {
   public static final long serialVersionUID = 445L; // not currently used
   public static bouncingBall02Frame myFrame;

   public void init () {
// NOTE: This is the starting point when this is run as an Applet
      myFrame = new bouncingBall02Frame ();
      myFrame.buildGUI ();
      myFrame.addHierarchyBoundsListener ()
      myFrame.start ();
   }

   public static void main (String[] args) {
// NOTE: This is the starting point when this is run as an Application
      myFrame = new bouncingBall02Frame ();
      myFrame.addWindowListener ();
      myFrame.buildGUI ();
      myFrame.addHierarchyBoundsListener ();
      myFrame.start ();  // same start() as in the Applet version
      }
   }