package primitives.machines;
import java.awt.*;
import java.awt.event.*;
import primitives.geomtry.*;
/**
*A class encapsulating the Translator Linkage
*@author Dori Eldar
*@see Adder
*/
public class Translator1 extends Machine implements FunctionalLinkage{
	 /**
	 *The vector of translation
	 */
	 Coordinate translator;
	 /**
	 *Used to store a copy of the output joint location.
	 */
	 Coordinate[] output= new Coordinate[1];
	 /**
	 *Used to store a copy of the input location
	 */
	 Coordinate[] inputs = new Coordinate[1];
	 /**
	 *Stores the 2 heights of the parallograms.
	 */
	 double[] heights = new double[2];
	 /**
	 *The relative origin, initialized to the location of the first anchor
	 */
	 Point origin;
	 /**
	 *@param startPoint the location of the first anchor.
	 *@param translator the translaton vetor in polar coordinates
	 */ 
	 public Translator1(Point startPoint,Coordinate translator){
		   super(4,2,null);
		   heights[1] = translator.x/2;
		   heights[0] = translator.x/3;
		   this.translator = translator;
		   dAnchors[0] = new Coordinate(startPoint.x,startPoint.y);
		   dAnchors[1] = Geomtry.getPointByVector(dAnchors[0],translator.x,translator.y);
		   for(int i=0;i<2;i++){
				dJoints[i] = Geomtry.getPointByVector(dAnchors[i],
					heights[0],Math.PI/2);
		   		dJoints[i+2] = Geomtry.getPointByVector(dJoints[i],
					heights[1],Math.PI/2);
			
		   }
		   	inputs[0] = new Coordinate();
		   output[0] = new Coordinate();
		   updatePoints(dAnchors,anchors);
		   origin = new Point(anchors[0]);
	 }		   			   
	 public Translator1(Rectangle r,Coordinate translator){
		 this(new Point(r.width/2,r.height/2),translator);
	 }
		
