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

// -*-Mode: Java-*- 
// $Id: MovementAnnotation.java,v 1.2 2003/03/06 00:40:43 moltmans Exp $
// MovementAnnotation.java -- 
// Author: Michael Oltmans <moltmans@ai.mit.edu> 
// Created: <Thu Oct 28 21:20:58 1999> 
// Time-stamp: <2000-04-18 16:35:41 moltmans> 

package rec.annotate;

import java.util.*;
import rec.util.*;
import rec.geo.*;
import rec.sys.*;
import rec.core.widgets.*;
import rec.simulate.widgets.*;
import rec.annotate.widgets.*;
import rec.speech.*;

/**
 * This class is for storing information about the motion of objects
 **/
public class MovementAnnotation implements Annotation
{
  protected LinkedList m_entities;
  protected LinkedList m_sentences;

  protected Annotation m_cause;
  protected Annotation m_effect;

  protected SpringAdjuster m_adjuster;
  
  protected Entity m_movee;
  protected Entity m_path;

  protected AnnotationNotebook m_notebook;

  /** just used internally to initialize **/
  protected MovementAnnotation( Surface s )
  {
    m_notebook = s.getAnnotationNotebook();
    m_sentences = new LinkedList();
    m_movee = new Entity( m_notebook );
    m_path = new Entity( m_notebook );
  }

  public MovementAnnotation( Surface s, WSimObject movee,
			     WSquiggleArrow path )
  {
    this( s );
    m_movee.setWidget( movee );
    m_path.setWidget( path );
  }


  public MovementAnnotation( AnnotationNotebook book, LinkedList sentences,
			     Entity movee, Entity path )
  {
    m_notebook = book;
    m_sentences = new LinkedList( sentences );
    m_movee = (Entity)movee.clone();
    m_path = (Entity)path.clone();
  }

  public MovementAnnotation( Surface s, WSentence sentence,
			     String movee, String path )
  {
    this( s );
    m_movee.addName( movee );
    m_path.addName( path );
    m_sentences.add( sentence );
  }

//    // problem: when block pushes ball gesture is made only one motion
//    // annotation is made, but the ball moves and the block moves.  
//    public MovementAnnotation( Surface s, WSentence sentence )
//    {
//      this( s );
//      m_sentences.add( sentence );

//      if ( sentence.hasTag( "propels" ) ) {
//        if ( sentence.hasDirectObject() ) {
//  	m_movee.addName( sentence.getDirectObject() );
//  	m_mover.addName( sentence.getSubject() );
//        }
//        // check for: "pushes against the lever"
//        else if ( sentence.hasPrepositionalPhrase() ) {
//  	m_mover.addName( sentence.getObjectOfPreposition() );
//  	m_movee.addName( sentence.getSubject() );
//        }
//        else {
//  	DebugWindow.addLine( "Problems",
//  			     "Unhandled propels in MovementAnnotation: "
//  			     + this + "\n" + sentence.getText() );
//        }
//      }
//      else if ( sentence.hasTag( "moves" ) ) {
//        m_movee.addName( sentence.getSubject() );
//        if ( sentence.hasPrepositionalPhrase() ) {
//  	m_path.addName( sentence.getPrepositionalPhrase() );
//        }
//      }
//      else {
//        DebugWindow.addLine( "Problems",
//  			   "Unhandled MovementAnnotation: "
//  			   + sentence.getText() );
//      }
//    }
  
  public Entity getMovee()
  {
    return m_movee;
  }

  /**
   * Get the entity (if there is one) that caused this motion event
   **/
  public Entity getMover() 
  {
    if( m_cause instanceof MovementAnnotation )
      return ((MovementAnnotation)m_cause).getMovee();
    return null;
  }
  
  /**
   * Get the path.  The tail is the start location and the head the
   * final destination.
   **/
  public Entity getPath()
  {
    return m_path;
  }

  public WSquiggleArrow getArrow() 
  {
    return (WSquiggleArrow)m_path.getWidget();
  }

  public LinkedList getSentences()
  {
    return new LinkedList( m_sentences );
  }

  public LinkedList getEntities() 
  {
    if( m_entities == null ) {
      m_entities = new LinkedList();
      m_entities.add( m_movee );
      m_entities.add( m_path );
    }
    return new LinkedList( m_entities );
  }

  /**
   * This is called to assert knowledge into the notebook.
   **/
  public void updateNotebook() 
  {
    //    adjustSpring();
  }

