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

package rec.simulate.recognizers;
// -*-Mode: Java-*- 
// SimAnchorRecognizer.java -- 
// Author: Christine Alvarado <calvarad@ai.mit.edu> 
// Maintainer: Christine Alvarado <calvarad@ai.mit.edu> 
// Version: 
// Created: <Wed May 26 16:06:48 1999> 
// Time-stamp: <2001-09-10 18:21:29 moltmans> 
// Keywords: 

import rec.geo.*;
import java.util.*;
import java.awt.*;
import rec.util.*;
import rec.sys.*;
import rec.core.widgets.*;
import rec.meche.Estimator;
import rec.simulate.widgets.*;
import rec.simulate.*;

public class SimMotorRecognizer extends Recognizer
{
    public boolean recognize( Surface s, Widget w )
    {

	// A motor has two parts, a circle and a trapazoid
	WCircle circle = null;
	WQuadrilateral trap = null;

	WSimMotor motor = null;

	if(w instanceof WCircle){

	    circle = (WCircle) w;

	    // find a good Base given this Circle
	    trap = getMotorTrap(s,circle);
	}
	else if (w instanceof WQuadrilateral){

	    // Make sure polygon is an appropriate trapazoid
	    if(isGoodTrap((WQuadrilateral)w)){
		trap = (WQuadrilateral) w;
	    }
	    else{
		return false;
	    }

	    // find a Circle given this Base
	    circle = getMotorCircle(s,trap);

	}
	else{
	    ///Not a circle or trap input
	}		

	if (circle != null && trap != null){

	    GPoint center = circle.getCenter();

	    SimulateSurface SimS = (SimulateSurface) s;

	    motor = SimS.makeSimMotor(center);

	    HashSet set = new HashSet();
	    set.add(circle);
	    set.add(trap);

	    s.replaceWidget(set, motor, true);

	    return( true );
	}
    
	return false;
    }

    boolean isGoodTrap(WQuadrilateral p){

	// good traps have the following properties:
	//
	// * Two horizontal edges of differnt sizes
	// * Non horizontal edges have the same size
	// * Left and right angles match
	// * Side edges equal in size

	GPolygon poly = p.getPolygon();
	GSegment[] sides = poly.getSides();

	GSegment top;
	GSegment right;
	GSegment bottom;
	GSegment left;

	//to test if the edge is horizontal, check if it's perpendicular
	//to a vertical line

	GSegment vertical = new GSegment(0,0,0,10);
	

	// determine which sides are top/bottom etc.
	if(Estimator.arePerpendicular(sides[0], vertical)){
	    if(!Estimator.arePerpendicular(sides[2], vertical)){
		//if side 0 is horizontal, so should be side 2
		return false;
	    }
	    if(sides[0].size() < sides[2].size()){
		top = sides[0];
		right = sides[1];
		bottom = sides[2];
		left = sides[3];
	    }
	    else if(sides[0].size() > sides[2].size()){
		top = sides[2];
		right = sides[3];
		bottom = sides[0];
		left = sides[1];
	    }
	    else{
		// the top and bottom sides should not be the same size
		return false;
	    }
	}
	else if(Estimator.arePerpendicular(sides[1], vertical)){
	    if(!Estimator.arePerpendicular(sides[3], vertical)){
		return false;
	    }
	    if(sides[1].size() < sides[3].size()){
		top = sides[1];
		right = sides[2];
		bottom = sides[3];
		left = sides[0];
	    }
	    else if(sides[1].size() > sides[3].size()){
		top = sides[3];
		right = sides[0];
		bottom = sides[1];
		left = sides[2];
	    }
	    else{
		// the top and bottom sides should not be the same size
		return false;
	    }
	}
	else{
	    return false;
	}


	// Check if small side is above large side
	if(!((top.getTopPoint()).getY() < (bottom.getTopPoint()).getY())){
	    return false;
	}


	// Check if top two angles match
	if(!Estimator.isSameAngle((new GAngle(left, top)).getAngle(),
				  (new GAngle(top, right)).getAngle())){
	    return false;
	}

	// Check if bottom two angles match
	if(!Estimator.isSameAngle((new GAngle(right, bottom)).getAngle(),
				  (new GAngle(bottom, left)).getAngle())){
	    return false;
	}

	return true;
    }

    boolean makesMotor(WCircle circle, WQuadrilateral trap){
	
	// Two Properties of a good motor:
	// * center of the circle is about above center of trap
	// * bottom of circle "touches" top of trap

	double error = 40; ///.5* circle.getRadius();

	if(!(Math.abs(circle.getCenter().getX() -
		      trap.getBounds().getCenterX()) 
	     < error)){
	    return false;
	}

	if(!(Math.abs(circle.getCenter().getY() +
		      circle.getRadius() -
		      trap.getBounds().getY())
	     < error)){
	    return false;
	}
	
	return true;
    }


    WCircle getMotorCircle(Surface s, WQuadrilateral trap)
    {

	GeometryCalculator gc = new GeometryCalculator(s);
	Iterator it = gc.getVisibleWidgets().iterator();

	while(it.hasNext()){
	    VisibleWidget vw = (VisibleWidget)it.next();

	    if(vw instanceof WCircle){
		if(makesMotor((WCircle) vw, trap)){
		    return (WCircle) vw;
		}
	    }
	}
	return null;

    }

    WQuadrilateral getMotorTrap(Surface s, WCircle circle)
    {
	GeometryCalculator gc = new GeometryCalculator(s);
	Iterator it = gc.getVisibleWidgets().iterator();
	
	while(it.hasNext()){
	    VisibleWidget vw = (VisibleWidget)it.next();	    

	    if(vw instanceof WQuadrilateral){
		if(isGoodTrap((WQuadrilateral) vw)){
		    if(makesMotor(circle, (WQuadrilateral) vw)){
			return (WQuadrilateral) vw;
		    }
		} 
	    }
	}

	return null;
    }

    public SimMotorRecognizer()
    {
    }
    
        
    public Iterator getInputTypes()
    {
	Vector v = new Vector();
	
	v.addElement( "rec.core.widgets.WCircle" );
	v.addElement( "rec.core.widgets.WQuadrilateral" );
	
	return( v.iterator() );
    }
    
    public Iterator getOutputTypes()
    {
	Vector v = new Vector();
	
	v.addElement( "rec.simulate.widgets.WSimMotor" );
	
	return( v.iterator() );
    }
    
}
