import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
import java.applet.Applet;
import java.applet.AudioClip;

public class BouncingBall extends JApplet implements ActionListener, ChangeListener, ItemListener {

   private double appwidth = 600, appheight = 290;

   private double coefficient, gravity;
   private double horizontalVelocity, verticalVelocity;
   private double xPosition, yPosition;
   private double height;
   private double sleeptime;
   private int bounces;
   private boolean action;

   private JLabel coefficientLabel;
   private JSlider coefficientSlider;
   private JCheckBox soundBox;
   private JButton startButton, resetButton;

   private AudioClip bounce;
   private Image ball;

   public void init() {

      defaultValues();
      gravity = 9.8000000000000007;

      Container c = getContentPane();
      c.setLayout (new FlowLayout());

      coefficientLabel = new JLabel ("Restitution Coefficient (%) : ");
      coefficientSlider = new JSlider (SwingConstants.HORIZONTAL, 0, 100, 30);
      coefficientSlider.addChangeListener (this);
      coefficientSlider.setMajorTickSpacing (20);
      coefficientSlider.setMinorTickSpacing (5);
      coefficientSlider.setPaintTicks (true);
      coefficientSlider.setPaintLabels(true);
      c.add (coefficientLabel);
      c.add (coefficientSlider);

      soundBox = new JCheckBox ("Sound", true);
      soundBox.addItemListener (this);
      c.add (soundBox);

      startButton = new JButton ("Start");
      startButton.addActionListener (this);
      c.add (startButton);

      resetButton = new JButton ("Reset");
      resetButton.addActionListener (this);
      c.add (resetButton);

      bounce = getAudioClip(getDocumentBase(), "bounce.au");
      ball = getImage(getDocumentBase(), "soccerball.gif");

   } // init()

   public void defaultValues() {
      horizontalVelocity = 2.0;
      verticalVelocity = 0.0;
      xPosition = 40.0;
      yPosition = 70.0;
      height = appheight - yPosition;
      bounces = 0;
      sleeptime = 0.10000000000000001;
      action = false;
   }

   public void paint(Graphics g) {
      getContentPane().setBackground (Color.decode("#ccccff"));
      if (action && xPosition + horizontalVelocity < appwidth) {
         try {
            Thread.sleep ((int)(sleeptime * 1000));
         }
         catch (InterruptedException interruptedexception) {
            showStatus (interruptedexception.toString());
         }
         xPosition += horizontalVelocity;
         yPosition += verticalVelocity * sleeptime + 0.5 * gravity * Math.pow (sleeptime, 2);
         verticalVelocity = verticalVelocity + gravity * sleeptime;
         if(yPosition >= appheight) {
            yPosition = appheight;
            verticalVelocity = -getVi (++bounces);
            bounce.play();
         }
         int i = ball.getWidth (this);
         int j = ball.getHeight (this);
         g.drawImage(ball, (int)xPosition, (int)yPosition, i / 2, j / 2, this);
         repaint();
      }
   } // paint()

   public void update(Graphics g) {
      paint(g);
   }

   public double getVi (int i) {
      if (i == 0)
         return 0.0;
      else
         return Math.sqrt (2 * gravity * getH(i));
   }

   public double getH (int i) {
      if (i == 0)
         return height;
      else
         return coefficient * 0.01 * getH(i - 1);
   }

   // Start / reset simulation with buttons.
   public void actionPerformed (ActionEvent e) {
      if (e.getSource() == startButton) {
         coefficient = coefficientSlider.getValue();
         showStatus ("Coefficient of Restitution: " + (int)coefficient + "%");
         action = true;
         repaint();
      }
      if (e.getSource() == resetButton) {
         coefficientSlider.setValue (30);
         defaultValues();
         getContentPane().repaint();
      }
   }

   // Set coefficient of restitution with slider.
   public void stateChanged (ChangeEvent e) {
         coefficient = coefficientSlider.getValue();
         showStatus ("Coefficient of Restitution: " + (int)coefficient + "%");
   }

   // Set sound on/off status with check box.
   public void itemStateChanged (ItemEvent e) {
      if (e.getStateChange() == ItemEvent.SELECTED)
         bounce = getAudioClip (getDocumentBase(), "bounce.au");
      else
         bounce = getAudioClip (getDocumentBase(), "");
   }

} // class BouncingBall
