import java.awt.*;
import java.applet.*;
import java.net.*;

public class SlideShow extends Applet {
  private Image[] images;
  private String[] text;
  private Label captions;

  private volatile int curFrame;

  private Thread timerThread;
  private volatile boolean noStopRequested;
  private boolean paused;
  private final Object pauseLock = new Object();

  private void printThreadName(String prefix) {
    String name = Thread.currentThread().getName();
    System.out.println(prefix + name);
  }

  public void init() {
    images = new Image[3];
    text = new String[3];
    captions = new Label();
    setLayout(new BorderLayout());
    add(BorderLayout.SOUTH, captions);

    Label name = new Label("by Claude Monet");
    name.setAlignment(Label.CENTER);
    add(BorderLayout.EAST, name);

    URL fig = null;
    try {
      fig = new URL("http://developer.java.sun.com:8080/" +
                   "developer/technicalArticles/Threads/applet/");
    } catch (java.net.MalformedURLException ex) {
      System.out.println("Bad URL");
      return;
    }

    images[0] = getImage(fig, "bordighera.jpg");
    images[1] = getImage(fig, "etretat.jpg");
    images[2] = getImage(fig, "leyden.jpg");
    text[0] = "Garden in Bordighera";
    text[1] = "Rock Arch West of Etretat";
    text[2] = "Bulbfield and Windmill near Leyden";
    printThreadName("init is ");

    startThread();
   }

   private void startThread() {
     paused = true;
     noStopRequested = true;

     // Use this inner class to hide the public run method
     Runnable r = new Runnable() {
       public void run() {
         runWork();
       }
     };
     timerThread = new Thread(r, "Timer");
     timerThread.start();
     printThreadName("startThread is ");
  }

  private void stopThread() {
    noStopRequested = false;
    timerThread.interrupt();
    printThreadName("stopThread is ");
  }

  private void runWork() { // note that this is private
    printThreadName("run is ");
    curFrame = 0;

    try {
        while ( noStopRequested ) {
        waitWhilePaused();

        curFrame = ( curFrame + 1 ) % images.length;
        repaint();

        Thread.sleep(3000);
      }
    } catch ( InterruptedException x ) {
      // reassert interrupt
     Thread.currentThread().interrupt();
     System.out.println("interrupt and return from run");
    }
  }

  private void setPaused(boolean newPauseState) {
    synchronized ( pauseLock ) {
      if ( paused != newPauseState ) {
        paused = newPauseState;
        pauseLock.notifyAll();
      }
    }
  }

  private void waitWhilePaused() throws InterruptedException {
    synchronized ( pauseLock ) {
      while ( paused ) {
        pauseLock.wait();
      }
    }
  }

  public void paint(Graphics g) {
    update(g);
    printThreadName("paint is ");
  }

  public void update(Graphics g) {
    g.drawImage(images[curFrame], 0, 0, this);
    captions.setText(text[curFrame]);
    printThreadName("update is ");
  }

  public void start() {
    setPaused(false);
    printThreadName("start is ");
  }

  public void stop() {
    setPaused(true);
    printThreadName("stop is ");
  }

  public void destroy() {
    stopThread();

    for (int i = 0; i < images.length; i++) {
       images[i].flush();
       images[i] = null;
       text[i] = null;
    }

    images = null;
    text = null;
    printThreadName("destroy is ");
  }
}
