/* File: NetworkPane.java
 * Author: Jason Gookins
 * Description: This class creates the pane that the network is actually displayed on.
 */

import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Robot;
import java.awt.geom.Line2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyVetoException;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class NetworkPane extends JPanel implements ActionListener, MouseListener, MouseMotionListener
{
	/**********************
	 ** Global Variables **
	 **********************/

	private static final Cursor normalCursor = new Cursor(Cursor.DEFAULT_CURSOR);
	private static final Cursor createNodeCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
	private static final Cursor dragNodeCursor = new Cursor(Cursor.MOVE_CURSOR);
	private static final Cursor dragEdgeCursor = new Cursor(Cursor.HAND_CURSOR);



	/************************
	 ** Instance Variables **
	 ************************/

	private Belief belief;
	private BeliefListener beliefListener;

	private ArrayList<Node> nodes;
	private ArrayList<Edge> edges;

	private Node clickedNode;
	private Edge clickedEdge;

	private boolean dragging;
	private int xGrab;
	private int yGrab;
	private int prevDragX;
	private int prevDragY;

	private boolean newEdge;
	private Node edgeChildNode;
	private Point edgeCurrentEnd;
	private Node edgeParentNode;

	private boolean newGroup;
	private boolean currentGroup;
	private DragGroup dragGroup;

	private JPopupMenu panePopup;
	private JPopupMenu nodePopup;
	private JPopupMenu edgePopup;
	private JPopupMenu distPopup;

	private boolean networkRan;
	private boolean areNamesDisplayed;

	private Robot robot;



	/**********************
	 ** Main Constructor **
	 **********************/

	public NetworkPane(Belief belief)
	{
		super();

		this.belief = belief;
		this.beliefListener = belief.getBeliefListener();

		this.nodes = new ArrayList<Node>();
		this.edges = new ArrayList<Edge>();

		this.clickedNode = null;
		this.clickedEdge = null;

		this.dragging = false;
		this.xGrab = 0;
		this.yGrab = 0;
		this.prevDragX = 0;
		this.prevDragY = 0;

		this.newEdge = false;
		this.edgeChildNode = null;
		this.edgeCurrentEnd = null;
		this.edgeParentNode = null;

		this.newGroup = false;
		this.currentGroup = false;
		this.dragGroup = new DragGroup();

		this.networkRan = false;
		this.areNamesDisplayed = false;

		setCursor(normalCursor);
		setLayout(null);
		setBackground(Color.white);
		setAutoscrolls(true);

		panePopup = new JPopupMenu();
		JMenuItem addNode = new JMenuItem("Add Node");
		panePopup.add(addNode).addActionListener(this);

		nodePopup = new JPopupMenu();
		JMenuItem addEdge = new JMenuItem("Add Edge");
		nodePopup.add(addEdge).addActionListener(this);
		JMenuItem editNode = new JMenuItem("Edit Node");
		nodePopup.add(editNode).addActionListener(this);
		nodePopup.addSeparator();
		JMenuItem deleteNode = new JMenuItem("Delete Node");
		nodePopup.add(deleteNode).addActionListener(this);

		edgePopup = new JPopupMenu();
		JMenuItem reverseEdge = new JMenuItem("Reverse Edge");
		edgePopup.add(reverseEdge).addActionListener(this);
		edgePopup.addSeparator();
		JMenuItem deleteEdge = new JMenuItem("Delete Edge");
		edgePopup.add(deleteEdge).addActionListener(this);

		distPopup = new JPopupMenu();
		JMenuItem viewDistribution = new JMenuItem("View Distribution");
		distPopup.add(viewDistribution).addActionListener(this);

		addContainerListener(beliefListener);
		addKeyListener(beliefListener);
		addMouseListener(beliefListener);
		addMouseListener(this);
		addMouseMotionListener(this);

		try
		{
			this.robot = new Robot();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}



	/***************
	 ** Accessors **
	 ***************/

	public ArrayList<Node> getNodes()
	{
		return this.nodes;
	}

	public ArrayList<Edge> getEdges()
	{
		return this.edges;
	}

	public boolean getAreNamesDisplayed()
	{
		return this.areNamesDisplayed;
	}

	public boolean getNetworkRan()
	{
		return this.networkRan;
	}

	public Node getClickedNode()
	{
		return this.clickedNode;
	}



	/**************
	 ** Mutators **
	 **************/

	public void setNodes(ArrayList<Node> newNodes)
	{
		this.nodes = newNodes;
	}

	public void setEdges(ArrayList<Edge> newEdges)
	{
		this.edges = newEdges;
	}

	public void setAreNamesDisplayed(boolean newAreNamesDisplayed)
	{
		this.areNamesDisplayed = newAreNamesDisplayed;
	}

	public void setNetworkRan(boolean newNetworkRan)
	{
		this.networkRan = newNetworkRan;
	}



	/*********************************
	 ** NetworkPane Painting Method **
	 *********************************/

	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);

		Graphics2D g2 = (Graphics2D)g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

		if (newEdge)
		{
			g2.setPaint(Color.BLACK);
			g2.draw(new Line2D.Double(edgeChildNode.center().x, edgeChildNode.center().y, edgeCurrentEnd.x, edgeCurrentEnd.y));
		}
		else if (newGroup || currentGroup)
		{
			dragGroup.paintComponent(g);
		}

		for (Edge edge : edges)
		{
			edge.paintComponent(g);
		}

		for (Node node : nodes)
		{
			node.paintComponent(g, areNamesDisplayed);
		}

		this.requestFocus();
	}



	/******************
	 ** Node Methods **
	 ******************/

	public void createNewNode(int xPos, int yPos, String newNodeName)
	{
		Node newNode = new Node();

		newNode.setName(newNodeName);
		newNode.setXPos(xPos - 25);
		newNode.setYPos(yPos - 25);

		addNode(newNode);

		repaint();
	}

	public void addNode(Node newNode)
	{
		nodes.add(newNode);
		newNode.setIndex(nodes.indexOf(newNode));
		this.add(newNode);
	}

	public void editNode(String originalNodeName)
	{
		beliefListener.getListPane().updateNodeInLists(originalNodeName, clickedNode);

		repaint();
	}

	public void deleteNode(Node deletedNode)
	{
		for (Node parent : deletedNode.getParents())
		{
			verifyOperations(parent);
		}

		for (int i = edges.size() - 1; i >= 0; i--)
		{
			Edge edge = edges.get(i);

			if (edge.getChildNode().equals(deletedNode) || edge.getParentNode().equals(deletedNode))
			{
				deleteEdge(edge);
			}
		}

		this.remove(deletedNode);
		nodes.remove(deletedNode);

		for (Node node : nodes)
		{
			node.setIndex(nodes.indexOf(node));
		}

		repaint();
	}

	public boolean isNodeHit(int x, int y)
	{
		boolean isNodeHit = false;
		clickedNode = null;

		for (Node node : nodes)
		{
			if (node.contains(x, y))
			{
				isNodeHit = true;
				clickedNode = node;

				break;
			}
		}

		return isNodeHit;
	}



	/******************
	 ** Edge Methods **
	 ******************/

	public void addEdge(Edge newEdge)
	{
		Node parentNode = newEdge.getParentNode();
		Node childNode = newEdge.getChildNode();

		parentNode.getChildren().add(childNode);
		childNode.getParents().add(parentNode);

		if (parentNode.getHasGiven() == 1)
		{
			beliefListener.getCurrentView().getGivenDistributions().remove(beliefListener.getCurrentView().getGivenDistNodes().indexOf(parentNode));
			beliefListener.getCurrentView().getGivenDistNodes().remove(parentNode);
			parentNode.setHasGiven(0);
		}

		edges.add(newEdge);
	}

	public void reverseEdge(Edge reversedEdge)
	{
		Node parentNode = reversedEdge.getParentNode();
		Node childNode = reversedEdge.getChildNode();

		verifyOperations(parentNode);

		parentNode.getChildren().remove(childNode);
		parentNode.getParents().add(childNode);
		childNode.getParents().remove(parentNode);
		childNode.getChildren().add(parentNode);

		reversedEdge.reverse();

		repaint();
	}

	public void deleteEdge(Edge deletedEdge)
	{
		Node parentNode = deletedEdge.getParentNode();
		Node childNode = deletedEdge.getChildNode();

		verifyOperations(parentNode);

		parentNode.getChildren().remove(childNode);
		childNode.getParents().remove(parentNode);

		edges.remove(deletedEdge);

		repaint();
	}

	public boolean isEdgeHit(int x, int y)
	{
		boolean isEdgeHit = false;
		clickedEdge = null;

		for (Edge edge : edges)
		{
			if ((edge.containsPoint(x, y) >= 0.0) && (edge.containsPoint(x, y) <= 100.0))
			{
				isEdgeHit = true;
				clickedEdge = edge;

				break;
			}
		}

		return isEdgeHit;
	}

	public boolean edgeExists(Node childNode, Node parentNode)
	{
		boolean edgeExists = false;

		for (Edge edge : edges)
		{
			if ((edge.getChildNode().equals(childNode) && edge.getParentNode().equals(parentNode)) || (edge.getChildNode().equals(parentNode) && edge.getParentNode().equals(childNode)))
			{
				edgeExists = true;

				break;
			}
		}

		return edgeExists;
	}

	public boolean createsCycle(Node childNode, Node parentNode)
	{
		boolean createsCycle = false;

		for (Node node : parentNode.getParents())
		{
			if (node.equals(childNode))
			{
				createsCycle = true;

				break;
			}
			else
			{
				createsCycle = createsCycle(childNode, node);
			}
		}

		return createsCycle;
	}

	public void verifyOperations(Node node)
	{
		if (node.getOperation().equals("and") || node.getOperation().equals("or"))
		{
			if (node.getChildren().size() - 1 < 2)
			{
				node.setOperation("null");
				beliefListener.getListPane().updateNodeInLists(node.getName(), node);
			}
		}
		else if (node.getOperation().equals("not"))
		{
			if (node.getChildren().size() - 1 < 1)
			{
				node.setOperation("null");
				beliefListener.getListPane().updateNodeInLists(node.getName(), node);
			}
		}
	}



	/*******************
	 ** Action Events **
	 *******************/

	public void actionPerformed(ActionEvent evt)
	{
		if (evt.getActionCommand().equals("Add Node"))
		{
			setCursor(createNodeCursor);
		}
		else if (evt.getActionCommand().equals("Edit Node"))
		{
			EditNodeDialog editNodeDialog = new EditNodeDialog(belief, this, nodes, clickedNode, beliefListener.getCurrentView());
		}
		else if (evt.getActionCommand().equals("Delete Node"))
		{
			deleteNode(clickedNode);
		}
		else if (evt.getActionCommand().equals("Add Edge"))
		{
			setCursor(dragEdgeCursor);
		}
		else if (evt.getActionCommand().equals("Reverse Edge"))
		{
			reverseEdge(clickedEdge);

			if (createsCycle(clickedEdge.getChildNode(), clickedEdge.getParentNode()))
			{
				reverseEdge(clickedEdge);

				NetworkDialog networkDialog = new NetworkDialog("Cycle Error", null, new JOptionPane("Reversing that edge would create a cycle in the graph. Please try elsewhere.", JOptionPane.ERROR_MESSAGE));
			}
		}
		else if (evt.getActionCommand().equals("Delete Edge"))
		{
			deleteEdge(clickedEdge);
		}
		else if (evt.getActionCommand().equals("View Distribution"))
		{
			BarGraphDialog barGraphDialog = new BarGraphDialog(belief, clickedNode);
		}
	}



	/******************
	 ** Mouse Events **
	 ******************/

	public void mouseClicked(MouseEvent evt)
	{
		int x = evt.getX();
		int y = evt.getY();

		if (evt.isMetaDown())
		{
			currentGroup = false;
			repaint();
		}

		if (networkRan)
		{
			if (isNodeHit(x, y))
			{
				if (evt.isMetaDown())
				{
					distPopup.show(this, x - 10, y - 2);
				}
			}
		}
		else
		{
			if (getCursor().equals(createNodeCursor))
			{
				if (!evt.isMetaDown())
				{
					setCursor(normalCursor);
					AddNodeDialog addNodeDialog = new AddNodeDialog(x, y);
				}
			}
			else
			{
				if (evt.isMetaDown())
				{
					if (isNodeHit(x, y))
					{
						nodePopup.show(this, x - 10, y - 2);
					}
					else if (isEdgeHit(x, y))
					{
						edgePopup.show(this, x - 10, y - 2);
					}
					else
					{
						panePopup.show(this, x - 10, y - 2);
					}
				}
			}
		}
	}

	public void mousePressed(MouseEvent evt)
	{
		int x = evt.getX();
		int y = evt.getY();

		if (!networkRan)
		{
			if (isNodeHit(x, y))
			{
				if (getCursor().equals(dragEdgeCursor))
				{
					newEdge = true;
					edgeChildNode = clickedNode;
					edgeCurrentEnd = clickedNode.center();
				}
				else if (getCursor().equals(normalCursor))
				{
					if (!dragGroup.isMember(clickedNode))
					{
						dragGroup = new DragGroup();
					}

					xGrab = x - clickedNode.getXPos();
					yGrab = y - clickedNode.getYPos();
					prevDragX = x;
					prevDragY = y;
					dragging = true;
					setCursor(dragNodeCursor);
				}
			}
			else
			{
				if (!getCursor().equals(createNodeCursor))
				{
					currentGroup = false;
					xGrab = x;
					yGrab = y;
					dragGroup = new DragGroup(new Point(xGrab, yGrab), new Point(x, y));
					newGroup = true;

					repaint();
				}
			}
		}
	}

	public void mouseDragged(MouseEvent evt)
	{
		int x = evt.getX();
		int y = evt.getY();

		if (newEdge)
		{
			edgeCurrentEnd = new Point(x, y);
		}
		else if (dragging)
		{
			if (dragGroup.isMember(clickedNode))
			{
				scrollRectToVisible(dragGroup.getGroupArea());

				if (x - (xGrab + (clickedNode.getXPos() - dragGroup.getGroupArea().getX())) < 0 ||
				    x + (50 - xGrab) + (dragGroup.getGroupArea().getMaxX() - (clickedNode.getXPos() + 49)) > 10000 ||
				    y - (yGrab + (clickedNode.getYPos() - dragGroup.getGroupArea().getY())) < 0 ||
				    y + (50 - yGrab) + (dragGroup.getGroupArea().getMaxY() - (clickedNode.getYPos() + 49)) > 10000)
				{
					Point nodeLocation = clickedNode.getLocationOnScreen();
					robot.mouseMove((int)nodeLocation.getX() + clickedNode.getXPos() + xGrab, (int)nodeLocation.getY() + clickedNode.getYPos() + yGrab);
				}
				else
				{
					for (Node node : dragGroup.getGroupMembers())
					{
						node.moveBy(x - prevDragX, y - prevDragY, getWidth(), getHeight());
					}

					dragGroup.moveBy(x - prevDragX, y - prevDragY, getWidth(), getHeight());

					prevDragX = x;
					prevDragY = y;
				}
			}
			else
			{
				currentGroup = false;

				scrollRectToVisible(new Rectangle(x, y, 25 - xGrab, 25 - yGrab));

				if (x - xGrab < 0 || x + (50 - xGrab) > 10000 || y - yGrab < 0 || y + (50 - yGrab) > 10000)
				{
					Point nodeLocation = clickedNode.getLocationOnScreen();
					robot.mouseMove((int)nodeLocation.getX() + clickedNode.getXPos() + xGrab, (int)nodeLocation.getY() + clickedNode.getYPos() + yGrab);
				}
				else
				{
					clickedNode.moveBy(x - prevDragX, y - prevDragY, getWidth(), getHeight());
					prevDragX = x;
					prevDragY = y;
				}
			}
		}
		else if (newGroup)
		{
			dragGroup = new DragGroup(new Point(xGrab, yGrab), new Point(x, y));
		}

		repaint();
	}

	public void mouseReleased(MouseEvent evt)
	{
		int x = evt.getX();
		int y = evt.getY();

		if (newEdge)
		{
			if (isNodeHit(x, y))
			{
				edgeParentNode = clickedNode;
				edgeCurrentEnd = new Point(edgeParentNode.center());

				if (!edgeChildNode.equals(edgeParentNode) && !edgeExists(edgeChildNode, edgeParentNode))
				{
					if (!createsCycle(edgeChildNode, edgeParentNode))
					{
						Edge newEdge = new Edge(edgeChildNode, edgeParentNode);

						addEdge(newEdge);

						mouseReleasedReset();
					}
					else
					{
						NetworkDialog networkDialog = new NetworkDialog("Cycle Error", "cycle", new JOptionPane("An edge here in that direction would create a cycle in the graph. Please try elsewhere.", JOptionPane.ERROR_MESSAGE));
					}
				}
				else
				{
					mouseReleasedReset();
				}
			}
		}
		else if (dragging)
		{
			dragging = false;
			setCursor(normalCursor);
		}
		else if (newGroup)
		{
			dragGroup.assembleGroup(nodes);
			newGroup = false;

			if (dragGroup.getGroupMembers().size() > 0)
			{
				currentGroup = true;
			}

			repaint();
		}
	}



	/**********************
	 ** Instance Methods **
	 **********************/

	private void mouseReleasedReset()
	{
		edgeParentNode = null;
		edgeCurrentEnd = null;
		newEdge = false;
		setCursor(normalCursor);

		repaint();
	}



	/****************************************
	 ** Currently Unused Interface Methods **
	 ****************************************/

	/* Carry out actions that happen due to the cursor entering an object */
	public void mouseEntered(MouseEvent evt) { }

	/* Carry out actions that happen due to the cursor leaving an object */
	public void mouseExited(MouseEvent evt) { }

	/* Carry out actions that happen due to the cursor moving */
	public void mouseMoved(MouseEvent evt) { }



	/**********************
 	 ** Internal Classes **
	 **********************/

	private class AddNodeDialog extends JInternalFrame implements ActionListener
	{
		private int xPos, yPos;
		private JTextField newNodeTextField;
		private JButton okayButton;
		private JButton cancelButton;

		public AddNodeDialog(int xPos, int yPos)
		{
			super("Add New Node", false, false, false, false);

			this.xPos = xPos;
			this.yPos = yPos;

			JPanel dialogPanel = new JPanel(new GridBagLayout());
			GridBagConstraints c = new GridBagConstraints();

			JLabel nameLabel = new JLabel("Name:");
			nameLabel.setPreferredSize(new Dimension(242, 19));
			nameLabel.setMinimumSize(new Dimension(242, 19));
			c.gridx = 0;
			c.gridy = 0;
			c.gridwidth = 2;
			c.gridheight = 1;
			c.weightx = 1.0;
			c.weighty = 0.5;
			c.insets = new Insets(9, 10, 1, 10);
			c.fill = GridBagConstraints.BOTH;
			dialogPanel.add(nameLabel, c);

			newNodeTextField = new JTextField();
			newNodeTextField.setPreferredSize(new Dimension(242, 20));
			newNodeTextField.setMinimumSize(new Dimension(242, 20));
			c.gridy = 1;
			c.insets = new Insets(0, 10, 4, 10);
			dialogPanel.add(newNodeTextField, c);

			okayButton = new JButton("OK");
			okayButton.setPreferredSize(new Dimension(61, 26));
			okayButton.setMinimumSize(new Dimension(61, 26));
			okayButton.addActionListener(this);
			c.gridy = 2;
			c.gridwidth = 1;
			c.insets = new Insets(5, 0, 12, 3);
			c.fill = GridBagConstraints.NONE;
			c.anchor = GridBagConstraints.EAST;
			dialogPanel.add(okayButton, c);

			cancelButton = new JButton("Cancel");
			cancelButton.setPreferredSize(new Dimension(61, 26));
			cancelButton.setMinimumSize(new Dimension(61, 26));
			cancelButton.setMargin(new Insets(0, 0, 0, 0));
			cancelButton.addActionListener(this);
			c.gridx = 1;
			c.insets = new Insets(5, 3, 12, 0);
			c.anchor = GridBagConstraints.WEST;
			dialogPanel.add(cancelButton, c);

			setContentPane(dialogPanel);
			getRootPane().setDefaultButton(okayButton);

			putClientProperty("JInternalFrame.frameType", "optionDialog");

			Dimension size = getPreferredSize();
			Dimension rootSize = belief.getRootPane().getSize();

			setBounds((rootSize.width - size.width) / 2, (rootSize.height - size.height) / 2, size.width, size.height);

			pack();
			belief.getFirstEventPane().setVisible(true);
			setVisible(true);
			belief.getLayeredPane().add(this, new Integer(1));

			try
			{
				setSelected(true);
			}
			catch (PropertyVetoException ignored) { }

			focusNewNodeTextField();
		}

		public void actionPerformed(ActionEvent evt)
		{
			if (evt.getSource().equals(okayButton))
			{
				addNewNode();
			}
			else if (evt.getSource().equals(cancelButton))
			{
				cleanUpAndDispose();
			}			
		}

		private void focusNewNodeTextField()
		{
			SwingUtilities.invokeLater(new Runnable()
			{
				public void run()
				{
					newNodeTextField.requestFocus();
					newNodeTextField.selectAll();
				}
			});		
		}

		private void addNewNode()
		{
			String newNodeName = newNodeTextField.getText();

			if (newNodeName != null)
			{
				if (newNodeName.length() > 0)
				{
					boolean nameExists = false;

					for (Node node : nodes)
					{
						if (newNodeName.compareToIgnoreCase(node.getName()) == 0)
						{
							nameExists = true;

							break;
						}
					}

					if (!nameExists)
					{
						createNewNode(xPos, yPos, newNodeName);
						cleanUpAndDispose();
					}
					else
					{
						AddNodeErrorDialog addNodeErrorDialog = new AddNodeErrorDialog("Name Exists Error", new JOptionPane("A node with that name already exists. Please try again.", JOptionPane.ERROR_MESSAGE));
					}
				}
				else
				{
					AddNodeErrorDialog addNodeErrorDialog = new AddNodeErrorDialog("Blank Name Error", new JOptionPane("Node name cannot be blank.", JOptionPane.ERROR_MESSAGE));
				}
			}
		}

		private void cleanUpAndDispose()
		{
			dispose();
			belief.getFirstEventPane().setVisible(false);
		}

		private class AddNodeErrorDialog extends InternalModalDialog
		{
			public AddNodeErrorDialog(String title, JOptionPane pane)
			{
				super(title, belief, pane);
			}

			public void cleanUpAfterClosing()
			{
				focusNewNodeTextField();
			}
		}
	}

	private class DragGroup
	{
		private Rectangle groupArea;
		private ArrayList<Node> groupMembers;

		public DragGroup()
		{
			groupArea = new Rectangle();
			groupMembers = new ArrayList<Node>();
		}

		public DragGroup(Point cornerOne, Point cornerTwo)
		{
			groupArea = new Rectangle(Math.min(cornerOne.x, cornerTwo.x), Math.min(cornerOne.y, cornerTwo.y), Math.abs(cornerOne.x - cornerTwo.x), Math.abs(cornerOne.y - cornerTwo.y));
			groupMembers = new ArrayList<Node>();
		}

		public Rectangle getGroupArea()
		{
			return this.groupArea;
		}

		public ArrayList<Node> getGroupMembers()
		{
			return this.groupMembers;
		}

		public void assembleGroup(ArrayList<Node> nodes)
		{
			for (Node node : nodes)
			{
				if (node.getXPos() > groupArea.getX() &&
				    node.getYPos() > groupArea.getY() &&
				    node.getXPos() + 50 < groupArea.getMaxX() &&
				    node.getYPos() + 50 < groupArea.getMaxY())
				{
					groupMembers.add(node);
				}
			}
		}

		public boolean isMember(Node node)
		{
			boolean isMember = false;

			for (Node member : groupMembers)
			{
				if (member.equals(node))
				{
					isMember = true;

					break;
				}
			}

			return isMember;
		}

		public void paintComponent(Graphics g)
		{
			Graphics2D g2 = (Graphics2D)g;
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

			g2.setPaint(Color.BLACK);
			g2.draw(groupArea);
		}

		public void moveBy(int x, int y, int width, int height)
		{
			int xPos = (int)groupArea.getX(), yPos = (int)groupArea.getY();

			xPos = Math.max((xPos += x), 0);
			xPos = Math.min(xPos, (width - 50));
			yPos = Math.max((yPos += y), 0);
			yPos = Math.min(yPos, (height - 50));

			groupArea.setLocation(xPos, yPos);
		}
	}

	private class NetworkDialog extends InternalModalDialog
	{
		private String type;

		public NetworkDialog(String title, String type, JOptionPane pane)
		{
			super(title, belief, pane);

			this.type = type;
		}

		public void cleanUpAfterClosing()
		{
			if (type.equals("cycle"))
			{
				mouseReleasedReset();
			}
		}
	}
}