import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;

public class FallingBall extends JPanel implements ActionListener {
    int n = 0;
    int MAX = 200;
    double time = 0;
    javax.swing.Timer timer;
    int interval = 40;
    double g = 9.8;
    double topY = 100*Math.ceil(position(MAX*interval/1000)/100);
    int lastSec = -1;
    Vector balls;
    Color oldBall = new Color(0.5f, 0.5f, 1f);
    Color currentBall = new Color(1f, 0.5f, 0.5f);
    AffineTransform transform;

    public FallingBall() {
        setBackground(Color.white);
	timer = new javax.swing.Timer(interval, this);
	balls = new Vector();
    } 

    public double position(double t) {
	return g*t*t/2;
    }

    public void actionPerformed(ActionEvent e) {
	n++;
	time = n*interval/1000.0;
	if (position(time) > topY+100) timer.stop();
	repaint();
    }

    public void start() {
	if (!timer.isRunning() && position(time) < topY) timer.start();
    }

    public void stop() {
	if (timer.isRunning()) timer.stop();
    }

    public void reset(){
	stop();
	time = 0;
	n = 0;
	balls = new Vector();
	lastSec = -1;
	repaint();
    }


    public void paintComponent(Graphics gfx) {
	// usual stuff
        super.paintComponent(gfx);
        Graphics2D g = (Graphics2D) gfx;
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_ON);


	Rectangle r = getBounds();
	transform = new AffineTransform();
	transform.translate(r.width/2 - 20, 0);
	transform.scale(1, getBounds().height/topY);
	GeneralPath ticks = new GeneralPath();
	ticks.moveTo(0, 0);
	ticks.lineTo(0, (float) topY);
	for (float y = 0; y <= topY; y+=10) {
	    ticks.moveTo(0, y);
	    ticks.lineTo(5, y);
	}
	for (float y = 0; y <= topY; y+=50) {
	    ticks.moveTo(0, y);
	    ticks.lineTo(10, y);
	}
	g.draw(transform.createTransformedShape(ticks));
	
	int sec = (int) Math.floor(time);
	if (sec > lastSec) {
	    balls.addElement(new Point2D.Double(30, position(sec)));
	}
	for (int i = 0; i < balls.size(); i++) {
	    Point2D.Double ball = (Point2D.Double) balls.elementAt(i);
	    drawBall(g, ball, oldBall);
	}
	drawBall(g, new Point2D.Double(30, position(time)), currentBall);
    }

    public void drawBall(Graphics2D g, Point2D.Double p, Color c) {
	Point2D.Double tp = (Point2D.Double) transform.transform(p, null);
	Ellipse2D.Double ball = new Ellipse2D.Double(tp.x - 3, tp.y - 3, 6, 6);
	g.setPaint(c);
	g.fill(ball);
	g.setPaint(Color.black);
	g.draw(ball);
    }

    public static void main(String[] args) {
        FallingBall ball = new FallingBall();
        ball.setPreferredSize(new Dimension(200, 600));

	JPanel buttonPanel = new JPanel();
	JButton rotate = new JButton("Start");
	JButton stop = new JButton("Stop");
	JButton reset = new JButton("Reset");
	buttonPanel.add(rotate);
	buttonPanel.add(stop);
	buttonPanel.add(reset);

	FallingBallControl bc = new FallingBallControl(ball);
	
	rotate.addActionListener(bc);
	stop.addActionListener(bc);
	reset.addActionListener(bc);

        JFrame frame = new JFrame("FallingBall");
	frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(ball, BorderLayout.CENTER);
        frame.getContentPane().add(buttonPanel, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }
}

    class FallingBallControl implements ActionListener {
	FallingBall ball;
	FallingBallControl(FallingBall b) {
	    ball = b;
	}
	public void actionPerformed(ActionEvent event) {
	    String actionCommand = event.getActionCommand();
	    if (actionCommand.equals("Start")) ball.start();
	    else if (actionCommand.equals("Reset")) ball.reset();
	    else if (actionCommand.equals("Stop")) ball.stop();
	}
	
    }	
        
        
                                         
