// (c) MIT 2003.  All rights reserved.

// $Id: BasicModule.java,v 1.23 2003/03/06 00:40:40 moltmans Exp $
// Time-stamp: <2001-03-12 13:24:18 moltmans>
package rec.sys;


////////////////////////////////////////////////////////////////////////////////
// $Id: BasicModule.java,v 1.23 2003/03/06 00:40:40 moltmans Exp $
// FILE:	BasicModule.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
//              Luke Weisman
//
// DATE:	09-XX-98
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Drawing recognition
//
// HISTORY:     06-XX-98 archon
//              Adapted from my RecognitionContext class
//
//              11-05-98 archon
//              Fixed unbindWidget array index errors
//
////////////////////////////////////////////////////////////////////////////////


import java.util.*;
import java.io.*;
import rec.util.*;
import rec.speech.*;

public class BasicModule implements Module
{
  protected String m_Name;

  protected boolean m_isEnabled;

  protected Id m_Id;

  protected HashSet m_Recognizers;

  protected HashSet m_Reasoners;

  protected HashSet m_Grammars;

  protected HashSet m_Modules;

  /**
   * This maps from widget types to the set of recognizers that take
   * that type as input.
   **/
  private Hashtable m_RecognizerTable;

  /**
   * This maps from widget types to the set of reasoners that list
   * that type as stronger than another
   */
  private Hashtable m_StrongerTable;
  private Hashtable m_WeakerTable;
  
  private HashSet m_FastRecognizers;

  public BasicModule( String moduleName )
  {
    m_Name = new String( moduleName );
    m_Id = new Id();

    m_isEnabled = true;

    m_Recognizers = new HashSet();
    m_FastRecognizers = new HashSet();
    m_Reasoners = new HashSet();
    m_Grammars = new HashSet();
    m_Modules = new HashSet();
    m_StrongerTable = new Hashtable();
    m_WeakerTable = new Hashtable();

    m_RecognizerTable = new Hashtable();
  }

  /////////////////////////////////////////////////////////////////////////////

  public String getName()
  {
    return( new String( m_Name ) );
  }

  public Id getId( )
  {
    return m_Id;
  }

  public String toString()
  {
    return getName() + ":" + getId();
  }

  /////////////////////////////////////////////////////////////////////////////

  public boolean isEnabled()
  {
    return( m_isEnabled );
  }

  public void enable()
  {
    //    Assert.note( !m_isEnabled, "module is already enabled" );
    m_isEnabled = true;

    setActiveGrammars( true );
    setActiveModules( true );
  }

  public void disable()
  {
    //    Assert.note( m_isEnabled, "module is already disabled" );
    m_isEnabled = false;

    setActiveGrammars( false );
    setActiveModules( false );
  }


  /////////////////////////////////////////////////////////////////////////////

  
  /**
   * Adds a new widget into an already existing relationship graph
   */
  public void reasonOnWidget( Surface s, Widget nw,
			      SimpleGraph relativeNodes )
  {
    // get the reasoners which pertain to this widget
    Hashtable strong = getStrongReasoners( nw );
    Hashtable weak = getWeakReasoners( nw );
    
    Iterator rel_it = relativeNodes.looseObjects();

    Collection strongEdges = new HashSet();
    Collection weakEdges = new HashSet();

    
    while ( rel_it.hasNext() ) {
      Object o = rel_it.next();
      Assert.check( o instanceof Widget, o + " is of class " +
		    o.getClass().getName() + " and is not a Widget" );
      Widget w = (Widget)o;

      Collection temp = (Collection)strong.get( w.getClass() );
      Collection applicable = new HashSet();

      if ( temp != null ) {
	applicable.addAll( temp );
      }

      Iterator isa_it = w.isa().iterator();
      while ( isa_it.hasNext() ) {
	Class cl = (Class)isa_it.next();
	Collection col = (Collection)strong.get( cl );
	if ( col != null ) {
	  applicable.addAll( col );
	}
      }


      Iterator appl_it = applicable.iterator();
      while ( appl_it.hasNext() ) {
	Reasoner r = (Reasoner)appl_it.next();
	// if we have a match, add an edge to the graph
	if ( r.match( s, nw, w ) ) {
	  strongEdges.add( w );
	}
      }
      
      
      // now repeat the process for the weak side
      
      temp = (Collection)weak.get( w.getClass() );
      applicable = new HashSet();

      if ( temp != null )
	applicable.addAll( temp );
      

      isa_it = w.isa().iterator();
      while ( isa_it.hasNext() ) {
	Collection col = (Collection)weak.get( (Class)isa_it.next() );
	if ( col != null ) {
	  applicable.addAll( col );
	}
      }
      
      appl_it = applicable.iterator();
      while ( appl_it.hasNext() ) {
	Reasoner r = (Reasoner)appl_it.next();
	// if we have a match, add an edge to the graph
	if ( r.match( s, w, nw ) ) {
	  weakEdges.add( w );
	}
      }
    }

    Iterator it = strongEdges.iterator();
    while ( it.hasNext() ) {
      Object obj = it.next();
      relativeNodes.addEdge( nw, obj );
    }

    it = weakEdges.iterator();
    while ( it.hasNext() ) {
      Object obj = it.next();
      relativeNodes.addEdge( obj, nw );
    }
  }

