/*
 * POO 2009 Pracownia z JHotDraw
 * Autor: Rafał Kozik
 * Czas przewidywany: 60 minut
 * Czas rzeczywisty: 150 minut
 * Powód: brak znajomości języka i środowisk programistycznych
 */

package CH.ifa.draw.samples.net;

import java.awt.*;
import java.util.*;

import CH.ifa.draw.framework.*;
import CH.ifa.draw.standard.*;
import CH.ifa.draw.figures.*;
import CH.ifa.draw.util.*;
import CH.ifa.draw.application.*;

public class NetApp extends DrawApplication {

	NetApp() {
		super("Net");
	}

	protected void createTools(Panel palette) {
		super.createTools(palette);

		Tool tool = new CreationTool(view(), new Bulb());
		palette.add(createToolButton(IMAGES + "RECT", "Żarówki", tool));

		tool = new CreationTool(view(), new Battery());
		palette.add(createToolButton(IMAGES + "RECT", "Baterie", tool));

		tool = new ConnectionTool(view(), new WireConnection());
		palette.add(createToolButton(IMAGES + "CONN", "Connection Tool", tool));
	}

	// -- main -----------------------------------------------------------

	public static void main(String[] args) {
		DrawApplication window = new NetApp();
		window.open();
	}
}

// połączenie między elementami
class WireConnection extends LineConnection {
	protected void handleConnect(Figure start, Figure end) {
		ElectroFigure a = (ElectroFigure) start;
		ElectroFigure b = (ElectroFigure) end;

		a.addConnection(b);
		b.addConnection(a);
	}

	protected void handleDisconnect(Figure start, Figure end) {
		ElectroFigure a = (ElectroFigure) start;
		ElectroFigure b = (ElectroFigure) end;

		a.removeConnection(b);
		b.removeConnection(a);
	}
	
	public Vector handles()
	{
		return new Vector();
	}
}

// bateria
class Battery extends ElectroFigure {
	public Vector handles() {
		Vector handles = new Vector();
		handles.addElement(new ConnectionHandle(this, RelativeLocator.north(),
				new WireConnection()));
		return handles;
	}

	protected void createConnectors() {
		fConnectors = new Vector();
		fConnectors.add(new LocatorConnector(this, RelativeLocator.north()));
	}
}

// żarówka
class Bulb extends ElectroFigure {
	// przeszukanie czy da się po kabelkach dojść do baterii
	boolean isPowered() {
		HashSet<Figure> visited = new HashSet<Figure>();
		LinkedList<Figure> queue = new LinkedList<Figure>();
		
		queue.push(this);
		
		while (!queue.isEmpty())
		{
			Figure f = queue.pop();
			
			if (visited.contains(f)) continue;
			
			if (f instanceof Battery) return true;
			
			visited.add(f);
			
			ElectroFigure ef = (ElectroFigure) f;

			Enumeration<Figure> e = ef.wiredConnections.elements();
			
			while (e.hasMoreElements())
				queue.add((Figure) e.nextElement());
		}
		
		return false;
	}

	public void drawBackground(Graphics g) {
		Rectangle r = displayBox();
		g.setColor(isPowered() ? Color.yellow : Color.white);
		g.fillOval(r.x, r.y, r.width, r.height);
	}

	public void drawFrame(Graphics g) {
		Rectangle r = displayBox();
		g.drawOval(r.x, r.y, r.width, r.height);
	}

	public Vector handles() {
		Vector handles = new Vector();
		handles.addElement(new ConnectionHandle(this, RelativeLocator.south(),
				new WireConnection()));
		return handles;
	}

	protected void createConnectors() {
		fConnectors = new Vector();
		fConnectors.add(new LocatorConnector(this, RelativeLocator.south()));
	}
}


// kod bazujący na RectangleFigure i NodeFigure
class ElectroFigure extends AttributeFigure {

	private Rectangle fDisplayBox;
	protected Vector fConnectors;
	protected boolean fConnectorsVisible;

	public ElectroFigure() {
		this(new Point(0, 0), new Point(0, 0));
	}

	public ElectroFigure(Point origin, Point corner) {
		basicDisplayBox(origin, corner);
	}

	public void basicDisplayBox(Point origin, Point corner) {
		fDisplayBox = new Rectangle(origin, new Dimension(40, 40));
	}

	public Vector handles() {
		return new Vector();
	}

	public Rectangle displayBox() {
		return new Rectangle(fDisplayBox.x, fDisplayBox.y, fDisplayBox.width,
				fDisplayBox.height);
	}

	protected void basicMoveBy(int x, int y) {
		fDisplayBox.translate(x, y);
	}

	public void drawBackground(Graphics g) {
		Rectangle r = displayBox();
		g.fillRect(r.x, r.y, r.width, r.height);
	}

	public void drawFrame(Graphics g) {
		Rectangle r = displayBox();
		g.drawRect(r.x, r.y, r.width - 1, r.height - 1);
	}

	public void connectorVisibility(boolean isVisible) {
		fConnectorsVisible = isVisible;
		invalidate();
	}

	private Vector connectors() {
		if (fConnectors == null)
			createConnectors();
		return fConnectors;
	}

	protected void createConnectors() {
		fConnectors = new Vector();
	}

	private Connector findConnector(int x, int y) {
		// return closest connector
		long min = Long.MAX_VALUE;
		Connector closest = null;

		Enumeration e = connectors().elements();
		while (e.hasMoreElements()) {
			Connector c = (Connector) e.nextElement();
			Point p2 = Geom.center(c.displayBox());
			long d = Geom.length2(x, y, p2.x, p2.y);
			if (d < min) {
				min = d;
				closest = c;
			}
		}
		return closest;
	}

	private void drawConnectors(Graphics g) {
		if (fConnectorsVisible) {
			Enumeration e = connectors().elements();
			while (e.hasMoreElements())
				((Connector) e.nextElement()).draw(g);
		}
	}

	public void draw(Graphics g) {
		super.draw(g);
		drawConnectors(g);
	}

	public Connector connectorAt(int x, int y) {
		return findConnector(x, y);
	}

	protected Vector<Figure> wiredConnections = new Vector<Figure>();

	public void addConnection(Figure f) {
		if (!wiredConnections.contains(f)) {
			wiredConnections.add(f);
			invalidateGraph();
		}
	}

	public void removeConnection(Figure f) {
		if (wiredConnections != null && wiredConnections.contains(f))
		{
			wiredConnections.remove(f);
			invalidateGraph();
		}
	}
	
	// odświeżenie wszystkich elementów do których da się dojść z tego elementu
	public void invalidateGraph()
	{
		HashSet<Figure> visited = new HashSet<Figure>();
		LinkedList<Figure> queue = new LinkedList<Figure>();
		
		queue.push(this);
		
		while (!queue.isEmpty())
		{
			Figure f = queue.pop();
			
			if (visited.contains(f)) continue;
			
			f.invalidate();
			
			visited.add(f);
			
			ElectroFigure ef = (ElectroFigure) f;

			Enumeration<Figure> e = ef.wiredConnections.elements();
			
			while (e.hasMoreElements())
				queue.add((Figure) e.nextElement());
		}
	}
}