import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

// Representation graphique d'une liaison, herite de GCircuitElement
public class GLiaison extends GCircuitElement {

	// GPin de la liaison
	GPin gpa, gpb;
	
	// Liaison
	Liaison link;
	
	// Valeur de la liaison
	boolean on;
	
	// Ligne
	static Stroke stroke = new BasicStroke(10.0f);
	
	// Trajectoire de base
	int[] pts;
	// Offset (deplacement d'un bout de la liaison avec la souris)
	int[] os;
	
	// Constructeur
	public GLiaison(GPin gpa, GPin gpb) throws Exception {
		super(gpa.canvas, new Liaison(gpa.pin, gpb.pin));
		this.gpa = gpa;
		this.gpb = gpb;
		link = (Liaison) ce;
		pts = new int[5];
		os = new int[3];
		if(gpa.pin.isOut()) 		os[2] = -(os[0] = 50);
		else if(gpb.pin.isOut())	os[2] = -(os[0] = -50);
		// On ecoute la liaison et les pins
		link.addCircuitElementListener(ceListener);
		gpa.parent.addClipListener(clipListener);
		gpb.parent.addClipListener(clipListener);
		gpa.pin.addPinListener(pinListener);
		gpb.pin.addPinListener(pinListener);
		on = gpa.pin.isOn() || gpb.pin.isOn();
		// Calcul de la trajectoire
		calculateTraject();
		gpa.canvas.redraw();
		gpa.canvas.refresh();
	}
	
	// Creation de l'image de base (pour la mini-map)
	public void initImg(){
		if(isDisposed) return;
		if(pts == null) return;
		
		width = height = 1000;
		
		BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = (Graphics2D)img.getGraphics();
		
		g.setColor(Color.black);
		g.setStroke(stroke);
		
		int px = width/2, py = height/2;
		for(int i=0; i<pts.length; i++){
			if(i%2==0)
				g.drawLine(px, py, px+=pts[i], py);
			else
				g.drawLine(px, py, px, py+=pts[i]);
		}
		
		g.dispose();
		
		i_base = img;
	}

	
	Clip.ClipListener clipListener = new Clip.ClipListener() {
		public void moved(Clip clip) {
			calculateTraject(); // Si un des clips a bouge, alors on recalcule la trajectoire
			Invalidate();
		}
		public void disposed(Clip clip) {
			Dispose();
		}
	};
	
	Pin.PinListener pinListener = new Pin.PinListener() {
		public void stateChanged(Pin pin){
			/*if(on != pin.isOn()){ // "On" si au moins un des pin est alimente
				on = pin.isOn();
				Invalidate();
			}*/
			if(gpa.pin.isOn() && gpb.pin.isOn()){ // "On" si les 2 pins sont alimentes
				if(!on){
					on = true;
					Invalidate();
				}
			}else{
				if(on){
					on = false;
					Invalidate();
				}
			}
		}
		public void disposed(Pin pin){}
		public boolean askState(Pin pin){return false;}
	};
	
	CircuitElement.CircuitElementListener ceListener = new CircuitElement.CircuitElementListener() {
		public void disposed(CircuitElement circuitElement) {
			if(!isDisposed()) Dispose(); // Si un des composants de la liaison a ete detruit alors la liaison n'a plus lieu d'etre
		}
	};
	
	// Destruction de la representation graphique de la liaison
	public void Dispose(){
		if(isDisposed()) return;
		super.Dispose();
		// Destruction de la liaison
		link.Dispose();
		// On arrete de tout ecouter
		link.remCircuitElementListener(ceListener);
		gpa.parent.remClipListener(clipListener);
		gpb.parent.remClipListener(clipListener);
		gpa.pin.remPinListener(pinListener);
		gpb.pin.remPinListener(pinListener);
		gpa = null;
		gpb = null;
		on = false;
		pts = null;
		os = null;
		clipListener = null;
		ceListener = null;
		pinListener = null;
	}
	
