package horstmann.ch08_graphed;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
   A graph consisting of selectable nodes and edges.
 */
public abstract class Graph implements Serializable
{
	private static final long serialVersionUID = 2008L;
	/**
      Constructs a graph with no nodes or edges.
	 */
	public Graph()
	{
		nodes = new ArrayList<Node>();
		edges = new ArrayList<Edge>();
	}

	/**
      Adds an edge to the graph that joins the nodes containing
      the given points. If the points aren't both inside nodes,
      then no edge is added.
      @param e the edge to add
      @param p1 a point in the starting node
      @param p2 a point in the ending node
	 */
	public boolean connect(Edge e, Point2D p1, Point2D p2)
	{
		Node n1 = findNode(p1);
		Node n2 = findNode(p2);
		if (n1 != null && n2 != null)
		{
			e.connect(n1, n2);
			edges.add(e);
			return true;
		}
		return false;
	}

	/**
      Adds a node to the graph so that the top left corner of
      the bounding rectangle is at the given point.
      @param n the node to add
      @param p the desired location
	 */
	public boolean add(Node n, Point2D p)
	{
		Rectangle2D bounds = n.getBounds();
		n.translate(p.getX() - bounds.getX(),
				p.getY() - bounds.getY());
		nodes.add(n);
		return true;
	}

	/**
      Finds a node containing the given point.
      @param p a point
      @return a node containing p or null if no nodes contain p
	 */
	public Node findNode(Point2D p)
	{
		for (int i = nodes.size() - 1; i >= 0; i--)
		{
			Node n = nodes.get(i);
			if (n.contains(p)) return n;
		}
		return null;
	}

	/**
      Finds an edge containing the given point.
      @param p a point
      @return an edge containing p or null if no edges contain p
	 */
	public Edge findEdge(Point2D p)
	{
		for (int i = edges.size() - 1; i >= 0; i--)
		{
			Edge e = edges.get(i);
			if (e.contains(p)) return e;
		}
		return null;
	}

	/**
      Draws the graph
      @param g2 the graphics context
	 */
	public void draw(Graphics2D g2)
	{
		for (Node n : nodes)
			n.draw(g2);

		for (Edge e : edges)
			e.draw(g2);

	}

	/**
      Removes a node and all edges that start or end with that node
      @param n the node to remove
	 */
	public void removeNode(Node n)
	{
		for (int i = edges.size() - 1; i >= 0; i--)
		{
			Edge e = edges.get(i);
			if (e.getStart() == n || e.getEnd() == n)
				edges.remove(e);
		}
		nodes.remove(n);
	}

	/**
      Removes an edge from the graph.
      @param e the edge to remove
	 */
	public void removeEdge(Edge e)
	{
		edges.remove(e);
	}

	/**
      Gets the smallest rectangle enclosing the graph
      @param g2 the graphics context
      @return the bounding rectangle
	 */
	public Rectangle2D getBounds(Graphics2D g2)
	{
		Rectangle2D r = null;
		for (Node n : nodes)
		{
			Rectangle2D b = n.getBounds();
			if (r == null) r = b;
			else r.add(b);
		}
		for (Edge e : edges)
			r.add(e.getBounds(g2));
		return r == null ? new Rectangle2D.Double() : r;
	}

	/**
      Gets the node types of a particular graph type.
      @return an array of node prototypes
	 */
	public abstract Node[] getNodePrototypes();

	/**
      Gets the edge types of a particular graph type.
      @return an array of edge prototypes
	 */
	public abstract Edge[] getEdgePrototypes();

	/**
      Gets the nodes of this graph.
      @return an unmodifiable list of the nodes
	 */
	public List<Node> getNodes()
	{
		return Collections.unmodifiableList(nodes); }

	/**
      Gets the edges of this graph.
      @return an unmodifiable list of the edges
	 */
	public List<Edge> getEdges()
	{
		return Collections.unmodifiableList(edges);
	}

	private ArrayList<Node> nodes;
	private ArrayList<Edge> edges;
}





