import java.awt.*;

    /* The constructor receives the array of images to be
     * cycled through. The time between frames may be varied
     * externally during the animator's life via its
     * setFrameTime method.
     *
     * Provide for `suspend' and `resume' operations by
     * polling the animationRunning attribute. Objects may suspend
     * and resume the thread in which the animator runs by
     * using its setAnimationRunning mutator.
     */
class ImageAnimator extends Canvas implements Runnable {
    public ImageAnimator(Image[] images){
        setImages(images);
    }

        // Repeatedly select a single image from the images array.
        // Draw this in an offscreen image before painting it on
        // the canvas.
    public void run(){
        // Create a full-size image for double buffering.
        final Dimension size = getSize();
        final Image offscreenImage = createImage(size.width,size.height);
        setOffscreenImage(offscreenImage);
        final Graphics offscreenGraphics = offscreenImage.getGraphics();

        // Obtain details of the list of images.
        final Image[] images = getImages();
        final int numImages = images.length;
        // Which of the multiple images to show next.
        int nextImage = 0;

        while(true){
            try{
                // Clear the previous image and draw the new one off screen.
                offscreenGraphics.clearRect(0,0,size.width,size.height);
                offscreenGraphics.drawImage(images[nextImage],0,0,this);
                // Render it on the canvas.
                paint(getGraphics());

                // Sleep before selecting the next image.
                // This time may be externally varied.
                Thread.sleep(getFrameTime());
                // Make sure it is ok to continue.
                checkStatus();
                // Select the next image.
                nextImage++;
                if(nextImage >= numImages){
                    // Wrap around.
                    nextImage = 0;
                }
            }
            catch(Exception e){
            }
        }
    }

        // Render the offscreen image on the canvas.
    public void paint(Graphics g){
        Image image = getOffscreenImage();
        if(image != null){
            g.drawImage(image,0,0,this);
        }
    }

    public long getFrameTime(){
        return frameTime;
    }

        // Allow the frame time to be set externally.
    public void setFrameTime(long milliseconds){
        if(milliseconds >= 0L){
            frameTime = milliseconds;
        }
    }

    protected Image[] getImages(){
        return images;
    }

    protected void setImages(Image[] i){
        images = i;
    }

    protected Image getOffscreenImage(){
        return offscreenImage;
    }

    protected void setOffscreenImage(Image i){
        offscreenImage = i;
    }

        // Check to see whether we are running or not.
    protected void checkStatus(){
        while(!getAnimationRunning()){
            try{
                wait();
            }
            catch(InterruptedException e){
            }
        }
    }

    public synchronized boolean getAnimationRunning(){
        return animationRunning;
    }

    public void setAnimationRunning(boolean r){
        synchronized(this){
            animationRunning = r;
            if(animationRunning){
                notify();
            }
            else{
                // This will suspend itself by polling via checkStatus.
            }
        }
    }

    // The full set of images to be animated.
    private Image[] images;
    // The offscreen image to be displayed by paint.
    private Image offscreenImage;
    // The time between image changes (milliseconds).
    private long frameTime = 100;
    // Whether the animation is suspended or not.
    private boolean animationRunning = true;
}
