package l23_3;

import java.util.ArrayList;

public class Graph {
	private ArrayList<GraphNode> nodes;
	private static boolean trace;

	public Graph() {
		nodes=new ArrayList<GraphNode>();
	}

	public static void startTrace() { trace = true; }

	public static void stopTrace() { trace = false; }

	public void addNode(int payload) {
		assert(findNode(payload)==null); // payload must be unique
		nodes.add(new GraphNode(payload));
	}
	
	public void addRef(int from,int to) {
		GraphNode fromNode=findNode(from);
		assert(fromNode!=null);
		GraphNode toNode=findNode(to);
		assert(toNode!=null);
		fromNode.addRef(toNode);
	}
	
	private GraphNode findNode(int payload) {
		for(GraphNode node : nodes) {
			if (node.getPayload()==payload) return node;
		}
		return null;
	}
	
	public int size() { return nodes.size(); }
	
	public boolean isReachable(GraphNode from,GraphNode to,int steps) {
		if (trace) System.out.println("isReachable(" + from.payload + "," + to.payload + "," + steps + ")");
		if (steps==0) return false;
		for(GraphNode next : from.refs) {
			if (next==to) return true;
			if (isReachable(next,to,steps-1)) return true;
		}
		return false;
	}
	
	public boolean containsCycles() {
		for(GraphNode from : nodes) {
			if (isReachable(from,from,nodes.size())) return true;
		}
		return false;
	}
	
	public int numRefs(int payload) {
		return findNode(payload).numRefs();
	}

	@Override
	public String toString() {
		if (nodes.isEmpty()) return "empty";
		String result="";
		for(GraphNode node : nodes) {
			result += node.toString();
		}
		return result;
	}
	
	private class GraphNode {
		private int payload;
		private ArrayList<GraphNode> refs;

		public GraphNode(int payload) {
			this.payload = payload;
			this.refs = new ArrayList<GraphNode>();
		}

		public void addRef(GraphNode to) { refs.add(to); }
		
		public int numRefs() { return refs.size(); }

		public int getPayload() { return payload; }

		@Override
		public String toString() {
			String result="[" + payload + "->";
			boolean comma=false;
			for (GraphNode to : refs) {
				if (comma) result+=",";
				comma=true;
				result+= to.payload;
			}
			return result+"]";
		}

	}

}