  /**each reasoner is hashed twice: once on the better type, and once
   * on the worse type.
   */
  public void add( Reasoner r )
  {
    Assert.check( ! m_Reasoners.contains( r ), "reasoner with id = " +
		  r.getId() + " is already in module " + m_Name );
    
    m_Reasoners.add( r );

    Iterator strong_it = r.getStrongerTypes();
    Iterator weak_it = r.getWeakerTypes();

    while ( strong_it.hasNext() ) {
      String strong_name = (String)strong_it.next();
      while ( weak_it.hasNext() ) {
	String weak_name = (String)weak_it.next();
	bindDoubleWidget( r, strong_name, weak_name, m_StrongerTable );
	bindDoubleWidget( r, weak_name, strong_name, m_WeakerTable );
      }
      weak_it = r.getWeakerTypes();
    }

    // some debug stuff
    
    Class c = null;
    try {
      c = Class.forName( "rec.simulate.widgets.wm.WMSimRod" );
    }
    catch( ClassNotFoundException e ) {

      Assert.error( "bad/unrecognized widget class name" );
    }

    Hashtable weaktest = (Hashtable)m_WeakerTable.get( c );
    if ( weaktest == null ) {
      weaktest = new Hashtable();
    }    
  }

  public void remove( Reasoner r )
  {

    m_Reasoners.remove( r );

    Iterator strong_it = r.getStrongerTypes();
    Iterator weak_it = r.getWeakerTypes();

    while ( strong_it.hasNext() ) {
      String strong_name = (String)strong_it.next();
      while ( weak_it.hasNext() ) {
	String weak_name = (String)weak_it.next();
	unbindDoubleWidget( r, strong_name, weak_name, m_StrongerTable );
	unbindDoubleWidget( r, weak_name, strong_name, m_WeakerTable );
      }
    }

  }
  

  //////////////////////////////////////////////////////////////////////
  /**
   * Adds recognizer.
   */
  public void add( Recognizer r )
  {
    
    Assert.check( ! m_Recognizers.contains( r ),
		  "recognizer with id = " + r.getId()
                  + " is already in module " + m_Name );
    
    m_Recognizers.add( r );

    if( r instanceof FastRecognizer ) {
      Console.writeln( "Adding fast recognizer " + r );
      m_FastRecognizers.add( r );
      return;
    }
    
    Iterator e = r.getInputTypes();

    while( e.hasNext() ) {
      bindSingleWidget( r, (String)e.next(), m_RecognizerTable );
    }

    Console.writeln( r.getClass().getName() + " installed" );
  }

  /**
   * Remove recognizer.
   */
  public void remove( Recognizer r )
  {
    Assert.check( m_Recognizers.contains( r ),
		  "recognizer with id = " + r.getId()
                  + " is not already in module " + m_Name );

    m_Recognizers.remove( r );

    if( r instanceof FastRecognizer ) {
      m_FastRecognizers.remove( r );
      return;
    }

    // e cannot be null because we added the recognizer in add()
    Iterator it = r.getInputTypes();

    while( it.hasNext() ) {
      unbindSingleWidget( r, (String)it.next(), m_RecognizerTable );
    }

    Console.writeln( r.getClass().getName() + " uninstalled" );
  }

  /**
   * get all the rules such that w is on the strong side
   */
  public Hashtable getStrongReasoners( Widget w )
  {

    Hashtable temp = (Hashtable)m_StrongerTable.get( w.getClass() ); 
    
    Hashtable ht = new Hashtable();

    if( temp != null ) {
      Iterator temp_it = temp.keySet().iterator();
      while ( temp_it.hasNext() ) {
	Object o = temp_it.next();
	Collection col = (Collection)ht.get( o );
	if ( col == null ) {
	  col = new HashSet();
	  ht.put( o, col );
	}
	col.addAll( (Collection)temp.get( o ) );
      }
    }
    
    // now add in all the things from the "isa" relation
    Collection types = w.isa();
    Iterator types_it = types.iterator();
    while ( types_it.hasNext() ) {
      Hashtable str = (Hashtable)m_StrongerTable.get((Class)types_it.next());
      if ( str != null ) {
	Iterator str_it = str.keySet().iterator();

	while ( str_it.hasNext() ) {
	  Object o = str_it.next();
	  Collection col = (Collection)ht.get( o );

	  if ( col == null ) {
	    col = new HashSet();
	    ht.put( o, col );
	  }

	  col.addAll( (Collection)str.get( o ) );
	}  
      }
    }

    return ht;
  }

