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

// $Id: MotionCausesMotionRule.java,v 1.8 2005/02/17 21:40:59 moltmans Exp $
// MotionCausesMotionRule.java -- 
// Author: Michael Oltmans <moltmans@ai.mit.edu> 
// Created: <Sun Jul 16 19:32:28 2000> 
// Time-stamp: <2005-02-16 14:03:38 moltmans> 
package rec.annotate.rules;

import java.awt.Rectangle;
import rec.annotate.facts.*;
import java.util.*;
import rec.util.*;
import rec.geo.*;
import rec.tms.*;
import rec.sys.*;
import rec.simulate.widgets.*;
import rec.core.widgets.*;

/**
 * Recognize motions that might cause other motions
 **/
public class MotionCausesMotionRule extends AnnotateRule
{
  public LinkedList getTriggers() 
  {
    LinkedList triggers = new LinkedList();
    triggers.add( Type.MOTION_EVENT );
    triggers.add( Type.MOTION_EVENT );
    
    return triggers;
  }

  public boolean match( LinkedList params ) 
  {
    MotionEvent cause = (MotionEvent)params.get(0);
    Arrow arrow = cause.getArrow();
    if( params.size() == 1 ) {
      return arrow != null;
    }

    MotionEvent effect = (MotionEvent)params.get(1);
    Body causeBody = cause.getBody();
    Body body = effect.getBody();
    Notebook book = getNotebook();
    
    // check if they are temoporaliy close to one another...
    int num = book.numAnnotations( cause, effect );
    if( cause.sameEvent( effect ) || num < 0 || num > 3 ) {
      return false;      
    }

    // check if it passes by within the size of the object we divide
    // the size by 2 since we want the distance on either side of the
    // line.
    Rectangle bounds = causeBody.getBody().getBounds();
    double size = ( bounds.getWidth() + bounds.getHeight() ) / 4.0;
    GShape shape = body.getBody().getShape();
    if( !arrow.getArrow().passesBy( shape, size ) ) {
      return false;
    }

    // it is ok if the second doesn't have a direction.  we give the
    // benefit of the doubt...
    Arrow arrow2 = effect.getArrow();
    if( arrow2 == null ) {
      return true;
    }
    
    // check if the directions of the two motions are aligned (more or
    // less anyway)
    double angle = Math.abs( arrow.getArrow().getHeadAngle() -
			     arrow2.getArrow().getTailAngle() );
    return Radian.isBetween( -0.25 * Math.PI, 0.25 * Math.PI, angle );
  }

  public void run( Surface s, Notebook book, LinkedList params )
  {
    MotionEvent cause = (MotionEvent)params.get(0);
    MotionEvent effect = (MotionEvent)params.get(1);

    Causes fact = new Causes( cause, effect );
    book.myassert( fact, "MotionCausesMotionRule", params );
  }
}

