/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.tools.move;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.cburch.logisim.circuit.Circuit;
import com.cburch.logisim.circuit.Wire;
import com.cburch.logisim.comp.Component;
import com.cburch.logisim.comp.EndData;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
public class MoveGesture {
private MoveRequestListener listener;
private Circuit circuit;
private HashSet<Component> selected;
private transient Set<ConnectionData> connections;
private transient AvoidanceMap initAvoid;
private HashMap<MoveRequest,MoveResult> cachedResults;
public MoveGesture(MoveRequestListener listener, Circuit circuit,
Collection<Component> selected) {
this.listener = listener;
this.circuit = circuit;
this.selected = new HashSet<Component>(selected);
this.connections = null;
this.initAvoid = null;
this.cachedResults = new HashMap<MoveRequest,MoveResult>();
}
HashSet<Component> getSelected() {
return selected;
}
AvoidanceMap getFixedAvoidanceMap() {
AvoidanceMap ret = initAvoid;
if (ret == null) {
HashSet<Component> comps = new HashSet<Component>(circuit.getNonWires());
comps.addAll(circuit.getWires());
comps.removeAll(selected);
ret = AvoidanceMap.create(comps, 0, 0);
initAvoid = ret;
}
return ret;
}
Set<ConnectionData> getConnections() {
Set<ConnectionData> ret = connections;
if (ret == null) {
ret = computeConnections(circuit, selected);
connections = ret;
}
return ret;
}
public MoveResult findResult(int dx, int dy) {
MoveRequest request = new MoveRequest(this, dx, dy);
synchronized (cachedResults) {
return cachedResults.get(request);
}
}
public boolean enqueueRequest(int dx, int dy) {
MoveRequest request = new MoveRequest(this, dx, dy);
synchronized (cachedResults) {
Object result = cachedResults.get(request);
if (result == null) {
ConnectorThread.enqueueRequest(request, false);
return true;
} else {
return false;
}
}
}
public MoveResult forceRequest(int dx, int dy) {
MoveRequest request = new MoveRequest(this, dx, dy);
ConnectorThread.enqueueRequest(request, true);
synchronized (cachedResults) {
Object result = cachedResults.get(request);
while (result == null) {
try {
cachedResults.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
result = cachedResults.get(request);
}
return (MoveResult) result;
}
}
void notifyResult(MoveRequest request, MoveResult result) {
synchronized (cachedResults) {
cachedResults.put(request, result);
cachedResults.notifyAll();
}
if (listener != null) {
listener.requestSatisfied(this, request.getDeltaX(), request.getDeltaY());
}
}
private static Set<ConnectionData> computeConnections(Circuit circuit,
Set<Component> selected) {
if (selected == null || selected.isEmpty()) return Collections.emptySet();
// first identify locations that might be connected
Set<Location> locs = new HashSet<Location>();
for (Component comp : selected) {
for (EndData end : comp.getEnds()) {
locs.add(end.getLocation());
}
}
// now see which of them require connection
Set<ConnectionData> conns = new HashSet<ConnectionData>();
for (Location loc : locs) {
boolean found = false;
for (Component comp : circuit.getComponents(loc)) {
if (!selected.contains(comp)) {
found = true;
break;
}
}
if (found) {
List<Wire> wirePath;
Location wirePathStart;
Wire lastOnPath = findWire(circuit, loc, selected, null);
if (lastOnPath == null) {
wirePath = Collections.emptyList();
wirePathStart = loc;
} else {
wirePath = new ArrayList<Wire>();
Location cur = loc;
for (Wire w = lastOnPath; w != null;
w = findWire(circuit, cur, selected, w)) {
wirePath.add(w);
cur = w.getOtherEnd(cur);
}
Collections.reverse(wirePath);
wirePathStart = cur;
}
Direction dir = null;
if (lastOnPath != null) {
Location other = lastOnPath.getOtherEnd(loc);
int dx = loc.getX() - other.getX();
int dy = loc.getY() - other.getY();
if (Math.abs(dx) > Math.abs(dy)) {
dir = dx > 0 ? Direction.EAST : Direction.WEST;
} else {
dir = dy > 0 ? Direction.SOUTH : Direction.NORTH;
}
}
conns.add(new ConnectionData(loc, dir, wirePath, wirePathStart));
}
}
return conns;
}
private static Wire findWire(Circuit circ, Location loc,
Set<Component> ignore, Wire ignoreW) {
Wire ret = null;
for (Component comp : circ.getComponents(loc)) {
if (!ignore.contains(comp) && comp != ignoreW) {
if (ret == null && comp instanceof Wire) {
ret = (Wire) comp;
} else {
return null;
}
}
}
return ret;
}
}