/*
 *This library is free software; you can redistribute it and/or
 *modify it under the terms of the GNU Lesser General Public
 *License as published by the Free Software Foundation; either
 *version 2.1 of the License, or (at your option) any later version.
 *
 *This library is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *Lesser General Public License for more details.
 *
 *You should have received a copy of the GNU Lesser General Public
 *License along with this library; if not, write to the Free Software
 *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* 
 * This software was written by Phillip Lord (p.lord@hgmp.mrc.ac.uk)
 * whilst at the University of Middlesex working as a post-doctoral
 * research fellow. 
 *
 * The initial code base is copyright by Middlesex Univeristy or the
 * Birth Defects Foundation. Modifications to the initial code base are copyright
 * of their respective authors, or their employers as appropriate. 
 * Authorship of the modifications may be determined from the ChangeLog
 * placed at the end of this file
 */

package uk.ac.man.bioinf.apps.invoker; // Package name inserted by JPack

import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import javax.swing.SwingUtilities;
import uk.ac.man.bioinf.apps.systemevents.SystemEvent;
import uk.ac.man.bioinf.apps.systemevents.SystemEventOption;
import uk.ac.man.bioinf.apps.systemevents.SystemEventProducer;
import uk.ac.man.bioinf.apps.systemevents.SystemListener;
import uk.ac.man.bioinf.debug.Debug;
import uk.ac.man.bioinf.util.ExceptionHandler;



/**   
 * InvokerInternalQueue.java
 *
 * This class is used as a helper mechanism for interacting with swing
 * objects. The idea here is that you it you can place an object on
 * this queue, and have a slow method invoked on it in a thread, and 
 * then a fast method in the system event queue, without having to
 * worry about the threads themselves. 
 *
 * This class originally was originally called SlowInternalQueue but
 * Ive changed it because this was only due to historical reasons
 * anyway.
 *
 * As this class contains an internal thread, with a reference to it,
 * it will not garbage collect by itself. It contains a "destroy()"
 * method which allows graceful shutdown. Once this method has been
 * called on an object any attempt to enqueue to the object will throw
 * an exception. 
 *
 * <P> 
 * Created: Mon Dec 07 13:52:32 1998
 * <P> 
 * Compliant: 
 * @author Phillip Lord
 * @version
 */
public class InvokerInternalQueue implements Runnable, SystemListener
{
  private List queue;
  private Thread internalThread;
  private boolean oneAtATime = true;
  //After all the vetoable possibilities have gone
  private int systemPriority = 10;
  //The default exception handler for this queue. This over rides
  //any handlers set for the AnonInvoker.
  private ExceptionHandler handle;
  private boolean destroyed = false;
  private SystemEventProducer producer;
  
  public InvokerInternalQueue( )
  {
    this( new SystemEventProducer(){
	public void addSystemEventListener( SystemListener listener )
	{
	}
	public void removeSystemEventListener( SystemListener listener )
	{
	}
      });
  }
  
  public InvokerInternalQueue( SystemEventProducer producer ) 
  {
    this( producer,
	  Collections.synchronizedList( new LinkedList() ) );
  }
  
  /**
   * A new queue. This queue will listen for SystemClosing events and
   * will shut down its own thread when it hears this. The list
   * parameter needs to be a list which is mutable. This class does
   * NOT provide any synchronisation for the list, so it needs to be
   * synchronised appropriately. It practice this means it should be
   * fully synchronised!
   * @param producer 
   * @param queue
   */
  public InvokerInternalQueue( SystemEventProducer producer, List queue )
  {
    this.queue = queue;
    this.producer = producer;
    internalThread = new Thread( this );
    internalThread.setPriority( Thread.MAX_PRIORITY );
    internalThread.start(); 
    producer.addSystemEventListener( this );
  }

  private boolean cont = true;
  
  public void run()
  {
    while( cont ){
      try
	{
	  //Wait here if qu is empty
	  while( queue.isEmpty() ){
	    waitImpl();
	  }
	  //Otherwise get the next event
	  AnonInvoker event = (AnonInvoker)queue.remove( 0 );
	  
	  //And invoke the slow method (via this slowImpl method
	  //which allows parameter passing.
	  event.slowImpl();
	  //Now invoke the fast method. SlowAnonInvoker already 
	  //implement runnable
	  
	  if ( oneAtATime ){
	    SwingUtilities.invokeAndWait( event );
	  }
	  else{
	    SwingUtilities.invokeLater( event );
	  }
	}
      catch( IndexOutOfBoundsException e )
	{
	  //Doesnt matter here, will just wait till next time around
	  Debug.throwable( this, e );
	}
      catch( InvocationTargetException e )
       	{
       	  Debug.both( this, "Have an exception in the slow internal event queue. Rethrowing it"
       		      , e );
       	  throw new InvokerInternalEventQueueException( e );
	}
      catch( InterruptedException e )
	{
	  //If this interrupt has been signalled by the systemclosing event
	  //then close down, otherwise ignore
	  if ( systemClosing ){
	    cont = false;
	  }
	}
      catch( RuntimeException e ){
	e.printStackTrace();
      }
    }
  }
  
