package net.hydromatic.clapham.chart.draw2d;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import net.hydromatic.clapham.chart.ChartOptions;
public class TracePath {
private List<Path> pathList;
private ChartOptions options;
private static class XYMatcher {
int x;
int y;
public XYMatcher(int x, int y) {
this.x = x;
this.y = y;
}
public boolean match(Path path) {
return x == path.x && y == path.y;
}
}
private static class X2YMatcher extends XYMatcher {
public X2YMatcher(int x, int y) {
super(x, y);
}
@Override
public boolean match(Path path) {
return x == ((LinePath) path).x2 && y == path.y;
}
}
private interface Trace<P extends Path> {
public Map<String, Collection<Path>> trace(P path);
}
private class RerunTrace implements Trace<ArcPath> {
public Map<String, Collection<Path>> trace(ArcPath path) {
Map<String, Collection<Path>> result = createMapResult();
LinePath lp = null;
ArcPath ap = null;
Path p = path;
int start = pathList.indexOf(p);
// the top-right corner
result.get("path").add(p);
// find the vertical line between the arcs
start = findLine(start, p.x + options.arcSize(), p.y
+ options.arcSize() / 2);
result.get("path").add(p = pathList.get(start));
// the bottom-right arc
lp = (LinePath) p;
// override start and start from the beginning
start = pathList.indexOf(path) + 1;
start = findArc(start, path.x, lp.y2 - options.arcSize() / 2 - 1);
result.get("path").add(p = pathList.get(start));
// start from the beginning
ap = (ArcPath) p;
start = findLine(0, new X2YMatcher(ap.x + options.arcSize() / 2
+ 1, ap.y + options.arcSize()));
result.get("path").add(p = pathList.get(start));
lp = (LinePath) p;
return result;
}
}
private class OptionTrace implements Trace<ArcPath> {
public Map<String, Collection<Path>> trace(ArcPath path) {
LinePath lp = null;
Map<String, Collection<Path>> result = createMapResult();
int start = pathList.indexOf(path);
// the left-top arc
result.get("path").add(pathList.get(start));
// the vertical line
Path p = null;
start = findLine(start, path.x + path.width, path.y + path.height
/ 2);
result.get("path").add(p = pathList.get(start));
// the left-bottom arc
lp = (LinePath) p;
start = findArc(start, p.x, lp.y2 - options.arcSize() / 2 - 1);
result.get("path").add(p = pathList.get(start));
// the long horizontal line
ArcPath ap = (ArcPath) p;
start = findLine(start, ap.x + ap.width / 2, ap.y + ap.height);
result.get("path").add(p = pathList.get(start));
// the bottom-right corner
lp = (LinePath) p;
start = findArc(start, lp.x2 - options.arcSize() / 2 - 1, lp.y
- options.arcSize());
result.get("path").add(p = pathList.get(start));
// the vertical line between the arcs
ap = (ArcPath) p;
start = findLine(start, ap.x + options.arcSize(), path.y
+ options.arcSize() / 2);
result.get("path").add(p = pathList.get(start));
// the top-right arc
lp = (LinePath) p;
start = findArc(start - 1, lp.x, lp.y - options.arcSize() / 2);
result.get("path").add(p = pathList.get(start));
return result;
}
}
public TracePath(ChartOptions options) {
pathList = new ArrayList<Path>();
this.options = options;
}
public TracePath add(Path path) {
pathList.add(path);
return this;
}
private int findArc(int start, int x, int y) {
return find(ArcPath.class, start, x, y);
}
private int findArc(int start, XYMatcher matcher) {
return find(ArcPath.class, start, matcher);
}
private int findLine(int start, int x, int y) {
return find(LinePath.class, start, x, y);
}
private int findLine(int start, XYMatcher matcher) {
return find(LinePath.class, start, matcher);
}
private <P extends Path> int find(Class<P> type, int start, int x, int y) {
return find(type, start, new XYMatcher(x, y));
}
private <P extends Path> int find(Class<P> type, int start,
XYMatcher matcher) {
while (start < pathList.size()) {
Path nextp = pathList.get(start++);
if (type.isInstance(nextp)) {
if (matcher.match(nextp)) {
return start - 1;
}
}
}
throw new IllegalStateException();
}
private Map<String, Collection<Path>> trace(LinePath path) {
Map<String, Collection<Path>> result = createMapResult();
int start = pathList.indexOf(path) + 1;
Path prev = path;
result.get("path").add(path);
for (int i = start; i < pathList.size(); i++) {
Path p = pathList.get(i);
if (prev.next(p)) {
result.get("path").add(p);
if (!(p instanceof LabeledPath)) {
prev = p;
}
} else if (prev.isAlt(p)) {
result.get("alt").add(p);
}
}
return result;
}
@SuppressWarnings("unchecked")
public Map<String, Collection<Path>> trace(Path path) {
Collections.sort(pathList);
if (path instanceof LinePath) {
return trace((LinePath) path);
} else if (path instanceof ArcPath) {
Trace[] trace = { new OptionTrace(), new RerunTrace() };
for (int i = 0; i < trace.length; i++) {
try {
return trace[i].trace((ArcPath) path);
} catch (IllegalStateException e) {
// just ignore and try the next trace
}
}
// no luck!
return createMapResult();
}
throw new UnsupportedOperationException(path.toString());
}
private Map<String, Collection<Path>> createMapResult() {
Map<String, Collection<Path>> result = new HashMap<String, Collection<Path>>();
result.put("path", new LinkedHashSet<Path>());
result.put("alt", new LinkedHashSet<Path>());
return result;
}
@Override
public String toString() {
Collections.sort(pathList);
return pathList.toString();
}
public Path first() {
return pathList.get(0);
}
}