  /**
   * This retracts the knowledge in case of error.
   **/
  public void retractUpdate()
  {
//      // Check if the mover is attached to a spring.  if so retract the
//      // restLength assigned earlier
//      SpringAdjuster adjuster = new SpringAdjuster( m_mover.getWidget() );
//      if( adjuster.hasSpring() ) {
//        adjuster.resetRestLength();
//      }

//      // Check if the movee is attached to a spring. if so retract the
//      // force the movee in the direction indicated by the arrow
//      else {
//        adjuster = new SpringAdjuster( m_movee.getWidget() );
//        if( adjuster.hasSpring() ) {
//  	adjuster.resetRestLength();
//        }
//      }
    
  }

  public String describe() 
  {
    String result = "Motion:";

    result += "[" + m_movee + "]";
    
//      if ( !m_mover.isEmpty() ) {
//        result += " is moved by [" + m_mover + "]";
//      }
    if( !m_path.isEmpty() ) {
      result += " via [" + m_path + "]";
    }

    if( m_cause != null ) {
      result += " caused by [" +
	((MovementAnnotation)m_cause).getMovee() + "]";
    }

    return result;
  }

  public void adjustSpring()
  {
//      // if the mover is connected to a spring and it propels then set
//      // the spring rest length accordingly
//      if( m_mover.hasWidget() && m_movee.hasWidget() )

//      if( (w != null) && (w instanceof WSimBody) ) {
//        Iterator sentences = getSentences().iterator();
//        boolean found = false;
      
//        while( sentences.hasNext() ) {
//  	WSentence sent = (WSentence)sentences.next();
//  	if( sent.hasTag( "propels" ) && sent.hasPrepositionalPhrase() ) {
//  	  Tag obj = sent.getObjectOfPreposition();
//  	  if( obj != null ) {
	    
//  	  Assert.check( !found,
//  			  "Oops: 2 push/pull sentences for same spring" );
//  	    found = true;
//  	    ((WSimSpring)w).setCompression( 1.2 );
//  	  }
//  	  else if( prepP.hasTag( "pulls" ) ) {
//  	    Assert.check( !found,
//  			  "Oops: 2 push/pull sentences for same spring" );
//  	    found = true;
//  	    ((WSimSpring)w).setCompression( .8 );
//  	  }
//  	}
//        }
//      }

 /* this isn't correct just at the momment...
    WSquiggleArrow arrow = getArrow();
    Entity mover = getMover();
    if( mover == null )
      return;
    
    // Check if the mover is attached to a spring.  if so it should
    // force the mover into the movee
    SpringAdjuster adjuster = new SpringAdjuster( this );
    if( adjuster.hasSpring() ) {
      adjuster.setRestLength( (WSimBody)mover.getWidget(),
			      (WSimBody)m_movee.getWidget(),
			      arrow );
    }

    // Check if the movee is attached to a spring. if so it should
    // force the movee in the direction indicated by the arrow
    else {
      adjuster = new SpringAdjuster( m_movee.getWidget() );
      if( adjuster.hasSpring() ) {
	adjuster.setRestLength( (WSimBody)m_movee.getWidget(), arrow );
      }
    }
 */
  }
  
  public Annotation merge( Annotation a ) 
  {
    if ( a instanceof MovementAnnotation )
      return merge( (MovementAnnotation)a );
    else
      return null;
  }

  /**
   * Try to merge the two annotations.  If they cannot be merged
   * return null.
   **/
  public MovementAnnotation merge( MovementAnnotation m )
  {
    // CAN WE MERGE OFF OF CAUSES?

    Entity movee = Entity.merge( m_movee, m.m_movee );
    Entity path  = Entity.merge( m_path, m.m_path );

    if ( movee == null || path  == null )
      return null;

    LinkedList sentences = getSentences();
    sentences.addAll( m.getSentences() );
    
    return new MovementAnnotation( m_notebook, sentences,
				   movee, path );
  }


  /**
   * @return the adjuster responsible for modifying this movement
   *         or null if there isn't one.
   **/
  public Adjuster getAdjuster()
  {
    if( m_adjuster == null ) {
      m_adjuster = new SpringAdjuster( this );

      if( !m_adjuster.hasSpring() ) {
	return null;
      }
      // if it has an effect, set it as a goal
      if( m_effect != null ) {
	m_adjuster.setGoal( m_effect );
      }
    }
    if( m_adjuster.hasSpring() ) {
      return m_adjuster;
    }
    else
      return null;
  }
  
  public Annotation getCause() 
  {
//      LinkedList list = new LinkedList();
//      if( m_cause != null )
//        list.add( m_cause );
//      return list;
    return m_cause;
  }

  public Annotation getEffect()
  {
    Assert.error( "Not implemented yet" );
    return null;
  }

  public void setCause( Annotation a ) 
  {
    m_cause = a;
  }

  public void setEffect( Annotation a )
  {
    m_effect = a;

    // make sure the adjuster is set up correctly
    // to achieve this goal
    Adjuster ad = getAdjuster();
    if( ad != null )
      ad.setGoal( a );
  }

}
