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

// -*-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:26:07 moltmans> 
// Keywords: 
package rec.simulate.recognizers;

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.meche.WMSpring;
import rec.simulate.widgets.*;
import rec.simulate.*;

public class SimSpringDamperRecognizer extends Recognizer
{
    public SimSpringDamperRecognizer()
    {
    }
    
    public boolean recognize( Surface s, Widget w )
    {
	Vector springs = new Vector();
	Vector dampers = new Vector();

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

	while(it.hasNext()){

	    VisibleWidget vw = (VisibleWidget)it.next();

	    if(vw instanceof WSimSpring){
		springs.add((WSimSpring) vw);
	    }
	    if(vw instanceof WSimDamper){
		dampers.add((WSimDamper) vw);
	    }
	}

	if((springs.size() == 0) ||
	   (dampers.size() == 0)){
	    return false;
	}

	if (w instanceof WLine){

	    Vector Ts = getTsIncludingLine((WLine) w, s);
	    
	    for(int i = 0; i < Ts.size(); i++){
		for(int j = 0; j < springs.size(); j++){
		    for(int k = 0; k < dampers.size(); k++){

			T t1 = (T)Ts.get(i);
			WSimSpring spring = (WSimSpring) springs.get(j);
			WSimDamper damper = (WSimDamper) dampers.get(k);

			if(t1.match(spring, damper)){
			    
			    T t2 = getTFinishing(spring, 
						 damper, 
						 t1, s);
			    			    
			    if (t2 != null){
				updateWorld(s, t1, t2, spring, damper);
				return true;
			    }
			}
		    }
		}
	    }
	}

	else if (w instanceof WSimDamper){

	    WSimDamper damper = (WSimDamper) w;
	    WSimSpring spring;
	    
	    GSegment perpSeg = new GSegment(damper.getHeadPoint(), 
					    damper.getTailPoint());
	    perpSeg.rotate(perpSeg.getMidpoint(), Math.PI/2);
	    
	    for( int i = 0; i < springs.size(); i++){
		spring = (WSimSpring) springs.get(i);
		
		if(Estimator.arePerpendicular(perpSeg,
					      new GSegment(spring.getHead(),
							   spring.getTail()))){
		    
		    Vector Ts = getTsConnecting(spring, damper, s);

		    for (int j = 0; j < Ts.size(); j++){
			T firstT = (T)Ts.get(i);

			T finalT = getTFinishing(spring, damper, firstT, s);

			if(finalT != null){
			    updateWorld(s, firstT, finalT, spring, damper);
			}
		    }
		    		    
		}
		
	    }
	    
	} 
	else if ((w instanceof WSimSpring)){
	    WSimSpring spring = (WSimSpring) w;
	    WSimDamper damper;
	    
	    GSegment perpSeg = new GSegment(spring.getHead(), 
					    spring.getTail());
	    perpSeg.rotate(perpSeg.getMidpoint(), Math.PI/2);
	    
	    for( int i = 0; i < dampers.size(); i++){
		damper = (WSimDamper) dampers.get(i);
		
		if(Estimator.arePerpendicular(perpSeg,
					      new GSegment(spring.getHead(),
							   spring.getTail()))){
		    
		    Vector Ts = getTsConnecting(spring, damper, s);

		    for (int j = 0; j < Ts.size(); j++){
			T firstT = (T)Ts.get(i);

			T finalT = getTFinishing(spring, damper, firstT, s);

			if(finalT != null){
			    updateWorld(s, firstT, finalT, spring, damper);
			}
		    }
		    		    
		}
		
	    }
	}
	else{
	}
	
	return false;
    }

    private void updateWorld( Surface s, 
			      T t1,
			      T t2,
			      WSimSpring spring,
			      WSimDamper damper){
			    
	
	boolean damperup;

	SimulateSurface SimS = (SimulateSurface) s;
	
	GSegment base = new GSegment(t1.getEndPoint(),
				     t2.getEndPoint());
	

	if((spring.getHead().getY() + spring.getTail().getY())
	   > (damper.getHeadPoint().getY() + damper.getTailPoint().getY())){
	    damperup = true;
	}
	else{ 
	    damperup = false;
	}

	WSimSpringDamper springDamper= SimS.makeSimSpringDamper(base, damperup);
	
	HashSet set = new HashSet();
	set.add(t1.getCrossbar());
	set.add(t1.getEnd());
	set.add(t2.getCrossbar());
	set.add(t2.getEnd());
	set.add(spring);
	set.add(damper);
	
	s.replaceWidget(set, springDamper, true);

    }

