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

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

package rec.annotate;

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

/**
 * This class is for storing information about the angular rotation of
 * objects
 **/
public class RotationAnnotation extends MovementAnnotation
{
  public static final int CCW = 2357;
  public static final int CW  = 7532;
  public static final int UNSPECIFIED = -1;
  
  protected int m_direction;
  protected Entity m_pivot;

  public RotationAnnotation( Surface s )
  {
    super( s );
    m_direction = UNSPECIFIED;
    m_pivot = new Entity( m_notebook );
  }

  public RotationAnnotation( Surface s, WSimBody movee, WSquiggleArrow path )
  {
    super( s, movee, path );
    m_pivot = new Entity( m_notebook );
    m_direction = getDirection( path );

    Widget pivot = findPivot( s, movee );
    m_pivot.setWidget( pivot );
  }

  public RotationAnnotation( Surface s, MovementAnnotation m ) 
  {
    super( m.m_notebook, m.m_sentences, m.m_movee, m.m_path );
    m_pivot = new Entity( m_notebook );
    m_direction = getDirection( (WSquiggleArrow)m.m_path.getWidget() );

    Widget pivot = findPivot( s, (WSimBody)m.m_movee.getWidget() );
    m_pivot.setWidget( pivot );
  }
    
  public RotationAnnotation( AnnotationNotebook book, LinkedList sentences,
			     Entity movee, Entity path,
			     Entity pivot, int direction )
  {
    super( book, sentences, movee, path );
    m_pivot = (Entity)pivot.clone();
    m_direction = direction;
  }

  public RotationAnnotation( Surface s, WSentence sentence )
  {
    this( s );

    Assert.error( "Not implemented yet" );
//      m_sentences.add( sentence );
//      Assert.check( sentence.hasTag( "rotates" ),
//  		  "Not a rotational sentence" );

//      if ( sentence.hasDirectObject() ) {
//        m_movee.addName( sentence.getDirectObject() );
//        m_mover.addName( sentence.getSubject() );
//      }
//      else {
//        m_movee.addName( sentence.getSubject() );
//      }
      
//      // check for: "rotates around the joint"
//      if( sentence.hasPrepositionalPhrase() &&
//  	sentence.isPrepositionRotational() )
//      {
//        m_pivot.addName( sentence.getObjectOfPreposition() );
//        m_path.addName( sentence.getPrepositionalPhrase() );
//      }
  }

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

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

  public void retractUpdate()
  {
    super.retractUpdate();
  }
  

  private int getDirection( WSquiggleArrow arrow ) 
  {
    if( arrow == null )
      return UNSPECIFIED;
    
    GPolySegment segment = arrow.getPolySegment();

    for( int i=2; i<segment.getNumberOfPoints(); i++ ) {
      GPoint p1 = segment.getPointAt( i-2 );
      GPoint p2 = segment.getPointAt( i-1 );
      GPoint p3 = segment.getPointAt( i );
      
      GPoint v1 = p2.minus( p1 );
      GPoint v2 = p3.minus( p2 );

      double cross = v1.crossProduct( v2 );
      if( cross > 0 )
	return CCW;
      if( cross < 0 )
	return CW;
    }

    DebugWindow.addLine( "Problems", "WARNING: " +
			 "unable to determine rotation direction" );
    return UNSPECIFIED;
  }

  private Widget findPivot( Surface s, WSimBody body ) 
  {
    if( body == null )
      return null;
    
    LinkedList pivots = new LinkedList();
    Set constraints = body.getConstraints();
    Iterator widgets = constraints.iterator();

    while( widgets.hasNext() ) {
      WSimConstraint w = (WSimConstraint)widgets.next();
      if( w instanceof WSimPinJoint ) {
	pivots.add( w );
      }
    }
    if( pivots.size() > 1 )
      Assert.error( "Too many pivots" );
    if( pivots.size() == 0 )
      return null;

    return (Widget)pivots.getFirst();
  }

  public String printDirection() 
  {
    if( m_direction == CCW )
      return "counterclockwise";
    if( m_direction == CW )
      return "clockwise";
    else
      return "";
  }
  
  public String describe() 
  {
    String result = "Rotation:";

    result += "[" + m_movee + "]";

    result += " is rotated " + printDirection();

    if ( !m_pivot.isEmpty() ) {
      result += " around [" + m_pivot + "]";
    }
    if ( !m_path.isEmpty() ) {
      result += " via [" + m_path + "]";
    }

    return result;
  }

  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 )
  {
    // check if temporaliy proximate...(how?)
    Entity movee = Entity.merge( m_movee, m.m_movee );
    Entity path  = Entity.merge( m_path, m.m_path );
    Entity pivot = m_pivot;
    int direction = UNSPECIFIED;
    
    if( m instanceof RotationAnnotation ) {
      RotationAnnotation r = (RotationAnnotation)m;
      // they must be the same direction or unspecified
      if( m_direction == UNSPECIFIED )
	direction = r.m_direction;
      else if( r.m_direction == UNSPECIFIED )
	direction = m_direction;
      else if( m_direction != r.m_direction )
	return null;
      
      pivot = Entity.merge( m_pivot, r.m_pivot );
    }

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

    LinkedList sentences = getSentences();
    sentences.addAll( m.getSentences() );

    return new RotationAnnotation( m_notebook, sentences,
				   movee, path,
				   pivot, direction );
  }
  
}
