/*
 *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, and modified whilst at the
 * University of Manchester.
 *
 * The initial code base is copyright by the respective
 * employers. 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.optionable; // Package name inserted by JPack

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
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;



/**
 * SaveableOptions.java
 *
 * This class is used to initilise and save all of the necessary
 * options required. This class is fully synchronized
 * Created: Tue Feb 02 16:40:27 1999
 *
 * @author Phillip Lord
 * @version $Id: SaveableOptions.java,v 1.16 2001/05/14 17:08:18 lord Exp $
 */

public abstract class SaveableOptions implements SystemListener, OptionHandler
{

  /**
   * We store the options in this hashtable which contains
   * a mapping between the option group name, and the object
   * which represents the options.
   */
  private Hashtable options = new Hashtable();
  /**
   * This stores the optionable objects which will be allocated
   * and change from one run time to the next. 
   */
  private Vector optionObjects = new Vector();
  
  private OptionableExceptionHandler exHandler;
  
  public SaveableOptions( SystemEventProducer producer, 
                          OptionableExceptionHandler exHandler )
  {
    this.exHandler = exHandler;
    producer.addSystemEventListener( this );
  }
  
  protected OptionableExceptionHandler getExceptionHandler()
  {
    return exHandler;
  }
  
  //Interface methods. SystemListener
  public void systemEventOccured( SystemEvent event )
  {
    if ( event.getOption() == SystemEventOption.SYSTEM_SHUTDOWN ){
      //Get the current state of all the optionable objects
      synchronized( FileSaveableOptions.class ){
	//We want to get the options of only those objects which have been created
	//this runtime. Those which havent been created this time (because they 
	//use has acted in a way to force its loading) clearly cant have changed 
	//either so we can ignore them.
	Enumeration enum = optionObjects.elements();
	while( enum.hasMoreElements() ){
	  Optionable opt = (Optionable)enum.nextElement();
	  if( opt.getOptionGroupName() == null ) 
	    throw new RuntimeException( "The group name of an optionable option should not be null" );
	  //Now update the options held in the hashtable
	  //about this class
	  if( opt.getOptions() != null ){
	    options.put( opt.getOptionGroupName(), opt.getOptions() );
	  }
	}
	saveOptions();
      }
    }
  }

  public int systemListenerPriority()
  {
    //A vetoable shut down so that we can report a failure to write to
    //file and inform the user of this event. But after the usable vetoable
    //options
    return 0;
  }
  
  
  public abstract OutputStream getSaveStream() throws IOException;
    
  public abstract InputStream getLoadStream() throws IOException;
  
  private void saveOptions()
  {
    try{
      ObjectOutput out = new ObjectOutputStream( getSaveStream() );
      out.writeObject( options );
    }
    catch( IOException io ){
      if( exHandler.handleSaveException( io ) ){
        saveOptions();
      }
    }
    catch( Exception exp ){
      if( exHandler.handleSaveException( exp ) ){
        saveOptions();
      }
    }
  }

  protected void loadOptions()
  {
    try{
      if( getLoadStream() != null ){
	ObjectInput in = new ObjectInputStream( getLoadStream() );
	options = (Hashtable)in.readObject();
      }
      else{
	options = new Hashtable();
      }      
    }
    catch( ClassNotFoundException cnfe ){
      if( exHandler.handleLoadException( cnfe ) ){
        loadOptions();
      }
    }
    catch( OptionableSourceNotFoundException osnfe ){
      if( exHandler.handleLoadException( osnfe ) ){
        loadOptions();
      }
    }
    catch( IOException io ){
      if( exHandler.handleLoadException( io ) ){
        loadOptions();
      }
    }
    catch( Exception exp ){
      //Caused by there being no config file, ie this is the first run
      if ( exHandler.handleLoadException( exp ) ){
        loadOptions();
      }
    }
  }
    
  //OptionHandler
  public synchronized void addOptionable( Optionable optionable )
  {
    if( Debug.debug ){
      Debug.message( this,  "Registering optionable object " + optionable + " with SavableOptions" );
    }
    //Add the optionable object to vector
    optionObjects.add( optionable );
    //Set the optionables optionHandler to this
    optionable.setOptionHandler( this );
    //Force an option update
    requestOptionUpdate( optionable );
  }

  public synchronized void removeOptionable( Optionable optionable )
  {
    optionObjects.remove( optionable );
  }
  
  public synchronized void requestOptionUpdate( Optionable optionable )
  {
    //Set the options of the optionable object, by removing
    //the options from the stored hashtable.
    optionable.setOptions( options.get( optionable.getOptionGroupName() ) );
  }
  
} // SaveableOptions



/*
 * ChangeLog
 * $Log: SaveableOptions.java,v $
 * Revision 1.16  2001/05/14 17:08:18  lord
 * Added proper error handling to save and restore.
 *
 * Revision 1.15  2001/04/11 17:04:42  lord
 * Added License agreements to all code
 *
 * Revision 1.14  2001/02/19 17:25:48  lord
 * Small bug fix
 *
 * Revision 1.13  2001/01/30 15:29:43  lord
 * Resurrected this class again when I realised that it would be useful
 * after all.
 *
 * Revision 1.12  2001/01/30 13:59:49  lord
 * File renamed from SaveableOptions, and repacked from the
 * photofit directories. I have done as much as I need to get
 * this class recompiling.
 *
 * Revision 1.11  1999/10/14 14:00:41  phillip2
 * Changed packages and a few other bits and bobs
 *
 * Revision 1.11  1999-10-12 15:09:44+01  phillip2
 * Outdated (renamed to 'D:\Home\rcs\D\Home\java\sources\photofit\options\SaveableOptions.java,v').
 *
 * Revision 1.10  1999-10-01 16:53:18+01  phillip2
 * Added some imports due to changed package structure
 *
 * Revision 1.9  1999-09-30 15:11:05+01  phillip2
 * Updated package statement
 *
 * Revision 1.9  1999-09-30 15:05:02+01  phillip2
 * Outdated (renamed to 'D:\Home\rcs\D\Home\java\sources\photofit\options\SaveableOptions.java,v').
 *
 * Revision 1.8  1999-08-04 15:56:47+01  phillip2
 * Added more documentation
 *
 * Revision 1.7  1999-06-11 11:29:17+01  phillip2
 * Removed wildcards from imports
 * Removed reference to PhotoFitStatic
 *
 * Revision 1.6  1999-05-07 17:28:22+01  phillip2
 * Changed System.out to Debug.message
 *
 * Revision 1.5  1999-05-06 14:05:36+01  phillip2
 * Added some minimal exception handling
 *
 * Revision 1.4  1999-04-25 16:47:46+01  phillip2
 * Updated to changed SystemListener interface
 *
 * Revision 1.3  1999-02-15 18:04:24+00  phillip2
 * Removed reference to PhotoFitInstall directory which doesnt exist anymore
 *
 * Revision 1.2  1999-02-10 18:43:32+00  phillip2
 * The handler now forces an update of options and sets the options
 * handler for the optionable options during addOptionable
 *
 * Revision 1.1  1999-02-09 19:08:14+00  phillip2
 * Initial revision
 *
 */
