package edu.neu.ccs.demeter.aplib;

import java.util.*;

/**
 * A combination of two traversals.
 */
public abstract class TraversalCombination extends Traversal {
  protected TraversalCombination(Traversal l, Traversal r)
    throws IncompatibleClassGraphsException
  {
    super(l.getClassGraph());
    if (!l.getClassGraph().equals(r.getClassGraph()))
      throw new IncompatibleClassGraphsException(l, r);
    left = l;
    right = r;
  }

  public Traversal getLeftTraversal() { return left; }
  public Traversal getRightTraversal() { return right; }
  Traversal left, right;

  protected abstract List combineNodeSetLists(List l, List r);

  protected abstract Traversal.NodeSet combineNodeSets(Traversal.NodeSet l,
						       Traversal.NodeSet r);

  protected abstract List combineEdgeSetLists(List l, List r);

  protected abstract Traversal.EdgeSet combineEdgeSets(Traversal.EdgeSet l,
						       Traversal.EdgeSet r);

  public List getNodeSets() {
    return combineNodeSetLists(left.getNodeSets(),
			       right.getNodeSets());
  }

  public Traversal.NodeSet getNodeSet(Object v) {
    return combineNodeSets(left.getNodeSet(v),
			   right.getNodeSet(v));
  }

  public List getStartSet() {
    return combineNodeSetLists(left.getStartSet(),
			       right.getStartSet());
  }

  public Traversal.NodeSet getStartSet(Object v) {
    return combineNodeSets(left.getStartSet(v),
			   right.getStartSet(v));
  }

  public List getFinishSet() {
    return combineNodeSetLists(left.getFinishSet(),
			       right.getFinishSet());
  }

  public Traversal.NodeSet getFinishSet(Object v) {
    return combineNodeSets(left.getFinishSet(v),
			   right.getFinishSet(v));
  }

  public class NodeSet extends Traversal.NodeSet {
    NodeSet(Traversal.NodeSet l, Traversal.NodeSet r) {
      super(l.node);
      assert l.sameNode(r);
      left = l;
      right = r;
    }
    public Traversal.NodeSet getLeftNodeSet() { return left; }
    public Traversal.NodeSet getRightNodeSet() { return right; }
    Traversal.NodeSet left, right;

    List getIncidentEdgeSets(boolean outgoing) {
      return combineEdgeSetLists(left.getIncidentEdgeSets(outgoing),
				 right.getIncidentEdgeSets(outgoing));
    }

    String indicesToString() {
      return "(" + left.indicesToString() + ", " +
	right.indicesToString() + ")";
    }
  }

  public List getEdgeSets() {
    return combineEdgeSetLists(left.getEdgeSets(),
			       right.getEdgeSets());
  }

  public Traversal.EdgeSet getEdgeSet(String key) {
    return new EdgeSet(left.getEdgeSet(key),
		       right.getEdgeSet(key));
  }

  public class EdgeSet extends Traversal.EdgeSet {
    EdgeSet(Traversal.EdgeSet l, Traversal.EdgeSet r) {
      super(l.edge);
      assert l.sameEdge(r);
      left = l;
      right = r;
    }
    public Traversal.EdgeSet getLeftEdgeSet() { return left; }
    public Traversal.EdgeSet getRightEdgeSet() { return right; }
    Traversal.EdgeSet left, right;

    public Traversal.NodeSet getTargetNodeSet() {
      return new NodeSet(left.getTargetNodeSet(),
			 right.getTargetNodeSet());
    }

    String indicesToString() {
      return "(" + left.indicesToString() + ", " +
	right.indicesToString() + ")";
    }
  }

}

