package primitives.machines;
import java.awt.*;
import primitives.geomtry.*;
/**
* A class defining a Peacullier Linkage used by <a href="StraightLineDemo.html">
* StraightLineDemo</a>
*@author Dori Eldar
*/
public class StraightLineMachine implements MachineListener{
	public ExtPoint bigCenter;
	public ExtPoint  smallCenter;
	public ExtPoint[] radialJoints;
	private Coordinate[] dradialJoints;
	public ExtPoint outputJoint;
	private Coordinate doutputJoint;
	public ExtPoint centralJoint;
	private Coordinate dcentralJoint;
	public double y,x ;
	public int r;
	public double alpha;
	public int line;
	public Coordinate leader  ;
	private Dimension d;
	private boolean demo;
	public int leaderJoint = 1;
	private Coordinate[] tempPoints;
	/**
	*Sets object fields to null.
	*@exception java.lang.Throwable .
	*/
	public void finalize() throws Throwable{
		tempPoints = null;
		centralJoint = null;
		outputJoint = null;
		radialJoints = null;
		smallCenter = null;
		bigCenter   = null;
		dradialJoints = null;
		doutputJoint = null;
		leader = null;d = null;
		temp = null;
	}
    /**
	* 
	*@param d the size of the rectangle in which to draw the machine.
	*@param demo if false draws circles and straight line of various joints.
	*/
	public StraightLineMachine(Dimension d,boolean demo){
		   int size = Math.min(d.width,d.height);
		   this.d = d;
		   this.demo = demo;
		   bigCenter = new ExtPoint(d.width/2,5);
		   y = size*2/3;
		   x = size/3;
		   r = size/5;
		   alpha = 0;
		   smallCenter  = (Geomtry.getPointByVector(bigCenter.toCoordinate(),r,Math.PI/2)).toPoint();
		   dcentralJoint = Geomtry.getPointByVector(smallCenter.toCoordinate(),r,Math.PI/2);
		   dradialJoints = Geomtry.getTriPoint(bigCenter.toCoordinate(),y,dcentralJoint,x);
			doutputJoint  = Geomtry.getTriPointEx(dradialJoints[0],x,dradialJoints[1],x,new Coordinate(d.width/2,size),null);
		   line = (int)doutputJoint.y;
		   centralJoint = dcentralJoint.toPoint();
		   radialJoints = new ExtPoint[2];
		   radialJoints[0] = dradialJoints[0].toPoint();
		   radialJoints[1] = dradialJoints[1].toPoint();
		   outputJoint = doutputJoint.toPoint();
	   	   tempPoints = new Coordinate[4];
		   leader = new Coordinate(dradialJoints[0].x,dradialJoints[0].y);
	}
	/**
	* preform backup of last valid state incase next step is not legal.
	*/
	private void savePoints(){
		tempPoints[0] = dradialJoints[0];
		tempPoints[1] = dradialJoints[1];
		tempPoints[2] = dcentralJoint;
		tempPoints[3] = doutputJoint;
	}
	/**
	*restores to the last valid state of the machine in case
	* the current state is not valid.
	*/
	private void restorePoints(){
		dradialJoints[0] = tempPoints[0];
		dradialJoints[1] = tempPoints[1];
		dcentralJoint    = tempPoints[2];
		doutputJoint	 = tempPoints[3] ;
	}
	private Coordinate temp =new Coordinate();
	/**
	* switches between 2 supporting joints.
	* this is done in order to keep a well behaved motion
	* without changing the moveMachine method to be based on the current
	* state of the machine.
	*@return the current angle of the straight motion joint
	*/
	public double switchLeader(){
		temp.move(dradialJoints[0].x,dradialJoints[0].y);
		dradialJoints[0].move(dradialJoints[1].x,dradialJoints[1].y);
		dradialJoints[1].move(temp.x,temp.y);
		if (leaderJoint==1) leaderJoint=0;
		else leaderJoint=1;
		temp.move(bigCenter.x,bigCenter.y);
		return Geomtry.getAngle(temp,dradialJoints[0]);
	}
    /**
	*preforms a motion of the machine
	*@param alpha the angle to move the leader joint to.
	*@exception MachineException if the move is not valid.
	*/
	public void moveMachine(double alpha) throws MachineException{
		savePoints();
		temp.move(bigCenter.x,bigCenter.y);
		dradialJoints[0] = Geomtry.getPointByVector(temp,y,alpha);
		try{
			temp.move(smallCenter.x,smallCenter.y);
			dcentralJoint   = Geomtry.getTriPointEx(temp,r,dradialJoints[0],x,dcentralJoint,dcentralJoint);
			temp.move(bigCenter.x,bigCenter.y);
			dradialJoints[1] = Geomtry.getTriPointEx(dcentralJoint,x,temp,y,dradialJoints[1],dradialJoints[1]);
			doutputJoint    = Geomtry.getTriPointEx(dradialJoints[0],x,dradialJoints[1],x,doutputJoint,doutputJoint);
		
		}catch (ArithmeticException e){
			restorePoints();
			throw new MachineException(reachedEnd);
		}
		centralJoint = dcentralJoint.toPoint();
		radialJoints[0] = dradialJoints[0].toPoint();
		radialJoints[1] = dradialJoints[1].toPoint();
		outputJoint = doutputJoint.toPoint();														  
		if((radialJoints[0].x==radialJoints[1].x)&&(radialJoints[0].y==radialJoints[1].y)) {
			throw new MachineException(p);
		}
	}
	final static String reachedEnd = "reached end";
	final static String p = "p";
	private void drawLine(Graphics g,Point p1,Point p2){
		g.drawLine(p1.x,p1.y,p2.x,p2.y);
	} 
	/**
	* Redraws the machine.
	*If <code>demo</code> is set to true, blue circles and black line of motion
	* of the various joint are also drawn.
	*@param g the graphic context to draw to.
	*/
  	public void redraw(Graphics g){
		 g.setColor(Color.red);
		 Geomtry.drawAnchor(g,smallCenter);
		 Geomtry.drawAnchor(g,bigCenter);
		 if (!demo){
			g.setColor(Color.black);
			g.drawLine(0,line,d.width,line);
			g.setColor(Color.blue);
			g.drawOval((int)(bigCenter.x-r),(int)bigCenter.y,(int)(2*r),(int)(2*r));
			g.drawOval((int)(bigCenter.x-y),(int)(bigCenter.y-y),(int)(2*y),(int)(2*y));
		 }

		g.setColor(Color.black);
		drawLine(g,smallCenter,centralJoint);
		for(int i=0;i<2;i++){
			g.setColor(Color.black);
			drawLine(g,bigCenter,radialJoints[i]);
			drawLine(g,centralJoint,radialJoints[i]);
			drawLine(g,outputJoint,radialJoints[i]);
			g.setColor(Color.green);
			Geomtry.drawJoint(g,radialJoints[i]);
		}
		Geomtry.drawJoint(g,centralJoint);
		Geomtry.drawJoint(g,outputJoint);
	}
	
}