  /**
   * get all the rules such that w is on the weak side
   */
  public Hashtable getWeakReasoners( Widget w )
  {

    Hashtable temp = (Hashtable)m_WeakerTable.get( w.getClass() );

    Hashtable ht = new Hashtable();
    
    if( temp != null ) {
      Iterator temp_it = temp.keySet().iterator();
      while ( temp_it.hasNext() ) {
	Object o = temp_it.next();
	Collection col = (Collection)ht.get( o );
	if ( col == null ) {
	  col = new HashSet();
	  ht.put( o, col );
	}
	col.addAll( (Collection)temp.get( o ) );
      }
    }

    // now add in all the things from the "isa" relation
    Collection types = w.isa();
    Iterator types_it = types.iterator();
    while ( types_it.hasNext() ) {
      Class c = (Class)types_it.next();
      Hashtable wea = (Hashtable)m_WeakerTable.get( c );
      if ( wea != null ) {
	Iterator wea_it = wea.keySet().iterator();

	while ( wea_it.hasNext() ) {
	  Object o = wea_it.next();
	  Collection col = (Collection)ht.get( o );

	  if ( col == null ) {
	    col = new HashSet();
	    ht.put( o, col );
	  }

	  col.addAll( (Collection)wea.get( o ) );
	}  
      }
    }
    
    return ht;
  }
  
  public Iterator getReasoners()
  {
    return m_Reasoners.iterator();
  }
  
  /** Get all of the normal (as opposed to fast) recognizers.  **/
  public Iterator getRecognizers( Widget w )
  {
   Collection col = (Collection)m_RecognizerTable.get( w.getClass() );

    if( col == null )
      col = new HashSet();

    return( col.iterator() );
  }

  public Iterator getFastRecognizers() 
  {
    return( m_FastRecognizers.iterator() );
  }

  /** Get both the fast and normal recognizers.  **/
  protected Iterator getAllRecognizers() 
  {
    return m_Recognizers.iterator();
  }

  public Iterator getGrammars()
  {
    return m_Grammars.iterator();
  }
  
  

  ////////////////////////////////////////////////////////////////////////////

  /**
   * Using surface s, this recognizes widget w 
   *
   * @return false if recognition should continue beyond this module;
   *     otherwise, true.
   */
  public boolean recognizeWidget( Surface s, Widget w )
  {
    Iterator e2 = getRecognizers( w );
    
    if( e2 != null ) {

      while( e2.hasNext() ) {

	Recognizer r = (Recognizer)e2.next();

	//Console.writeln( "    rec: " + r + ":" + r.getId() );

	boolean canRun = true;
	Iterator outputTypes = r.getOutputTypes();
	while( outputTypes.hasNext() && canRun ) {
	  String o = (String)outputTypes.next();
	  if ( s.banned( o ) ) {
	    canRun = false;
	  }
	}

	// catch any errors that occur in the recognizers so that
	// they don't break the whole recognition process...
	try {
	  if( canRun &&  r.recognize( s, w ) ) {

	    //
	    // MDM
	    //
	    // call onRecognize handler
	    //
	    // defined in subclasses
	    onRecognize( r, s, w );

	    s.endRecognition();
	  }
	}
	catch( Exception e ) {
	  Assert.note( "Exception occured while running: " + r );
	  Assert.note( e.toString() );
	  StringWriter write = new StringWriter();
	  e.printStackTrace( new PrintWriter( write ) );
	  Console.writeln( write.toString() );
	  System.out.println("Exception: " + e +
			     " occured while running: " + r );
//  	  DebugWindow.addLine( "Warning", "Exception: " + e +
//  			       " occured while running: " + r );
//  	  DebugWindow.addLine( "Warning", write );
	  
	}
      }
    }

    return( false );
  }

  /** defined in subclasses */
  public void onRecognize( Recognizer r, Surface s, Widget w )
  {
  }

  ////////////////////////////////////////////////////////////////////////////