  private synchronized void waitImpl() throws InterruptedException
  {
    //Will be called on the internalThread thread...
    wait();
  }
  
  private synchronized void notifyImpl()
  {
    notifyAll();
  }
  
  public void makeEmpty()
  {
    if( destroyed ) throw new InvokerInternalQueueException( "Invoker queue has been destroyed and should not be used" );
    //Not sure whether this is wise or not. It should 
    //stop the run() method where it lies
    internalThread.interrupt();
    queue.clear();
  }
    
  private synchronized void shutdown()
  {
    destroyed = true;
    systemClosing = true;
    internalThread.interrupt();
    queue.clear();
    
    // The thread will stop itself in the run method if it can. This
    // just makes doubly sure that it does. 
    if( internalThread.isAlive() ) cont = false;
    internalThread = null;
  }
  
  public boolean isDestroyed()
  {
    return destroyed;
  }
  
  /**
   * Gracefully close down the thread
   */
  public synchronized void destroy()
  {
    shutdown();
    producer.removeSystemEventListener( this );
  }
  
  public void enqueue( AnonInvoker event )
  {
    if( destroyed ) throw new InvokerInternalQueueException( "Invoker queue has been destroyed and should not be used" );
    //Set the exception handler for this event.
    if( handle != null ) event.setExceptionHandler( handle );
    queue.add( event );
    notifyImpl();
  }
  
  /**
   * Get the value of oneAtATime.
   * @return Value of oneAtATime.
   */
  public boolean getOneAtATime()
  {
    return oneAtATime; 
  }
  
  /**
   * Set the value of oneAtATime.
   * @param oneAtATime Value to assign to oneAtATime.
   */
  public void setOneAtATime( boolean oneAtATime )
  {
    this.oneAtATime = oneAtATime;
  }
  
  
  public boolean systemClosing = false;
  public synchronized void systemEventOccured( SystemEvent e )
  {
    if ( e.getOption() == SystemEventOption.SYSTEM_SHUTDOWN ){
      shutdown();      
    }
  }

  public int systemListenerPriority()
  {
    return systemPriority;
  }

  public String toString()
  {
    return "InvokerInternalQueue:" + super.toString();
  }
} // InvokerInternalQueue



/*
 * ChangeLog
 * $Log: InvokerInternalQueue.java,v $
 * Revision 1.5  2001/04/11 17:04:42  lord
 * Added License agreements to all code
 *
 * Revision 1.4  2001/01/31 17:52:54  lord
 * Exception handler moved to utils package
 *
 * Revision 1.3  2000/12/05 15:06:13  lord
 * Provided the ability to destroy objects of this class, other than
 * by signalling a system event. Previously there was no other way to
 * kill the internal thread. This has given rise to a new "state" for the
 * objects of the class, namely "destroyed".
 *
 * Revision 1.2  2000/11/08 18:21:53  lord
 * Added default constructor
 * Removed references to thread.stop() which is deprecated
 *
 * Revision 1.1  2000/05/08 16:22:10  lord
 * Initial checkin
 *
 * Revision 1.5  1999-05-14 15:14:20+01  phillip2
 * Now installs an exception handler based on the Debug class
 *
 * Revision 1.4  1999-05-03 23:09:05+01  phillip2
 * Have changed notify into notify all.
 * Changed "if empty" to "while empty"
 * Have removed the "suspend" and "remove" calls from makeEmpty. This is probably
 * unnecessary. Potentially it means that some more events on the queue will run if the
 * thread running makeEmpty is hung after the "interrupt" and before the makeEmpty
 * call. The other problem is that its deadlock prone. It says this of these methods in the
 * documentation. Deadlock occured with HotSpot though not the classic VM.
 *
 * Revision 1.3  1999-04-25 16:49:55+01  phillip2
 * Updated due to changes in SystemListener interface
 *
 * Revision 1.2  1999-03-19 17:22:32+00  phillip2
 * Removed unncessary synchronisation enqueue (the queue itself
 * provides full sync'ing).
 * Added toString method because threads toString is crap in 1.2
 *
 * Revision 1.1  1999-02-02 16:04:10+00  phillip2
 * Initial revision
 *
 */
