package primitives.machines;
import java.awt.*;
import java.awt.event.*;
import primitives.geomtry.*;
/**
*A class encapsulating an Inversor Linkage:ouput=conjugate(1/input)
*@see FunctionalLinkage
*@author Dori Eldar
*/
public class Inversor1 extends Machine implements  FunctionalLinkage{
	/**
	*The length of the bars connecting the anchor & the supporting joints
	*/
	double a;
	/**
	*The length of the bars connecting the supporting joints and the input/output joints
	*/
	double b;
	/**
	*The radius of inversion 
	*/
	double radius;
	/**
	*Stores a copy of the output joint location
	*/
	Coordinate[] output = new Coordinate[1];
	/**
	*Store the location of the input joint
	*/
	Coordinate[] inputs = new Coordinate[1];
	/**
	*stores a copy of <code>a</code> and <code>b</code>
	*@see #a
	*@see #b
	*/
	double[] parameters = new double[2];
	/**
	*The location on the canvas of the relative orign
	*/
	Point origin;
	/**
	*@d the size of the rectangle is used to initialize the Inversor so it would fit into 
	*this rectangle. The <a href="#origin">origin</a> is set to the upper left corner of
	*<code>d</code>.
	*/
	public Inversor1(Rectangle d){
		super(4,1,null);
		output[0] = new Coordinate();
		for(int i=0;i<4;i++)
			dJoints[i] = new Coordinate();
		double r = Math.min(d.width,d.height);
		dAnchors[0] = new Coordinate(d.x,d.y);
		origin = new Point(d.x,d.y);
		parameters[0] = r;
		radius = Math.rint(r*2/3);
		try{
		setParameters(parameters);
		inputs[0] = Geomtry.getPointByVector(dAnchors[0],radius+10,0);
		setInputJoints(inputs);
		}catch(MachineException m){}
		updatePoints(dAnchors,anchors);
	}
	/**
	@param parameters contains the new values for <a href="#a">a</a> and <a href="#b">b</a>.
	*@exception MachineException if a><a href="#radius">radius</a>. 
	*@see FunctionalLinkage#setParameters
	*/
	public void setParameters(double[] parameters)throws MachineException{
		if (radius>parameters[0]) throw new MachineException(0);
		a = parameters[0];
		b = Math.sqrt(a*a-radius*radius);
	}
	/**
	*@return <a href="#a">a</a> and <a href="#b">b</a>.
	*@see FunctionalLinkage#getParameters
	*/
	public double[] getParameters(){
		parameters[0] = a;
		parameters[1] = b;
		return parameters;
	}
	/**
	*@see FunctionalLinkage#forceInputJoints
	*/
	public Coordinate[] forceInputJoints(Coordinate[] inputs){
		double d = Geomtry.distance(inputs[0],dAnchors[0]);
		if (d<2)
			inputs[0].translate(4,4);
		if(d<a-b+10){
			a = radius*radius/d+d/4;
			b = Math.sqrt(a*a-radius*radius);
		}else if(d>a+b-10){
			parameters[0] = d+20;
			try{
				setParameters(parameters);
			}catch(MachineException e){}
		}
		try{
			return setInputJoints(inputs);
		}catch(MachineException e){}
		return getInputJoints();
	}
	/**
	*@exception MachineException if the new location for the input joint is not valid.
	*@see FunctionalLinkage#setInputJoints
	*/
	public Coordinate[] setInputJoints(Coordinate[] input)throws MachineException{
		savePoints();
		try{  			
		    System.arraycopy(Geomtry.getTriPoint(dAnchors[0],a,input[0],b),
				0,dJoints,0,2);
			System.arraycopy(Geomtry.getTriPoint(dJoints[0],b,dJoints[1],b),
				0,dJoints,2,2);
			if(Geomtry.distance(dJoints[2],input[0])>2){
				Coordinate temp = dJoints[2];
				dJoints[2] = dJoints[3];
				dJoints[3] = temp;
			}
			output[0].move(dJoints[3]);
			return(output);
		}catch(ArithmeticException e){
			restorePoints();
			throw new MachineException("Input Out Of Domain");
		}
	}
	/**
	*@see FunctionalLinkage#getInputJoints
	*/
	
