/*
 *  ====================================================================
 *  DemoAnimation.java : This applet animates a butterfly and from a
 *  java programming perspective is interesting in a couple of ways.
 *  First, the program uses a buffered image and affine transformations
 *  (scaling, translation, rotation) to transform the size and motion
 *  of the butterfly. The graphics animation works by storing the
 *  transformed image in a buffer and flipping it to the screen after
 *  applying the transformation to the buffered image.  Thus, you see
 *  the butterfly moving around and changing size. Buttons are
 *  provided to start and stop the animation.
 *
 *  The second interest aspect of this program is the animation canvas,
 *  which runs as a separate thread of execution. The affine trans-
 *  formation is applied to the image that is stored in the buffered
 *  image.
 *
 *  Adapted from : Pantham S., Pure JFC Swing, 1999.
 *  Modified by : Mark Austin                                March, 2001
 *  ====================================================================
 */

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
import java.net.URL;

public class DemoAnimation extends JApplet {
    DemoAnimationCanvas canvas;
    JButton startButton, stopButton;
    URL codeBase;      // URL for applet codebase

    public void init() {

        // 1. Get the content pane and assign layout

        Container container = getContentPane();

        // 2. Get codebase for applet ....

        codeBase = getCodeBase();

        // 3. Add the canvas with rectangles

        canvas = new DemoAnimationCanvas( codeBase );
        container.add(canvas);

        // 4. Add buttons to start or stop the animation

        startButton = new JButton("Start Animation");
        startButton.addActionListener(new ButtonListener());

        stopButton  = new JButton("Stop Animation");
        stopButton.addActionListener(new ButtonListener());

        JPanel panel = new JPanel();
        panel.add(startButton); panel.add(stopButton);
        container.add(BorderLayout.SOUTH, panel);

    }

    // 5. Button listener

    class ButtonListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            JButton temp = (JButton) e.getSource();

            if (temp.equals(startButton)) {
                canvas.start();
            }
            else if (temp.equals(stopButton)) {
                canvas.stop();
            }
        }
    }
}

// 6. Definition of the canvas that displays animation.

class DemoAnimationCanvas extends JPanel implements Runnable {
    Thread thread;

    Image image;
    BufferedImage bi;

    double x, y, xi, yi; 
    int rotate;
    double scale; int UP = 0; int DOWN = 1;
    int scaleDirection;

    URL butterflyURL;  // URL for butterfly image ...

    // 7. DemoAnimationCanvas constructor ...

    DemoAnimationCanvas( URL codeBase ) {

        setBackground(Color.green);  // For canvas background color
       
        // Construct URL for image icon ....

        try {
           butterflyURL = new URL( codeBase, "images/butterfly.gif" );
        } catch ( java.net.MalformedURLException e ) {
           System.out.println("Badly specified URL!!");
        }

        // Download and save image icon ....

        image = new ImageIcon( butterflyURL ).getImage();

        MediaTracker mt = new MediaTracker( this );
        mt.addImage(image, 1);
        try {
            mt.waitForAll();
        } catch (Exception e) {
            System.out.println("Exception while loading image.");
        }

        // If the image has an unknown width, the image is not created
        // by using the specified file. Therefore exit the program.  

        if (image.getWidth(this) == -1) {
            System.out.println("*** Make sure you have the image "
                + "(butterfly.gif) file in the same directory.***");
            System.exit(0);
        }

        rotate = (int) (Math.random() * 360);
        scale = Math.random() * 1.5;
        scaleDirection = DOWN;

        xi = 50.0; yi = 50.0;

    }

    // This method computes the step size for animation.

    public void step( int w, int h ) {
        // upgrade the translation coordinates
        x += xi; y += yi;

        // the x and y exceed the dimensions of canvas
        if (x > w) {
            x = w - 1;
            xi = Math.random() * -w/32;
        }
        if (x < 0) {
            x = 2;
            xi =  Math.random() * w/32;
        }
        if (y > h) {
            y = h - 2;
            yi = Math.random() * -h/32;
        }
        if (y < 0) {
            y = 2;
            yi = Math.random() * h/32;
        }

        // upgrade the rotation coordinates

        if ((rotate += 5) == 360) {
            rotate = 0;
        }

        // upgrade the scaling coordinates depending on the
        // increase or decrease in size. If the increase in size
        // exceeds a limit of 1.5, decrease the size. If the
        // decrease in size falls below 0.5, increase the size.

        if (scaleDirection == UP) {

            if ((scale += 0.5) > 1.5) {
                scaleDirection = DOWN;
            }

        }

        // upgrade the scaling coordinates

        else if (scaleDirection == DOWN) {
            if ((scale -= .05) < 0.5) {
                scaleDirection = UP;
            }
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Dimension d = getSize();

        bi = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D big =  bi.createGraphics();

        step(d.width, d.height);

        AffineTransform at = new AffineTransform();
        at.setToIdentity();
        at.translate(x, y);
        at.rotate(Math.toRadians(rotate));
        at.scale(scale, scale);
        big.drawImage(image, at, this);

        Graphics2D g2D = (Graphics2D) g;
        g2D.drawImage(bi, 0 , 0, null);

        big.dispose();
    }

    // Starts the thread

    public void start() {
        thread = new Thread(this);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread.start();
    }

    // Stops the thread

    public void stop() {
        if (thread != null)
            thread.interrupt();
        thread = null;
    }

    // Runs the thread

    public void run() {
        Thread me = Thread.currentThread();
        while (thread == me) {
            repaint();
        }
        thread = null;
    }
}