	 /**
	 *@param inputs the first member of the array is used to force the location of
	 *the input joint.
	 *@return a 1 size array containing the location of the output joint.
	 * @see FunctionalLinkage
	 */
	 public Coordinate[] forceInputJoints(Coordinate[] inputs){
		 double d = Geomtry.distance(dAnchors[0],inputs[0]);
		 if (d<Math.abs(heights[0]-heights[1])+10){
			 heights[0] = Math.max(heights[0],heights[1]);
			 heights[1] = heights[0];
		 }else if(d>heights[0]+heights[1]-10){
			 heights[0] = d/2+20;
			 heights[1] = heights[0];
		 }try{
			  return setInputJoints(inputs);
		 }catch(MachineException e){}
         return getOutputJoints();
	 }
	 /**
	 *@param inputJoints the location of the input joint is set to the first member of this array
	 * @exception MachineException if the new locations for the input joint is not valid.
	 * @see FunctionalLinkage
	 */
	 public Coordinate[] setInputJoints(Coordinate[] inputJoints)throws MachineException{
		   tempJoints[0] = dJoints[0];
		   try{
				dJoints[0] = Geomtry.getTriPointEx(dAnchors[0],heights[0],inputJoints[0],
					heights[1],dJoints[0],dJoints[1]);
		   }catch(ArithmeticException e){
				dJoints[0] = tempJoints[0];
			   throw new MachineException("input not in domain");
		   }
		   dJoints[2].move(inputJoints[0].x,inputJoints[0].y);
		   dJoints[1] = Geomtry.getPointByVector(dJoints[0],translator.x,translator.y);
		   dJoints[3] = Geomtry.getPointByVector(dJoints[2],translator.x,translator.y);
		   output[0].move(dJoints[3].x,dJoints[3].y);
		   return output;
	  }
	  /**
	  * @see FunctionalLinkage
	  */
	  public Coordinate[] getOutputJoints(){
		  output[0].move(dJoints[3].x,dJoints[3].y);
		  return output;
	  }
	  /**
	  * @see FunctionalLinkage
	  */
	  public Coordinate[] getInputJoints(){
		   inputs[0].move(dJoints[2].x,dJoints[2].y);
		   return inputs;
	  }
	  /**
	  *Draws the translator linkage
	  *@param g the graphic context to draw to
	  */
	  public void redraw(Graphics g){
		  updatePoints(dJoints,joints);
		  Color c = g.getColor();
		  for(int i=0; i<2;i++){
			  g.setColor(c);
			  drawLine(g,joints[i],joints[i+2]);
			  drawLine(g,joints[2*i],joints[2*i+1]);
			  drawLine(g,joints[i],anchors[i]);
		  	  g.setColor(Color.yellow);
			  Geomtry.drawJoint(g,joints[i]);
			  g.setColor(Color.red);
			  Geomtry.drawAnchor(g,anchors[i]);
			}
			Geomtry.drawJoint(g,joints[3]);
			g.setColor(Color.green);
			Geomtry.drawJoint(g,joints[2]);
			if(activeJoint>-1) {
				g.setColor(Color.blue);
				if (activeJoint==5)
					Geomtry.drawAnchor(g,anchors[1]);
				else if(activeJoint<4&&activeJoint!=1)Geomtry.drawJoint(g,joints[activeJoint]);
			}
			g.setColor(c);
	   }
	   double[] parameters = new double[4];
	   /**
	   * @return the 2 heights of the parallograms, the modul and argument of the translator vector.
	   * @see FunctionalLinkage
	   */
	   public double[] getParameters(){
		   parameters[0] = heights[0];
		   parameters[1] = heights[1];
		   parameters[2] = translator.x;
		   parameters[3] = translator.y;
		   return parameters;
	   }
	   /**
	   * @param parameters see <a href="#getParameters">getParameters</a> for the list of parameters
	   * @exception MachineException if the modul of the vector or one of the heights of the
	   * parallograms is less then 10 pixels.
	   * @see FunctionalLinkage
	   */
	   public void setParameters(double[] parameters)throws MachineException{
		   if(parameters[2]<10||parameters[0]<10||parameters[1]<10)
			   throw new MachineException("vector too small");
		   System.arraycopy(parameters,0,heights,0,2);
		   translator.x = parameters[2];
		   translator.y = parameters[3];
		   dAnchors[1] = Geomtry.getPointByVector(dAnchors[0],translator.x,translator.y);
		   updatePoints(dAnchors,anchors);
	   }
	   int activeJoint = -1;
	   Coordinate temp = new Coordinate();
	   /**
	   *@see FunctionalLinkage
	   */
	   public int mouseMoved(MouseEvent m){
		   Point p = m.getPoint();
		   activeJoint = -1;
		   temp.move(p.x,p.y);
		   if(Geomtry.distance(dJoints[2],temp)<8) activeJoint = 2;
		   else if(Geomtry.distance(dJoints[0],temp)<8) activeJoint = 0;
		   else if(Geomtry.distance(dAnchors[1],temp)<8) activeJoint = 5;
		   else if(Geomtry.distance(dJoints[3],temp)<8) activeJoint = 3;
		   else if(Geomtry.distance(dJoints[1],temp)<8) activeJoint = 1;
		   else if(Geomtry.distance(dAnchors[0],temp)<8) activeJoint = 4;
		   return activeJoint;
	   }
	   /**
	   * @exception MachineException if the new location of the active joint
	   * is not valid
	   * @see FunctionalLinkage
	   */
	   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==5){
				double distance = Geomtry.distance(dAnchors[0],dJoints[2]);
				dAnchors[1].move(p.x,p.y);
				if (Geomtry.distance(dAnchors[0],dAnchors[1])<4){
					dAnchors[1].move(anchors[1].x,anchors[1].y);
					throw new MachineException("anchors too close");
				}
				anchors[1].move(p.x,p.y);
				translator.x = Geomtry.distance(dAnchors[0],dAnchors[1]);
				translator.y = Geomtry.getAngle(dAnchors[0],dAnchors[1]);
				inputs[0].move(dJoints[2].x,dJoints[2].y);
				setInputJoints(inputs);
		   }else if(activeJoint==0){
			   temp.move(p.x,p.y);
			   double theight1 = Geomtry.distance(temp,dAnchors[0]);
			   double theight2 = Geomtry.distance(temp,dJoints[2]);
			   if(theight1<10||theight2<10)
				   throw new MachineException(0);
			   heights[0] = theight1;
			   heights[1] = theight2;
			   inputs[0].move(dJoints[2].x,dJoints[2].y);
			   setInputJoints(inputs);
		   }
	   }
	   final static String jointStr = "Set input domain: ";
	   final static String inputStr = "Move input joint(";
	   final static String anchorStr = "Set translation vector(";
	   final static String outputStr = "Output joint:(";
	   final static String def = "Translator Linkage vector:("; 
	   /**
	   * @see FunctionalLinkage
	   */

	public String getActiveStr(int activeJoint){
		switch(activeJoint){
		case 0:return jointStr+(int)Math.abs(heights[0]-heights[1])+annulus+(int)(heights[0]+heights[1]);
		case 2:return inputStr+(joints[2].x-origin.x)+comma+(origin.y-joints[2].y)+bracket;
		case 5:return anchorStr+(int)(translator.x*Math.cos(translator.y))+comma+
					(int)((-1)*translator.x*Math.sin(translator.y))+bracket;
		case 3:return outputStr+(joints[3].x-origin.x)+comma+(origin.y-joints[3].y)+bracket;
		default:return def+(int)(translator.x*Math.cos(translator.y))+comma+
				(int)(translator.x*(-1)*Math.sin(translator.y))+bracket;
		}
	}
	/**
	* @see FunctionalLinkage
	*/
	public int getActiveJoint(){
		return activeJoint;
	}   
	/**
	* @see FunctionalLinkage
	*/
	public void setActiveJoint(int activeJoint){
		this.activeJoint = activeJoint;
	}
}