package bicat.run_machine;

import javax.swing.SwingUtilities;

/**
 * <p>Title: BicAT Tool </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * @author D. Frick, A. Prelic
 * @version 1.0
 *
 **/

public abstract class SwingWorker {

  private Object value; // see getValue(), setValue();

  // ===========================================================================
  /**
   * class to maintain reference to current worker thread under separate
   * synchronization control
   *
   * */
  private static class ThreadVar {
    private Thread thread;
    ThreadVar(Thread t) { thread = t; }
    synchronized Thread get() { return thread; }
    synchronized void clear() { thread = null; }
  }

  private ThreadVar threadVar;

  // ===========================================================================
  /**
   * Start a thread that will call the <code>construct</code> method and then
   * exit.
   *
   * */
  public SwingWorker() {
    final Runnable doFinished = new Runnable() {
      public void run() { finished(); }
    };

    Runnable doConstruct = new Runnable() {
      public void run() {
        try {
          setValue(construct());
        }
        finally {
          threadVar.clear();
        }
        SwingUtilities.invokeLater(doFinished);
      }
    };

    Thread t = new Thread(doConstruct);
    threadVar = new ThreadVar(t);
  }

  // ===========================================================================
  /**
   * Start the woker thread
   *
   * */
  public void start() {
    Thread t = threadVar.get();
    if(t != null) t.start();
  }

  // ===========================================================================
  /**
   * Get the value produced by the worker thread, or null if it hasn't been
   * constructed yet.
   *
   * */
  protected synchronized Object getValue() { return value; }

  // ===========================================================================
  /**
   * Set the value produced by the worker thread.
   *
   * */
  protected synchronized void setValue(Object x) { value = x; }

  // ===========================================================================
  /**
   * Compute the value to be returned by the <code>get</code> method.
   *
   * */
  public abstract Object construct();

  // ===========================================================================
  /**
   * Called on the event dispatching thread (not on the worker thread) after the
   * <code>construct</code> method has returned.
   *
   * */
  public void finished() {}

  // ===========================================================================
  /**
   * A new method that interrupts the worker thread. Call this method to force
   * the worker to stop what it's doing.
   *
   * */
  public void interrupt() {
    Thread t = threadVar.get();
    if(t != null) t.interrupt();
    threadVar.clear();
  }

  // ===========================================================================
  /**
   * Return the value created by the <code>construct</code> method. Returns null
   * if either the constructing thread ir teh current thread was interrupted
   * before a value was produced.
   *
   * @return the value created by the <code>construct</code> method
   *
   * */
  public Object get() {
    while(true) {
      Thread t = threadVar.get();
      if(t == null) return getValue();
      try {
        t.join();
      } catch(InterruptedException e) {
        Thread.currentThread().interrupt(); // propagate
        return null;
      }
    }
  }

  // ===========================================================================

}
