package primitives.spaces;
import primitives.machines.*;
import primitives.geomtry.*;
import java.awt.*;
import java.awt.event.*;
import primitives.frames.Frames;
public  class ArmsConSpace implements MachineListener,MouseMotionListener{
	private NarmsMachine machine;
	private Dimension d;
	public Rectangle basicRect;
	private Coordinate[] machineVertices;
	private double[] machineAngles;
	public Coordinate state;
	private Coordinate  spaceCenter;
	private double originAngle;
	private int vOffset;
	private int hOffset;
	public Point currentRect;
	private Frames controller   ;
	private int minVertex;
	boolean translated = false;
	boolean move;
	int maxVertex;
	public void finalize() throws Throwable{
		machine = null;
		d = null;
		basicRect = null;
		machineVertices = null;
		state = null;
		spaceCenter = null;
		currentRect = null;
		controller = null;
	}
	public ArmsConSpace(Dimension d,NarmsMachine machine,Frames controller)throws MachineException{
		if (machine.arms!=4) throw new MachineException("Machine is not 4 arms Machine");
		this.machine = machine;
		machineVertices = machine.findConstraintAngles();
		machineAngles = new double[4];
		for(int i=0;i<4;i++){
			machineAngles[i] = Geomtry.getAngle(machine.origin,machineVertices[i]);
			machineAngles[i] = Geomtry.getStandartAngle(machineAngles[i]);
		}
		this.d = d;
		int r = Math.min(d.width,d.height)/4-20;
		basicRect = new Rectangle(r,r);
		currentRect = new Point(0,0);
		vOffset = (d.height-4*r)/2;
		hOffset = (d.width-4*r)/2;

		state = new Coordinate(0,0);
		spaceCenter = new Coordinate(basicRect.width/2,basicRect.height/2);
		originAngle = Geomtry.getStandartAngle(Geomtry.getAngle(spaceCenter,new Coordinate(0,0)));
		this.controller = controller;
		maxVertex = getMaxVertex();
		minVertex = (maxVertex+1)%4;
	}
	
	private int findStateLowerVertex(double angle){
//		int maxVertex = getMaxVertex();
		if((angle>=machineAngles[maxVertex])||(angle<=machineAngles[minVertex]))
			return maxVertex;
		int j = (maxVertex+2)%4;
			while(angle>=machineAngles[j])
				j = (j+1)%4;
		return (j+3)%4;
	}
	private int getMaxVertex(){
		double maxAngle =  -0.1;
		int maxVertex = -1;
		for(int j=0;j<4;j++)
			if(machineAngles[j]>maxAngle){
				maxVertex = j;
				maxAngle = machineAngles[j];
			}
		return maxVertex;
	}
	Coordinate t=new Coordinate();
	private void translateState(double angle,double d){
		t.move(spaceCenter.x,spaceCenter.y);
		int hSign= 1;
		int vSign = 1;
		currentRect.move(0,0);
		for(int i=0;i<4;i++)
			if(machine.bendStates[i]!=-1){
				t.translate((i%2-1)*(1-i)*hSign*basicRect.width,(i%2)*(i-2)*vSign*basicRect.width);
				currentRect.translate((i%2-1)*(1-i)*hSign*basicRect.width,(i%2)*(i-2)*vSign*basicRect.width);
				angle = Math.PI*(i%2+1)-angle;
				if(i%2==0)hSign = hSign*(-1);
				else vSign = vSign*(-1);
			}
		state = Geomtry.getPointByVector(t,d,angle);
		state.move(Geomtry.modulo(state.x,4*basicRect.width),
			Geomtry.modulo(state.y,4*basicRect.height));
		currentRect.move((int)Math.rint(Geomtry.modulo(currentRect.x,4*basicRect.width)),
			(int)Math.rint(Geomtry.modulo(currentRect.y,4*basicRect.height)));

  }
	public void changeState(){
			double mAngle = Geomtry.getStandartAngle(machine.getCurrentAngle());
			int vertex = findStateLowerVertex(mAngle);
			double nextMachineVertex = machineAngles[(vertex+1)%4];
			double angle = originAngle+(Math.PI/2)*vertex+(Math.PI/2)*
				Geomtry.getStandartAngle(mAngle-machineAngles[vertex])
				/Geomtry.getStandartAngle(nextMachineVertex-machineAngles[vertex]);
			double tangle = Geomtry.getStandartAngle(angle);
			while(tangle>Math.PI/4) tangle = tangle-Math.PI/2;
			double distance = (basicRect.width/2)/Math.abs(Math.cos(tangle));
			distance = distance*machine.getRatioDistance(mAngle);
			translateState(angle,distance);
	}
			
