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

// -*-Mode: Java-*- 
// $Id: MotionEvent.java,v 1.4 2003/03/06 00:40:49 moltmans Exp $
// MotionEvent.java -- 
// Author: Michael Oltmans <moltmans@ai.mit.edu> 
// Created: <Mon May 08 00:21:04 2000> 
// Time-stamp: <2000-07-19 16:57:43 moltmans> 
package rec.annotate.facts;

import rec.annotate.rules.*;
import java.util.*;
import rec.annotate.widgets.*;
import rec.util.*;
import rec.sys.*;
import rec.tms.*;

/**
 * This class is for storing information about the motion of bodies
 **/
public class MotionEvent extends Event
{
  protected Body m_body;
  protected Annotation m_base;
  protected boolean m_isSentence;
  protected boolean m_isArrow;

  public MotionEvent( Surface s, Body body, Annotation base )
  {
    super( s, base.getStartTime(), base.getEndTime() );
    Assert.notNull( base,
		    "Cannot make MotionEvent with null base" );
    m_body = body;
    m_base = base;
    m_isSentence = (base instanceof Sentence);
    m_isArrow = (base instanceof Arrow);
  }
  
  public Annotation getBase()
  {
    return m_base;
  }

  public Body getSubject()
  {
    return getBody();
  }

  public boolean isBasedOn( Annotation a ) 
  {
    if( a == null )
      return false;
    return a.sameFact( getBase() );
  }
    
  public Body getBody()
  {
    return m_body;
  }

  /**
   * Get the path.  The tail is the start location and the head the
   * final destination.
   **/
  public Arrow getArrow()
  {
    if( m_isArrow )
      return (Arrow)m_base;
    return null;
  }

  public Sentence getSentence() 
  {
    if( m_isSentence )
      return (Sentence)m_base;
    return null;
  }

  public String getFactType() 
  {
    return Type.MOTION_EVENT;
  }

  public boolean sameFact( Fact fact ) 
  {
    if( fact instanceof MotionEvent ) {
      MotionEvent f = (MotionEvent)fact;
      return ( m_base.sameFact( f.m_base ) &&
	       m_body.sameFact( f.m_body ) );
    }
    return false;
  }

  public boolean sameEvent( MotionEvent event )
  {
    if( sameFact( event ) )
      return true;
    
    if( !m_body.sameFact( event.m_body ) )
      return false;

    LinkedList widgets = getSurface().getTimeOrderedElectedWidgets();

    // if they overlap temporally they are probably the same event
    if( doesTimeOverlap( event ) ) {
      return true;
    }
    
    long start;
    long end;

    if( getStartTime() < event.getStartTime() ) {
      start = getEndTime();
      end = event.getStartTime();
    }
    else {
      start = event.getEndTime();
      end = getStartTime();
    }

    Iterator it = widgets.iterator();

    // count the number of intervening widgets.  if there are too many
    // annotations then they are probably not the same event
    int count = 0;
    while( it.hasNext() ) {
      Widget w = (Widget)it.next();
      // we have checked the interesting time period
      if( w.getTime() >= end )
	return true;
      // count the annotations if the interesting time period.  if
      // there are more than 1 then they are not the same event
      if( (w.getTime() > start) && (w instanceof WAnnotation) ) {
	count ++;
	if( count > 1 ) {
	  return false;
	}
      }
    }
    return true;
  }

  public Event combineWith( Event e )
  {
    // rotations are more specific than general motions
    if( e instanceof RotationEvent ) {
      return e.combineWith( this );
    }
    if( !(e instanceof MotionEvent) ) {
      return null;
    }

    if( !m_body.sameFact( ((MotionEvent)e).m_body ) ) {
      return null;
    }
    
    // we don't care about the base for this...
    return new MotionEvent( getSurface(), m_body, this );
  }    

  public String toString() 
  {
    return( "<Motion: [" + m_body.getName() +
	    "] based on [" + m_base + "]>" );
  }

  public String toEnglish()
  {
    return m_body.toEnglish() + " moves";
  }
  
}