	// Calcul de la trajectoire
	public void calculateTraject(){
		if(isDisposed()) return;
		
		// Points de depart et d'arrivee
		int pax = gpa.x+gpa.parent.x+gpa.width/2, pay = gpa.y+gpa.parent.y+ gpa.height/2;
		int pbx = gpb.x+gpb.parent.x+gpb.width/2, pby = gpb.y+gpb.parent.y+ gpb.height/2;
		// Difference a parcourir
		int dx = pbx-pax;
		int dy = pby-pay;
		
		// Points de depart
		x = pax;
		y = pay;
		
		// Liaison divisee en 5 parties
		pts[0] = (int)Math.floor(dx/10.0)		+ os[0];
		pts[1] = dy/2							+ os[1];
		pts[2] = (int)Math.ceil((8.0*dx)/10)	- os[0] + os[2];
		pts[3] = pby-(y+pts[1]);
		pts[4] = pbx-(x+pts[0]+pts[2]);
		
		// On met a jour l'image de base
		initImg();
	}
	
	public void repaint(Graphics2D g) {
		if(isDisposed()) return;
		try{
			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
			
			if(isHover() && isSelected()) g.setColor(Color.orange);
			else if(isHover())	g.setColor(Color.pink);
			else if(isSelected())	g.setColor(Color.red);
			else if(on)	g.setColor(Color.green);
			else	g.setColor(Color.black);
			g.setStroke(stroke);
			
			int px = x, py = y;
			for(int i=0; i<pts.length; i++){
				if(i%2==0)
					g.drawLine(px, py, px+=pts[i], py);
				else
					g.drawLine(px, py, px, py+=pts[i]);
			}
			
			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		}catch(Exception ex){}
	}
		
	int oOf;
	int selectedLine;
	public boolean mousePressed(MouseEvent me) {
		if(isDisposed()) return false;
		selectedLine = -1;
		int px = x + pts[0], py = y;
		for(int i=1; i<pts.length-1; i++){
			if(i%2==0){
				if(hitTest(new Rectangle(px, py-5, pts[i], 10), me.getPoint()))
					{ selectedLine = i-1;	break; }
				px+=pts[i];
			}
			else{
				if(hitTest(new Rectangle(px-5, py, 10, pts[i]), me.getPoint()))
					{ selectedLine = i-1;	break; }
				py+=pts[i];
			}
		}
		if(selectedLine<0) return false;
		oOf = os[selectedLine];
		pm = selectedLine%2==1?me.getY():me.getX();
		return true;
	}

	public boolean mouseReleased(MouseEvent me) {
		if(isDisposed()) return false;
		if(selectedLine < 0) return false;
		selectedLine = -1;
		return true;
	}
	int pm;
	public boolean mouseDragged(MouseEvent me){
		if(isDisposed()) return false;
		if(selectedLine < 0) return false;
		os[selectedLine] += (selectedLine%2==1?me.getY():me.getX())-pm;
		pm = selectedLine%2==1?me.getY():me.getX();
		calculateTraject();
		Invalidate();
		return true;
	}
	
	// Verification de quelle partie de la liaison est sous la souris
	public boolean hitTest(int x, int y){
		if(isDisposed()) return false;
		if(pts==null) return false;
		selectedLine = -2;
		int px = 0, py = 0;
		for(int i=0; i<pts.length; i++){
			if(i%2==0){
				if(hitTest(new Rectangle(px, py-5, pts[i], 10), new Point(x,y)))
					{ selectedLine = i-1; break; }
				px+=pts[i];
			}else{
				if(hitTest(new Rectangle(px-5, py, 10, pts[i]), new Point(x,y)))
					{ selectedLine = i-1; break; }
				py+=pts[i];
			}
		}
		return selectedLine != -2;
	}
	
	// Recuperation si la liaison est sous la souris
	public Object getItem(MouseEvent me) {
		if(isDisposed()) return null;
		if(hitTest(me.getPoint().x - x, me.getPoint().y - y)) return this;
		return null;
	}
}