    Vector getTsConnecting(WSimSpring spring,
			   WSimDamper damper,
			   Surface s){

	Vector Ts = new Vector();

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

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

	    if (vw instanceof WLine){
		
		WLine line = (WLine) vw;
		    
		GeometryCalculator gc2 = new GeometryCalculator(s);
		Iterator it2 = gc.getVisibleWidgets().iterator();
		    
		while (it2.hasNext()){
		    VisibleWidget vw2 = (VisibleWidget)it2.next();

		    if (vw2 instanceof WLine){
			WLine line2 = (WLine) vw2;
			
			if(Estimator.arePerpendicular
			   ((new GSegment(line.getHead(),
					  line.getTail())),
			    (new GSegment(line2.getHead(),
					  line2.getTail())))){

			    if (line.includes(line2.getHead(),20) ||
				line.includes(line2.getTail(),20)){

				T tTest = new T(line2, line);
				
				if (tTest.match(spring, damper)){
				    Ts.add(tTest);
				}
			    }
			}
		    }
		}
	    }
	}

	return Ts;
    }

    T getTFinishing(WSimSpring spring, 
		    WSimDamper damper, 
		    T otherT, 
		    Surface s){
	

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

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

	    if (vw instanceof WLine){
		
		WLine line = (WLine) vw;
		    
		GeometryCalculator gc2 = new GeometryCalculator(s);
		Iterator it2 = gc.getVisibleWidgets().iterator();
		    
		while (it2.hasNext()){
		    VisibleWidget vw2 = (VisibleWidget)it2.next();

		    if (vw2 instanceof WLine){
			WLine line2 = (WLine) vw2;
			
			if(Estimator.arePerpendicular
			   ((new GSegment(line.getHead(),
					  line.getTail())),
			    (new GSegment(line2.getHead(),
					  line2.getTail())))){

			    if (line.includes(line2.getHead(),20) ||
				line.includes(line2.getTail(),20)){

				T tTest = new T(line2, line);
				
				if (tTest.match(spring, damper, otherT)){
				    return tTest;
				}
			    }
			}
		    }
		}
	    }
	}

	return null;
    }

    Vector getTsIncludingLine(WLine w, Surface s){
	Vector Ts = new Vector();

	GeometryCalculator gc = new GeometryCalculator(s);
	Iterator it = gc.getVisibleWidgets().iterator();
	
	while(it.hasNext()){
	    VisibleWidget vw = (VisibleWidget)it.next();
	    
	    if(vw instanceof WLine){
		WLine found = (WLine) vw;

		GSegment inSeg = new GSegment (w.getHead(), 
					       w.getTail());
		GSegment foundSeg = new GSegment(found.getHead(),
						 found.getTail());
		
		if(Estimator.arePerpendicular(inSeg, foundSeg)){

		    if (found.includes(w.getHead(),20) ||
			found.includes(w.getTail(),20)){
			
			// found is the crossbar
			Ts.add(new T(w,found));
		    }
		    else if(w.includes(found.getHead(),20) ||
			    w.includes(found.getTail(),20)){
			
			Ts.add(new T(found,w));
		    }
		}
	    }
	}
	return Ts;
    }
    
    public Iterator getInputTypes()
    {

	Vector v = new Vector();

	v.addElement("rec.simulate.widgets.wm.WMSimSpring");
	v.addElement("rec.simulate.widgets.wm.WMSimDamper");
	v.addElement("rec.core.widgets.WLine" );

	return( v.iterator() );
    }
    
    public Iterator getOutputTypes()
    {
	Vector v = new Vector();


	v.addElement( "rec.simulate.widgets.WSimSpringDamper" );
	
	return( v.iterator() );
    }
    
    class T
    {
	private WLine end;
	private WLine crossbar;
	
	GPoint endPoint = null;

	

	public T(WLine inend, WLine incrossbar)
	{
	    end = inend;
	    crossbar = incrossbar;
	    
	}
	
	public boolean match(WSimSpring spring, WSimDamper damper){
	    
	    // The ends that do attach to this T
	    GPoint springRightEnd;
	    GPoint damperRightEnd;

	    // The ends that don't attach to this T
	    GPoint springWrongEnd;
	    GPoint damperWrongEnd;
	    

	    GSegment springBase = new GSegment(spring.getHead(), 
					       spring.getTail());
	    GSegment damperBase = new GSegment(damper.getHeadPoint(),
					       damper.getTailPoint());

	    GSegment crossSeg = new GSegment(crossbar.getHead(),
					     crossbar.getTail());

	    // crossbar is perpendicular to both spring and damper
	    if (!(Estimator.arePerpendicular(springBase, crossSeg))){
		return false;
	    }
	    if (!(Estimator.arePerpendicular(damperBase, crossSeg))){
		return false;
	    }
	    
	    GPoint damperAttachPoint;

	    // crossbar attaches to one end of spring.  Whichever end 
	    // doesn't attach to the spring becomes the damperAttachPoint

	    if((crossbar.getHead()).includes(spring.getHead(), 20)){
		springRightEnd = spring.getHead();
		springWrongEnd = spring.getTail();
		damperAttachPoint = crossbar.getTail();
	    }
		
	    else if ((crossbar.getTail()).includes(spring.getHead(), 20)){
		springRightEnd = spring.getHead();
		springWrongEnd = spring.getTail();
		damperAttachPoint = crossbar.getHead();
	    }
	    else if ((crossbar.getTail()).includes(spring.getTail(), 20)){
		springRightEnd = spring.getTail();
		springWrongEnd = spring.getHead();
		damperAttachPoint = crossbar.getHead();
	    }
	    else if ((crossbar.getHead()).includes(spring.getTail(), 20)){
		springRightEnd = spring.getTail();
		springWrongEnd = spring.getHead();
		damperAttachPoint = crossbar.getTail();
	    }
	    else{
		return false;
	    }
	   
	    // Check that the damperAttachPoint connects to one of the ends
	    // of the damper.

	    if(damperAttachPoint.includes(damper.getHeadPoint(), 20)){
		damperRightEnd = damper.getHeadPoint();
		damperWrongEnd = damper.getTailPoint();
	    }
	    else if(damperAttachPoint.includes(damper.getTailPoint(), 20)){
	        damperRightEnd = damper.getTailPoint();
		damperWrongEnd = damper.getHeadPoint();
	    }
	    else{
	        return false;
	    }
	    
	    //  // end points the opposite direction of spring
	    if((Geometry.angleBetween(getEndPoint(), 
				      springRightEnd, 
				      springWrongEnd)) < .5 * Math.PI){
		return false;
	    }

	    // end points the opposite direction of spring
	    if((Geometry.angleBetween(getEndPoint(), 
				      damperRightEnd, 
				      damperWrongEnd)) < .5 * Math.PI){
		return false;
	    }
		
	    return true;
	}


	public boolean match(WSimSpring spring, WSimDamper damper, T otherT){
		
	    GPoint springFreeEnd;
	    GPoint damperFreeEnd;
	
	    if(!match(spring, damper)){
		return false;
	    }

	    WLine otherCrossbar = otherT.getCrossbar();
	
	    if(otherCrossbar.includes(spring.getTail(), 20))
		springFreeEnd = spring.getHead();
	    else
		springFreeEnd = spring.getTail();
	    
	    if(otherCrossbar.includes(damper.getTailPoint(), 20))
		damperFreeEnd = damper.getHeadPoint();
	    else
		damperFreeEnd = damper.getTailPoint();
	    
	    if(((crossbar.getHead()).includes(springFreeEnd, 20) &&
		(crossbar.getTail()).includes(damperFreeEnd, 20)) ||
	       ((crossbar.getTail()).includes(springFreeEnd, 20) &&
		(crossbar.getHead()).includes(damperFreeEnd, 20))){
		
		return true;
	    }
	    else{
		return false;
	    }
	    
	}

		
	public WLine getEnd(){
	    return end;
	}

	public WLine getCrossbar(){
	    return crossbar;
	}

	public GPoint getEndPoint()
	{
	    if(endPoint != null){
		return endPoint;
	    }
	    else{
		
		if(crossbar.includes(end.getHead(),20)){
		    endPoint = end.getTail();
		}
		else{
		    endPoint = end.getHead();
		}
		
		return endPoint;
	    }
	}
    }    
}