	public  Coordinate[] getInputJoints(){
		inputs[0].move(dJoints[2]);
		return inputs;
	}
	/**
	*@see FunctionalLinkage#getInputJoints
	*/
	
	public Coordinate[] getOutputJoints(){
		output[0].move(dJoints[3]);
		return output;
	/**
	*Draws the Inversor Linkage
	*@param g the graphic context to draw to.
	*/
	}public void redraw(Graphics g){
		Color c = g.getColor();
		updatePoints(dJoints,joints);
		for(int i=0;i<2;i++){
			g.setColor(c);
			drawLine(g,anchors[0],joints[i]);
			drawLine(g,joints[i],joints[2]);
			drawLine(g,joints[i],joints[3]);
			g.setColor(Color.yellow);
			Geomtry.drawJoint(g,joints[i]);
		}
		g.setColor(Color.green);
		Geomtry.drawJoint(g,joints[2]);
		g.setColor(Color.red);
		Geomtry.drawAnchor(g,anchors[0]);
		Geomtry.drawJoint(g,joints[3]);
		if(activeJoint>-1){
			g.setColor(Color.blue);
			Geomtry.drawJoint(g,joints[activeJoint]);
		}
		g.setColor(c);
	}
	/**
	*The acitive joint is the joint the mouse pointer is over.
	*@see #mouseMoved
	*/
	int activeJoint = -1;
	/**
	* Used by mouseMoved method
	*/
	private Coordinate temp = new Coordinate();
	/**
	*@see FunctionalLinkage#mouseMoved
	*/
	public int mouseMoved(MouseEvent m){
		Point p = m.getPoint();
		temp.move(p.x,p.y);
		activeJoint = -1;
		int i=0;
		while(activeJoint<0&&i<4)
			if (Geomtry.distance(temp,dJoints[i])<8)
				activeJoint = i;
			else i++;
		return activeJoint;
	}
	/**
	*@exception MachineException if the new location for the active joint is not valid.
	*@see FunctionalLinkage#mouseDragged
	*/
	public void mouseDragged(MouseEvent m) throws MachineException{
		Point p = m.getPoint();
		if(activeJoint==2){
			inputs[0].move(p.x,p.y);
			setInputJoints(inputs);
		}
		else if(activeJoint>-1&&activeJoint<3){
			temp.move(p.x,p.y);
			parameters[0] = Geomtry.distance(temp,dAnchors[0]);
			setParameters(parameters);
			setInputJoints(getInputJoints());
		}
	}
	static final String  def = "Inversor Linkage, radius of inversion: ";
	static final String inputStr = "Move input joint (";
	static final String outputStr = "Output joint (";
	static final String jointStr1 = "Set input domain ";
	/**
	*@see FunctionalLinkage#getActiveStr
	*/
	public String getActiveStr(int activeJoint){
		switch(activeJoint){
		case -1:return def+(int)radius;
		case 2:	return inputStr+(joints[2].x-origin.x)+comma+(origin.y-joints[2].y)+bracket;
		case 3: return outputStr+(joints[2].x-origin.x)+comma+(origin.y-joints[2].y)+bracket;
		default:return jointStr1+(int)(a-b)+annulus+(int)(a+b);
		}
	}
	/**
	*@see FunctionalLinkage#getActiveJoint
	*/
	public int getActiveJoint(){
		   return activeJoint;
	}
	/**
	*@see FunctionalLinkage#setActiveJoint
	*/
	public void setActiveJoint(int activeJoint){
		this.activeJoint = activeJoint;
	}
}