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

// -*-Mode: Java-*- 
// $Id: AnnotationNotebook.java,v 1.2 2003/03/06 00:40:43 moltmans Exp $
// AnnotationNotebook.java -- 
// Author: Michael Oltmans <moltmans@ai.mit.edu> 
// Created: <Fri Aug 27 12:55:48 1999> 
// Time-stamp: <2000-04-28 12:05:26 moltmans> 
package rec.annotate;

import java.util.*;
import rec.tinybrowser.*;
import rec.sys.*;
import rec.util.*;
import rec.simulate.widgets.*;
import rec.speech.*;
import rec.tms.*;

/**
 * This holds the annotation objects in a hashtable so that they can
 * be referenced by the widgets they relate to.
 **/
public class AnnotationNotebook 
{
  /** map widgets to annotations **/
  private Hashtable m_annotationMap;

  /** The annotations most recent created first **/
  private LinkedList m_events;
  
  /** map names to entities **/
  private Hashtable m_namesToWidgets;
  /** map entities to names **/
  private Hashtable m_widgetsToNames;

  /** A truth maintenance system to keep track of our data **/
  private JTMS m_tms;

  public AnnotationNotebook( )
  {
//      m_Causal = new CuasalStructure();

    m_events = new LinkedList();
    m_annotationMap = new Hashtable();
    m_namesToWidgets = new Hashtable();
    m_widgetsToNames = new Hashtable();
    m_tms = new JTMS( "Annotations" );
  }


  /** Add a (bi-directional) mapping between objects and names **/
  public void addName( String name, Widget obj ) 
  {
    if( name == null || obj == null ) {
      return;
    }
    
    if ( m_namesToWidgets.containsKey( name ) &&
	 !m_namesToWidgets.get( name ).equals( obj ) )
    {
      DebugWindow.addLine( "Problems",
			   "Need to actually try to merge objects" );
    }
    
    m_namesToWidgets.put( name, obj );
    m_widgetsToNames.put( obj, name );
    obj.setEnglishName( name );
  }

  public void removeName( String name, Widget obj ) 
  {
    Widget testObj = (Widget) m_namesToWidgets.get( name );
    String testStr = (String) m_widgetsToNames.get( obj );
    if ( testObj.equals( obj ) && testStr.equals( name ) ) {
      m_namesToWidgets.remove( name );
      m_widgetsToNames.remove( obj );
    }
  }
  

  public Widget lookupWidget( String name ) 
  {
    return (Widget)m_namesToWidgets.get( name );
  }

  public String lookupName( Widget obj )
  {
    return (String)m_widgetsToNames.get( obj );
  }

  /**
   * Add a mapping from the widget to an annotation that mentions the widget.
   **/
  protected void addAnnotation( Widget obj, Annotation a ) 
  {
    if( obj == null || a == null )
      return;

    LinkedList annotations = (LinkedList)m_annotationMap.get( obj );
    if( annotations == null ) {
      annotations = new LinkedList();
      m_annotationMap.put( obj, annotations );
    }

    annotations.add( a );
  }

  public void removeAnnotation( Widget obj, Annotation a )
  {
    if( obj == null )
      return;
    
    LinkedList annotations = (LinkedList)m_annotationMap.get( obj );
    if( annotations == null )
      return;

    annotations.remove( a );
    if( annotations.isEmpty() )
      m_annotationMap.remove( obj );
  }

  /**
   * Add annotation to the front of the event list and make the
   * bindings that the annotation asserts.
   **/
  public void addEvent( Annotation a ) 
  {
    m_events.addFirst( a );

    Iterator ents = a.getEntities().iterator();
    while( ents.hasNext() ) {
      Entity e = (Entity)ents.next();
      e.updateBindings( this );
      addAnnotation( e.getWidget(), a );
    }
  }

  /**
   * @return List of events; most recent listed first.
   **/
  public LinkedList getEvents() 
  {
    return new LinkedList( m_events );
  }
  
  /**
   * @return a possibly empty, linked list containing all annotations
   *         for this widget.
   **/
  public LinkedList getAnnotations( Widget obj )
  {
    if( obj == null )
      return new LinkedList();
    
    LinkedList annotations = (LinkedList)m_annotationMap.get( obj );
    if( annotations == null )
      return new LinkedList();

    return new LinkedList( annotations );
  }

  /**
   * Merge adjacent events if they are different descriptions of the
   * same event.
   **/
  public void mergeEvents() 
  {
    if( m_events.size() < 2 )
      return;

    ListIterator it = m_events.listIterator( 0 );
    Annotation a1;
    Annotation a2;
    Annotation merged = null;


    while( it.nextIndex() + 1 < m_events.size() ) {
      // compare this one and the next one
      a1 = (Annotation)it.next();
      a2 = (Annotation)it.next();

      merged = a1.merge( a2 );
      // replace the old ones with the merged version
      if( merged != null ) {
	it.remove();
	it.previous();
	it.remove();
	it.add( merged );
      }

      // back up one so we compare merged result with the next one
      // or a2 with the next elmt
      it.previous();
    }
  }
}