	public void redraw(Graphics g){
		g.clearRect(-1*hOffset,-1*vOffset,d.width,d.height);
		if(!translated){
			g.translate(hOffset,vOffset);
			translated = true;
		}
		g.setColor(Color.black);
		for(int i=0;i<5;i++){
			if(i==4) g.setColor(Color.gray);
			g.drawLine(0,basicRect.height*i,basicRect.width*4,basicRect.height*i);
			g.drawLine(basicRect.width*i,0,basicRect.width*i,basicRect.height*4);
		}
		g.setColor(Color.green);
		Geomtry.drawJoint(g,state.toPoint());
		g.setColor(Color.red);
		g.drawRect(currentRect.x,currentRect.y,basicRect.width,basicRect.height);
	}
	private double setSwitchingState(double angle){
		int h = (int)(state.x/basicRect.width);
		int v = (int)(state.y/basicRect.height);
		if(machine.bendStates[0]!=-1+(h/2)*2) machine.switchBend(0);
		if(machine.bendStates[2]!=-1+(h%3)*(3-h)) machine.switchBend(2);
		if (machine.bendStates[1]!=-1+(v/2)*2) machine.switchBend(1);
		if (machine.bendStates[3]!=-1+(v%3)*(3-v)) machine.switchBend(3);
		if (v%2==1)angle = -angle;
		if (h%2==1)angle = Math.PI-angle;
		return angle;
	}
	private double findMachineAngle(double angle){
			angle = Geomtry.getStandartAngle(angle-originAngle);
			int offset = ((int)(angle/(Math.PI/2))+minVertex)%4;
			double dAngle = Geomtry.getStandartAngle(machineAngles[(offset+1)%4]-machineAngles[offset]);
   			double mAngle = (angle-(Math.PI/2)*offset)*dAngle/(Math.PI/2)+machineAngles[offset];
			return mAngle;
	}
	Coordinate rectCenter = new Coordinate();
	public void mouseMoved(MouseEvent e){
		if(move){
			Point p = e.getPoint();
			state.move(Geomtry.modulo(p.x-hOffset,4*basicRect.width),
			Geomtry.modulo(p.y-vOffset,4*basicRect.height));
			currentRect.move(basicRect.width*(int)(state.x/basicRect.width),
				basicRect.height*(int)(state.y/basicRect.height));
			rectCenter.move(currentRect.x+spaceCenter.x,currentRect.y+spaceCenter.y);
			double angle = Geomtry.getStandartAngle(
			setSwitchingState(Geomtry.getAngle(rectCenter,state)));
			double mAngle = findMachineAngle(angle);
			double tangle = angle;
			while(tangle>Math.PI/4) tangle = tangle-Math.PI/2;
			double distance = (basicRect.width/2)/Math.abs(Math.cos(tangle));
			distance = machine.getMaxDistance(mAngle)*Geomtry.distance(rectCenter,state)/distance;
			rectCenter = Geomtry.getPointByVector(machine.origin,distance,mAngle);
			machine.moveCenterEx(rectCenter.x-machine.getJointEx(-1).x,
				rectCenter.y-machine.getJointEx(-1).y);
			for(int i=0;i<controller.frames.length;i++)
				controller.frames[i].drawArea.repaint();
		}
		move = !move;
	}
	public void mouseDragged(MouseEvent e){}										
}