  private void bindDoubleWidget( Reasoner r, String widgetName1,
				 String widgetName2, Hashtable table )
  {
    Class c1 = null;

    try {
      c1 = Class.forName( widgetName1 );
    }
    catch( ClassNotFoundException e ) {
      Assert.error( "bad/unrecognized widget class name " + widgetName1
		    + " (check packaging?)" );
    }
    
    Class c2 = null;

    try {
      c2 = Class.forName( widgetName2 );
    }
    catch( ClassNotFoundException e ) {
      Assert.error( "bad/unrecognized widget class name " + widgetName2
		    + " (check packaging?)" );
    }
    
    
    Hashtable recs = (Hashtable)table.get( c1 );
    
    if ( recs == null ) {
      recs = new Hashtable();
      table.put( c1, recs );
    }
    
    Collection recs2 = (Collection)recs.get( c2 );

    if ( recs2 == null ) {
      recs2 = new HashSet();
      recs.put( c2, recs2 );
    }

    recs2.add( r );
  }

  private void unbindDoubleWidget( Reasoner r, String widgetName1,
				 String widgetName2, Hashtable table )
  {
    Class c1 = null;
    
    try {
      c1 = Class.forName( widgetName1 );
    }
    catch( ClassNotFoundException e ) {

      Assert.error( "bad/unrecognized widget class name " + widgetName1
		    + " (check packaging?)" );
    }

    Class c2 = null;
    
    try {
      c2 = Class.forName( widgetName2 );
    }
    catch( ClassNotFoundException e ) {

      Assert.error( "bad/unrecognized widget class name " + widgetName2
		    + " (check packaging?)" );
    }

    // c has to be in the table because we put it there in add()
    Hashtable recs_table = (Hashtable)table.get( c1 );

    Assert.check( recs_table != null, "recognizer " + r
		  + " cannot be found in table " + table );
    
    Collection recs = (Collection)recs_table.get( c2 );

    Assert.check( recs != null, "recognizer " + r
		  + " cannot be found in table " + table );
    
    recs.remove( r );
    
    // clean up table entry if this was the last recognizer in the set
    if( recs.isEmpty() )
      recs_table.remove( c2 );

    if ( recs_table.isEmpty() )
      table.remove( c1 );
  }
  
  private void bindSingleWidget( Recognizer r, String widgetName,
				 Hashtable table )
  {
    Class c = null;

    try {
      c = Class.forName( widgetName );
    }
    catch( ClassNotFoundException e ) {
      Assert.error( "bad/unrecognized widget class name " + widgetName
		    + " (check packaging?)" );
    }

    Collection recs = (Collection)table.get( c );

    if ( recs == null ) {
      recs = new HashSet();
      table.put( c, recs );
    }

    recs.add( r );
  }

  private void unbindSingleWidget( Recognizer r, String widgetName,
				   Hashtable table )
  {
    Class c = null;

    try {
      c = Class.forName( widgetName );
    }
    catch( ClassNotFoundException e ) {

      Assert.error( "bad/unrecognized widget class name " + widgetName
		    + " (check packaging?)" );
    }

    // c has to be in the table because we put it there in add()
    Collection recs = (Collection)table.get( c );
    recs.remove( r );

    // clean up table entry if this was the last recognizer in the set
    if( recs.isEmpty() )
      table.remove( c );
  }


  //////////////////////////////////////////////////////////////////////
  // Grammar related methods:
  //////////////////////////////////////////////////////////////////////
  public void add( RecGrammar gram, Surface surface ) 
  {
    Assert.check( ! m_Grammars.contains( gram ),
		  "Grammar: " + gram.getName() +
		  " is already in module " + m_Name );

    gram.addTagsListener( surface );
    
    m_Grammars.add( gram );

    Console.writeln( gram.getName() + " installed" );
  }

  public void remove( RecGrammar gram )
  {
    Assert.check( m_Grammars.contains( gram ),
		  "Grammar: " + gram +
		  " is not in module " + m_Name );

    m_Grammars.remove( gram );
    // make sure it is off before we remove it
    gram.deactivate();

    Console.writeln( gram + " uninstalled" );
  }

  public void setDependentModules( Set modules ) 
  {
    m_Modules = new HashSet( modules );
  }
  

  private void setActiveGrammars( boolean active )
  {
    Iterator grammars = getGrammars();
    while ( grammars.hasNext() ) {
      RecGrammar gram = (RecGrammar)grammars.next();
      if ( active )
	gram.activate();
      else
	gram.deactivate();
    }
  }

  private void setActiveModules( boolean active ) 
  {
    Iterator mods = m_Modules.iterator();

    while( mods.hasNext() ) {
      Module mod = (Module)mods.next();
      if ( active )
	mod.enable();
      else
	mod.disable();
    }
  }
  
}
