/*
 * @(#)TreeJDesktop.java
 *
 * Last Modified: 9/15/01
 */

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import java.beans.*;
import java.net.*;

/**
 * The JDesktop class intended for a tree structure. Within this class, the panels for the trees
 * are constructed as are the JPanel panes to hold them and the internal frames and tabbed pane.
 *
 * <p> The desktop listens for OptionEvents specific to a chosen tree, and calls that required command
 * for the selected panel (whether it is tabbed pane or a selected window). Also, the desktop listens
 * for events affecting all trees. The only events that bypass the desktop are those based upon clicking
 * upon the tree.
 *
 *
 * @author Corey Sanders
 * @version 1.3 9/01/02
 */
public class TreeJDesktop extends JDesktopPane implements OptionListener, TreeMessageListener, ChangeListener {

	/**
	 * id for the Desktop Panel.
	 */
	private static int id = OptionEvent.DESKTOP_PANEL;

	/**
	 * Listeners for the tree messages passed.
	 */
	 private LinkedList optionListeners = new LinkedList();
	/**
	 * Listeners for the tree messages passed.
	 */
	 private LinkedList treeListeners = new LinkedList();


	/***************************/
	/* Trees panels controlled */
	/***************************/

	/**
	 * BSTTree Panel.
	 */
	private BSTTreeJPanel bstTreeJPanel;
	/**
	 * Splay Panel.
	 */
	private SplayTreeJPanel splayTreeJPanel;
	/**
	 * Balanced Panel.
	 */
	private BalancedBSTTreeJPanel balancedBSTTreeJPanel;
	/**
	 * RootInsertion Panel.
	 */
	private RootInsertionBSTTreeJPanel rootInsertionBSTTreeJPanel;
	/**
	 * Red-Black Panel.
	 */
	private RedBlackTreeJPanel redBlackTreeJPanel;

	/**
	 * Randomized BST Pannel.
	 */
	//private RandomizedBSTTreeJPanel randomizedBSTTreeJPanel;


	/**
	 * Flag as to weather the desktop is in windows or tabbed panes.
	 */
	boolean windows = true;


	/**
	 * Tabbed pane for the desktop, if windows are not used.
	 */
	JTabbedPane tabbedPane;

	/**
	 * BSTTree JInternalFrame (only needed if windows).
	 */
	JInternalFrame bstInternalFrame;
	/**
	 * Splay JInternalFrame (only needed if windows).
	 */
	JInternalFrame splayInternalFrame;
	/**
	 * Balanced JInternalFrame (only needed if windows).
	 */
	JInternalFrame balancedInternalFrame;
	/**
	 * RootInsertion JInternalFrame (only needed if windows).
	 */
	JInternalFrame rootInsertionInternalFrame;
	/**
	 * Red-Black JInternalFrame (only needed if windows).
	 */
	JInternalFrame redBlackInternalFrame;
	/**
	 * Randomized JInternalFrame (only needed if windows).
	 */
	//JInternalFrame randomizedInternalFrame;


	/**
	 * The message for the dialog box. Starts off null, and must be created.
	 */
	Object[] message = null;

	/**
	 * The radio button for duplication in the make random tree dialog box.
	 */
	JRadioButton duplicateButton;

	ColorPreferencesDialog dialog;


	/**
	 * True when the message for the dialog box to create a tree has been made.
	 */
	private boolean constructedMessage = false;


	/**
	 * Sole Constructor, which constructs all of the tree panels and sets up all of the listening.
	 */
    public TreeJDesktop() {
		// Super call.
		super();

		// Construct the color options Panel.
		constructColorOptionsJPanel();

		// Set Color.
		setBackground(Color.white);

		setBorder(BorderFactory.createEmptyBorder());

		// Construct all tree panels.
		bstTreeJPanel = new BSTTreeJPanel();
		splayTreeJPanel = new SplayTreeJPanel();
		balancedBSTTreeJPanel = new BalancedBSTTreeJPanel();
		rootInsertionBSTTreeJPanel = new RootInsertionBSTTreeJPanel();
		redBlackTreeJPanel = new RedBlackTreeJPanel();
		//randomizedBSTTreeJPanel = new RandomizedBSTTreeJPanel();

		// Set all intial Background Colors.
		bstTreeJPanel.setBackground(Color.white);
		splayTreeJPanel.setBackground(Color.white);
		balancedBSTTreeJPanel.setBackground(Color.white);
		rootInsertionBSTTreeJPanel.setBackground(Color.white);
		redBlackTreeJPanel.setBackground(Color.white);
		//randomizedBSTTreeJPanel.setBackground(Color.white);



		// Add all trees as option listeners to this desktop.
		addOptionListener(bstTreeJPanel);
		addOptionListener(splayTreeJPanel);
		addOptionListener(balancedBSTTreeJPanel);
		addOptionListener(rootInsertionBSTTreeJPanel);
		addOptionListener(redBlackTreeJPanel);
		//addOptionListener(randomizedBSTTreeJPanel);

		// Adds this as a tree message listener to all panels.
		bstTreeJPanel.addTreeMessageListener(this);
		splayTreeJPanel.addTreeMessageListener(this);
		balancedBSTTreeJPanel.addTreeMessageListener(this);
		rootInsertionBSTTreeJPanel.addTreeMessageListener(this);
		redBlackTreeJPanel.addTreeMessageListener(this);
		//randomizedBSTTreeJPanel.addTreeMessageListener(this);

		// Default setup.
		makeTabbedPane();


    }

	/**
	 * Original intialization. This calls methods that are called everytime a selected
	 * panel is changed, and obviously needs to be called initially.
	 */
	public void initialize() {
		((TreeJPanel)getSelectedPanel()).getInputOptions();
		((TreeJPanel)getSelectedPanel()).makeColorSchemeOptions();
		((TreeJPanel)getSelectedPanel()).makeColorSettings();
	}

	/**
	 * Makes the windows for the Desktop. The method also removes the JInternalFrames to make
	 * new frames or removes the tabbedPane (whichever is currently active).
	 */
	public void makeWindows() {
		if (isWindows()) {
			remove(bstInternalFrame);
			remove(splayInternalFrame);
			remove(balancedInternalFrame);
	        remove(rootInsertionInternalFrame);
	        remove(redBlackInternalFrame);
			// remove(randomizedInternalFrame);
		}
		else {
			remove(tabbedPane);
		}

		setWindows(true);

		bstTreeJPanel.setComponentShown(true);
		splayTreeJPanel.setComponentShown(true);
		balancedBSTTreeJPanel.setComponentShown(true);
		rootInsertionBSTTreeJPanel.setComponentShown(true);
		redBlackTreeJPanel.setComponentShown(true);


		bstInternalFrame = new JInternalFrame("BST",
		                    true, //resizable
		                    true, //closable
		                    true, //maximizable
                    		true);//iconifiable
        (bstInternalFrame.getContentPane()).add(bstTreeJPanel);

 		splayInternalFrame = new JInternalFrame("Splay Tree",
 		                    true, //resizable
 		                    true, //closable
 		                    true, //maximizable
                     		true);//iconifiable
        (splayInternalFrame.getContentPane()).add(splayTreeJPanel);

 		balancedInternalFrame = new JInternalFrame("Balanced Tree",
 		                    true, //resizable
 		                    true, //closable
 		                    true, //maximizable
                     		true);//iconifiable
        (balancedInternalFrame.getContentPane()).add(balancedBSTTreeJPanel);

 		rootInsertionInternalFrame = new JInternalFrame("Root Insertion",
 		                    true, //resizable
 		                    true, //closable
 		                    true, //maximizable
                     		true);//iconifiable
        (rootInsertionInternalFrame.getContentPane()).add(rootInsertionBSTTreeJPanel);


 		redBlackInternalFrame = new JInternalFrame("Red-Black",
 		                    true, //resizable
 		                    true, //closable
 		                    true, //maximizable
                     		true);//iconifiable
        (redBlackInternalFrame.getContentPane()).add(redBlackTreeJPanel);

         		/*randomizedInternalFrame = new JInternalFrame("Randomized",
		 		                    true, //resizable
		 		                    true, //closable
		 		                    true, //maximizable
		                     		true);//iconifiable
		        (randomizedInternalFrame.getContentPane()).add(randomizedBSTTreeJPanel); */




		setLayout(null);
		setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);

		Dimension dim = getSize();
		int w = dim.width;
		int h = dim.height;


        add(bstInternalFrame);
		bstInternalFrame.setVisible(true);
        bstInternalFrame.setSize(w/3, h/2);
        bstInternalFrame.setLocation(0,0);

        add(splayInternalFrame);
        splayInternalFrame.setVisible(true);
        splayInternalFrame.setSize(w/3, h/2);
		splayInternalFrame.setLocation(w/3, 0);

        add(balancedInternalFrame);
        balancedInternalFrame.setVisible(true);
        balancedInternalFrame.setSize(w/3, h/2);
        balancedInternalFrame.setLocation(2*w/3, 0);

        add(rootInsertionInternalFrame);
        rootInsertionInternalFrame.setVisible(true);
        rootInsertionInternalFrame.setSize(w/3, h/2);
        rootInsertionInternalFrame.setLocation(0, h/2);

        add(redBlackInternalFrame);
        redBlackInternalFrame.setVisible(true);
        redBlackInternalFrame.setSize(w/3, h/2);
        redBlackInternalFrame.setLocation(w/3, h/2);

       // add(randomizedInternalFrame);
       // randomizedInternalFrame.setVisible(true);
       // randomizedInternalFrame.setSize(w/3, h/2);
       // randomizedInternalFrame.setLocation(w/3, h/2);

        revalidate();

	}

	/**
	 * Makes a tabbedPane. It removes the Internal frames first and then constructs the
	 * internal frames.
	 */
    public void makeTabbedPane() {
		if (!isWindows()) {
			return;
		}

		setWindows(false);

		// Original instantiation
		if (bstInternalFrame != null) {
			remove(bstInternalFrame);
			remove(splayInternalFrame);
			remove(balancedInternalFrame);
	        remove(rootInsertionInternalFrame);
	        remove(redBlackInternalFrame);
	       // remove(randomizedInternalFrame);

		}

		bstTreeJPanel.setComponentShown(true);
		splayTreeJPanel.setComponentShown(false);
		balancedBSTTreeJPanel.setComponentShown(false);
		rootInsertionBSTTreeJPanel.setComponentShown(false);
		redBlackTreeJPanel.setComponentShown(false);


		tabbedPane = new JTabbedPane();
		tabbedPane.setBorder(BorderFactory.createEmptyBorder());

		// Add change listener
		tabbedPane.addChangeListener(this);

		tabbedPane.addTab("BST",bstTreeJPanel);
        tabbedPane.setSelectedIndex(0);

        tabbedPane.addTab("Splay Tree", splayTreeJPanel);

        tabbedPane.addTab("Balanced BST", balancedBSTTreeJPanel);

		tabbedPane.addTab("Root Inserted BST", rootInsertionBSTTreeJPanel);

        tabbedPane.addTab("Red-Black", redBlackTreeJPanel);

      	//tabbedPane.addTab("Randomized BST", randomizedBSTTreeJPanel);

		setLayout(new BorderLayout());

		add(tabbedPane, BorderLayout.CENTER);

		revalidate();


	}


	/**
 	 ********************
	 * Accesssor methods*
	 ********************
	 */


	/**
	 * Gets if the desktop is set up in windows format or tabbed pabe format. If the desktop
	 * is as windows, true is returned.
	 *
	 * @return true if the desktop is set as windows.
	 */
	public boolean isWindows() {
		return windows;
	}



 	 /**
	  * Gets the currently selected panel, whether it is the panel selected of the windows
	  * or whether it is the selected tabbed pane.
	  *
	  * @return JPanel currently selected whether in the window or Tabbed Pane.
	  */
	  public JPanel getSelectedPanel() {
		  // Windows
		  if (isWindows()) {
			  JInternalFrame selectedFrame = getSelectedFrame();
			  if (selectedFrame == bstInternalFrame) {
				  return bstTreeJPanel;
			  }
			  if (selectedFrame == splayInternalFrame) {
				  return splayTreeJPanel;
			  }
			  if (selectedFrame == balancedInternalFrame) {
				  return balancedBSTTreeJPanel;
			  }
			  if (selectedFrame == rootInsertionInternalFrame) {
				  return rootInsertionBSTTreeJPanel;
			  }
			  if (selectedFrame == redBlackInternalFrame) {
				  return redBlackTreeJPanel;
			  }
			  //if (selectedFrame == randomizedInternalFrame) {
				 // return randomizedBSTTreeJPanel;
			  //}

		  }
		  // Tabs
		  else {
			  int selectedIndex = tabbedPane.getSelectedIndex();
 			  if (selectedIndex == 0) {
				  return bstTreeJPanel;
			  }
			  if (selectedIndex == 1) {
				  return splayTreeJPanel;
			  }
			  if (selectedIndex == 2) {
				  return balancedBSTTreeJPanel;
			  }
			  if (selectedIndex == 3) {
				  return rootInsertionBSTTreeJPanel;
			  }
			  if (selectedIndex == 4) {
				  return redBlackTreeJPanel;
			  }
			  if (selectedIndex == 5) {
				 // return randomizedBSTTreeJPanel;
			  }

		  }

		  return bstTreeJPanel;
	  }



	/**
 	 ******************
	 * Mutator methods*
	 ******************
	 */


	/**
	 * Sets whether the desktop is set as windows or tabbed pane. This should only be change
	 * along with actually changing the format or problems will insue.
	 *
	 * @param windows boolean true if the format is as windows.
	 */
	protected void setWindows(boolean windows) {
		this.windows = windows;
	}

	/**
 	 ********************
	 * Listening methods*
	 ********************
	 */


	/**
	 * Adds an OptionEventListener according to
	 * the OptionEventListener interface and the <code>OptionEvent</code>.
	 *
	 * @param l the listener added recieves the OptionEvents occuring.
	 */
 	public void addOptionListener(OptionListener l) {
		optionListeners.add(l);
	}

	/**
	 * Removes an OptionEventListener according to
	 * the OptionEventListener interface and the <code>OptionEvent</code>.
	 *
	 * @param l the listener removed from recieving the OptionEvents occuring.
	 */
	public void removeOptionListener(TreeMessageListener l) {
		optionListeners.remove(l);
	}

	/**
	 * Sends an optionEvent to all listeners of the current OptionEvents. Simply
	 * passes the information on.
	 *
	 * @param e the option event to be passed on.
	 */
	protected void optionEvent(OptionEvent e) {
		ListIterator list = optionListeners.listIterator(0);
		while (list.hasNext()) {
			optionEvent(e, (OptionListener)list.next());
		}
	}

	/**
	 * Sends an optionEvent to all listeners of the current OptionEvents. Simply
	 * passes the information on.
	 *
	 * @param e the option event to be passed on.
	 * @param l the specific optionListener to recieve the event.
	 */
	protected void optionEvent(OptionEvent e, OptionListener l) {
		l.optionEventPerformed(e);
	}


	/**
	 * Adds an TreeMessageListener from the TREE, according to
	 * the TreeMessageListener interface and the <code>TreeMessageEvent</code>.
	 *
	 * @param l the listener added recieves the TreeMessageEvents occuring.
	 */
	public void addTreeMessageListener(TreeMessageListener l) {
		treeListeners.add(l);
	}

	/**
	 * Removes an TreeMessageListener from the TREE, according to
	 * the TreeMessageListener interface and the <code>TreeMessageEvent</code>.
	 *
	 * @param l the listener removed from recieving the TreeMessageEvents occuring.
	 */
	public void removeTreeMessageListener(TreeMessageListener l) {
		treeListeners.remove(l);
	}

	/**
	 * Calls all of the treeListeners of the Tree and passes the tree message information information regarding the
	 * status of the Tree.
	 */
	protected void messageAction(String msg, Object msgObj) {
		TreeMessageEvent messageEvent = new TreeMessageEvent(this, TreeMessageEvent.PANEL, msg, msgObj);
		ListIterator list = treeListeners.listIterator(0);
		while (list.hasNext()) {
			((TreeMessageListener)list.next()).treeMessageEventPerformed(messageEvent);
		}
	}


	/************************************************/
	/* Overiding Classes							*/
	/************************************************/

	/**
	 * Adds the specified mouse listener to receive mouse events from this component. If l is null, no exception is thrown and no
     * action is performed.
     *
     * @param l  the mouse listener.
     */
	 public void addMouseListener(MouseListener l) {
	 	bstTreeJPanel.addMouseListener(l);
		splayTreeJPanel.addMouseListener(l);
		balancedBSTTreeJPanel.addMouseListener(l);
		rootInsertionBSTTreeJPanel.addMouseListener(l);
		redBlackTreeJPanel.addMouseListener(l);
		//randomizedBSTTreeJPanel.addMouseListener(l);

	}


	/**
	 * Removes the specified mouse listener so that it no longer receives mouse events from this component. This method
     * performs no function, nor does it throw an exception, if the listener specified by the argument was not previously added to
     * this component. If l is null, no exception is thrown and no action is performed.
     *
     * @param l the mouse listener.
     */
	 public void removeMouseListener(MouseListener l) {
	 	bstTreeJPanel.removeMouseListener(l);
		splayTreeJPanel.removeMouseListener(l);
		balancedBSTTreeJPanel.removeMouseListener(l);
		rootInsertionBSTTreeJPanel.removeMouseListener(l);
		redBlackTreeJPanel.removeMouseListener(l);
		//randomizedBSTTreeJPanel.removeMouseListener(l);

	}

	/**
	 * Adds the specified mouse motion listener to receive mouse motion events from this component. If l is null, no exception is
     * thrown and no action is performed.
     *
     * @param l the mouse motion listener.
     */
	 public void addMouseMotionListener(MouseMotionListener l) {
	 	bstTreeJPanel.addMouseMotionListener(l);
		splayTreeJPanel.addMouseMotionListener(l);
		balancedBSTTreeJPanel.addMouseMotionListener(l);
		rootInsertionBSTTreeJPanel.addMouseMotionListener(l);
		redBlackTreeJPanel.addMouseMotionListener(l);
		//randomizedBSTTreeJPanel.addMouseMotionListener(l);

	}

	/**
	 * Removes the specified mouse motion listener so that it no longer receives mouse motion events from this component. This
     * method performs no function, nor does it throw an exception, if the listener specified by the argument was not previously
     * added to this component. If l is null, no exception is thrown and no action is performed.
	 *
	 * @param l the mouse motion listener.
	 */
	public void removeMouseMotionListener(MouseMotionListener l) {
	 	bstTreeJPanel.removeMouseMotionListener(l);
		splayTreeJPanel.removeMouseMotionListener(l);
		balancedBSTTreeJPanel.removeMouseMotionListener(l);
		rootInsertionBSTTreeJPanel.removeMouseMotionListener(l);
		redBlackTreeJPanel.removeMouseMotionListener(l);
		//randomizedBSTTreeJPanel.removeMouseMotionListener(l);

	}



	/************************************************/
	/* Implements TreeMessage Listener              */
	/************************************************/

	/**
	 * Listens to tree message events.
	 *
	 * @param e TreeMessageEvent that contains information about the tree.
	 */
	public void treeMessageEventPerformed(TreeMessageEvent e) {

		if (e.getMessage().equals(TreeMessageEvent.ERROR_MESSAGE)) {
			if (isWindows()) {

				JInternalFrame currentFrame = bstInternalFrame;

				if (e.getSource() == bstTreeJPanel) {
					currentFrame = bstInternalFrame;
			  	}
			  	if (e.getSource() == splayTreeJPanel) {
					currentFrame = splayInternalFrame;
			  	}
			  	if (e.getSource() == balancedBSTTreeJPanel) {
					currentFrame = balancedInternalFrame;
			  	}
			  	if (e.getSource() == rootInsertionBSTTreeJPanel) {
					currentFrame = rootInsertionInternalFrame;
			  	}
			  	if (e.getSource() == redBlackTreeJPanel) {
					currentFrame = redBlackInternalFrame;
			  	}
			  	//if (e.getSource() == randomizedBSTTreeJPanel) {
					//currentFrame = randomizedInternalFrame;
			  	//}

			  	if (!(currentFrame.isClosed() || currentFrame.isIcon())) {

					JOptionPane.showMessageDialog((JPanel)e.getSource(), (String)e.getMessageObject(), "Error", JOptionPane.ERROR_MESSAGE);
				}
			}
			else {
				/*int tempIndex = tabbedPane.getSelectedIndex();
 			  	if (e.getSource() == bstTreeJPanel) {
					tabbedPane.setSelectedIndex(0);
				}
			  	if (e.getSource() == splayTreeJPanel) {
					tabbedPane.setSelectedIndex(1);
			  	}
			  	if (e.getSource() == balancedBSTTreeJPanel) {
					tabbedPane.setSelectedIndex(2);
			  	}
			  	if (e.getSource() == rootInsertionBSTTreeJPanel) {
					tabbedPane.setSelectedIndex(3);
			  	}
			  	if (e.getSource() == redBlackTreeJPanel) {
					tabbedPane.setSelectedIndex(4);
			  	}
			  	//if (e.getSource() == randomizedBSTTreeJPanel) {
					//tabbedPane.setSelectedIndex(5);
			  	//}

				JOptionPane.showMessageDialog(this, (String)e.getMessageObject(), "Error", JOptionPane.ERROR_MESSAGE);

				tabbedPane.setSelectedIndex(tempIndex);
				*/

				int checkIndex=0;

				if (e.getSource() == bstTreeJPanel) {
					checkIndex = 0;
				}
			  	if (e.getSource() == splayTreeJPanel) {
					checkIndex = 1;
			  	}
			  	if (e.getSource() == balancedBSTTreeJPanel) {
					checkIndex = 2;
			  	}
			  	if (e.getSource() == rootInsertionBSTTreeJPanel) {
					checkIndex = 3;
			  	}
			  	if (e.getSource() == redBlackTreeJPanel) {
					checkIndex = 4;
			  	}

				if (tabbedPane.getSelectedIndex() == checkIndex) {
					JOptionPane.showMessageDialog(this, (String)e.getMessageObject(), "Error", JOptionPane.ERROR_MESSAGE);
				}
			}

			return;

		}
		else if (e.getMessage().equals(TreeMessageEvent.COLOR_CHANGE)) {
			dialog.setApplied(false);
			return;
		}

		messageAction(e.getMessage(), e.getMessageObject());
	}

	/**
	 * Constructs a random Tree for every tree currently in the desktop.
	 *
	 * @param startKey the starting key for the random tree.
	 * @param endKey the ending key for the random tree.
	 * @param size the size of the random tree created.
	 * @param duplicates true if duplicates are allowed in the list.
	 */
	public void makeRandomTree(String startKey, String endKey, int size, boolean duplicates) {
		boolean priorBSTAnimating = false;
		boolean priorSplayAnimating= false;
		boolean priorBalancedAnimating = false;
		boolean priorRootInsertionAnimating = false;
		boolean priorRedBlackAnimating = false;

		boolean priorSelected = false;

		LinkedList keyList = new LinkedList();

		if (isWindows()) {
			priorBSTAnimating = (bstTreeJPanel).isAnimating();
			priorSplayAnimating = (splayTreeJPanel).isAnimating();
			priorBalancedAnimating = (balancedBSTTreeJPanel).isAnimating();
			priorRootInsertionAnimating = (rootInsertionBSTTreeJPanel).isAnimating();
			priorRedBlackAnimating = (redBlackTreeJPanel).isAnimating();

			OptionEvent animationOffEvent = new OptionEvent(this, OptionEvent.DESKTOP_PANEL, OptionEvent.ANIMATION_OFF);

			optionEvent(animationOffEvent);
		}
		else {
			priorSelected = ((TreeJPanel)getSelectedPanel()).isAnimating();

			OptionEvent animationOffEvent = new OptionEvent(this, OptionEvent.DESKTOP_PANEL, OptionEvent.ANIMATION_OFF);

			optionEvent(animationOffEvent, ((TreeJPanel)getSelectedPanel()));
		}



		String keyType = ((TreeJPanel)getSelectedPanel()).getKeyType();

		if (keyType.equals(TreeJPanel.INTEGER)) {
			int start, end;
			try {
				start = (Integer.decode(startKey)).intValue();
				end = (Integer.decode(endKey)).intValue();
			}
			catch (NumberFormatException e) {
				JOptionPane.showMessageDialog(this, "You must enter key limits that\nare the same type as the trees : Integers!", "Type Error", JOptionPane.ERROR_MESSAGE);
				return;
			}

			for(int i = 0; i < size; i++) {

				int randomKey = (int)(Math.random() * (end-start)) + start;


				if (!duplicates) {

					// Impossible to insert more.
					if ( (keyList.size() == (end-start)) || (keyList.size() == (start-end)) ) {
						JOptionPane.showMessageDialog(this, "Impossible to enter another distinct key!", "Out of Bounds", JOptionPane.ERROR_MESSAGE);
						return;
					}

					boolean duplicateFound = false;

					for (int j = 0; j < keyList.size(); j++) {
						// Check if a duplicate exists
						if ( ((int)((Integer)keyList.get(j)).intValue()) == randomKey) {
							i--;
							duplicateFound = true;
							break;
						}
					}

					if (duplicateFound) {
						continue;
					}

					Integer randomInt = new Integer(randomKey);
					keyList.add(randomInt);
				}
				OptionEvent optionEvent = new OptionEvent(this, OptionEvent.DESKTOP_PANEL, OptionEvent.INSERT, (new Integer(randomKey)).toString());
				optionEvent(optionEvent);
			}
		}
		if (keyType.equals(TreeJPanel.CHARACTER)) {
			double start, end;
			try {
				start = (short)(new Character(startKey.charAt(0)).charValue());
				end = (short)(new Character(endKey.charAt(0)).charValue());
			}
			catch (NumberFormatException e) {
				JOptionPane.showMessageDialog(this, "You must enter key limits that\nare the same type as the trees : Characters!", "Type Error", JOptionPane.ERROR_MESSAGE);
				return;
			}


			for(int i = 0; i < size; i++) {
				short randomInt = (short)((Math.random() * (end-start)) + start);
				char randomKey = (char)randomInt;

				if (!duplicates) {

					// Impossible to insert more.
					if ( (keyList.size() == (end-start)) || (keyList.size() == (start-end)) ) {
						JOptionPane.showMessageDialog(this, "Impossible to enter another distinct key!", "Out of Bounds", JOptionPane.ERROR_MESSAGE);
						return;
					}

					boolean duplicateFound = false;

					for (int j = 0; j < keyList.size(); j++) {
						// Check if a duplicate exists
						if ( ((int)((Character)keyList.get(j)).charValue()) == randomKey) {
							i--;
							duplicateFound = true;
							break;
						}
					}

					if (duplicateFound) {
						continue;
					}

					Character randomChar = new Character(randomKey);
					keyList.add(randomChar);
				}


				OptionEvent optionEvent = new OptionEvent(this, OptionEvent.DESKTOP_PANEL, OptionEvent.INSERT, (new Character(randomKey)).toString());
				optionEvent(optionEvent);

			}
		}
		if (keyType.equals(TreeJPanel.DOUBLE)) {
			double start, end;
			try {
				start = (double)(new Double(startKey).doubleValue());
				end = (double)(new Double(endKey).doubleValue());
			}
			catch (NumberFormatException e) {
				JOptionPane.showMessageDialog(this, "You must enter key limits that\nare the same type as the trees : Doubles!", "Type Error", JOptionPane.ERROR_MESSAGE);
				return;
			}

			for(int i = 0; i < size; i++) {
				double randomKey = (double)((Math.random() * (end-start)) + start);


				// Impossible to insert more.
				/*if ( (keyList.size() == ((end-start))) || (keyList.size() == ((start-end)*100)) ) {
					JOptionPane.showMessageDialog(this, "Impossible to enter another distinct key!", "Out of Bounds", JOptionPane.ERROR_MESSAGE);
					return;
				}*/

				if (!duplicates) {

					boolean duplicateFound = false;

					for (int j = 0; j < keyList.size(); j++) {
						// Check if a duplicate exists
						if ( Math.abs( (((Double)keyList.get(j)).doubleValue()) - randomKey)  <=.000000001) {
							i--;
							duplicateFound = true;
							break;
						}
					}

					if (duplicateFound) {
						continue;
					}

					Double randomDouble = new Double(randomKey);
					keyList.add(randomDouble);
				}



				OptionEvent optionEvent = new OptionEvent(this, OptionEvent.DESKTOP_PANEL, OptionEvent.INSERT, ((new Double(randomKey)).toString()).substring(0, 5) );
				optionEvent(optionEvent);

			}
		}

		OptionEvent animationOnEvent = new OptionEvent(this, OptionEvent.DESKTOP_PANEL, OptionEvent.ANIMATION_ON);


		if (isWindows()) {
			if (priorBSTAnimating) {
				optionEvent(animationOnEvent, bstTreeJPanel);
			}
			if (priorSplayAnimating) {
				optionEvent(animationOnEvent, splayTreeJPanel);
			}
			if (priorBalancedAnimating) {
				optionEvent(animationOnEvent, balancedBSTTreeJPanel);
			}
			if (priorRootInsertionAnimating) {
				optionEvent(animationOnEvent, rootInsertionBSTTreeJPanel);
			}
			if (priorRedBlackAnimating) {
				optionEvent(animationOnEvent, redBlackTreeJPanel);
			}
		}
		else {
			if(priorSelected) {

				optionEvent(animationOnEvent, ((TreeJPanel)getSelectedPanel()));
			}
		}


	}



	/**
	 * Constructs the message for the creating of a tree with random elements. This is used
	 * so the last number entered appears in the fields unless the input type changes.
	 */
	protected void constructMessage() {

		JTextField start = new JTextField("0");
		JTextField end = new JTextField("100");
		JTextField count = new JTextField("50");

		String keyType = ((TreeJPanel)getSelectedPanel()).getKeyType();

		if (keyType.equals(TreeJPanel.INTEGER)) {
			start = new JTextField("0");
			end = new JTextField("100");
			count = new JTextField("50");
		}
		if (keyType.equals(TreeJPanel.CHARACTER)) {
			start = new JTextField("a");
			end = new JTextField("z");
			count = new JTextField("15");

		}
		if (keyType.equals(TreeJPanel.DOUBLE)) {
			start = new JTextField("0");
			end = new JTextField("1");
			count = new JTextField("25");

		}



		message = new Object[8];

		message[0] = "Please enter the values for the random tree construction:";

		message[1] = "starting element:";

		message[2] = start;

		message[3] = "ending element:";

		message[4] = end;

		message[5] = "amount of elements:";

		message[6] = count;


		duplicateButton = new JRadioButton("Duplicates");
		JRadioButton noDuplicateButton = new JRadioButton("No Duplicates");
		noDuplicateButton.setSelected(true);

		ButtonGroup group = new ButtonGroup();
		group.add(duplicateButton);
		group.add(noDuplicateButton);

		JPanel buttonPanel = new JPanel();

        buttonPanel.setLayout(new GridLayout(1, 0));
        buttonPanel.add(noDuplicateButton);
        buttonPanel.add(duplicateButton);

		message[7] = buttonPanel;


		constructedMessage = true;
	}

	/**
	 * Gets the icon from the resource (JAR file).
	 */
	public Icon getIcon(String imageName) {

   		// Get current classloader
   		URL url = this.getClass().getResource(imageName);


   		Image img=Toolkit.getDefaultToolkit().getImage(url);

		return new ImageIcon(img);

	}

	/**
	 * Option event listener. The result of an optionEvent being performed depends upon the event.
	 * Within the desktop, the events are used or passed on to the trees accordingly. Here the
	 * separating of the events takes place.
	 *
	 * @param e OptionEvent passed with information about the event.
	 */
	public void optionEventPerformed(OptionEvent e) {
		// Make into windows
		if (e.getActionCommand().equals(OptionEvent.WINDOWS)) {
			makeWindows();
		}
		// Make into tabbed panes
		else if (e.getActionCommand().equals(OptionEvent.TABBED_PANE)) {
			makeTabbedPane();
		}
		// Make a random tree
		else if (e.getActionCommand().equals(OptionEvent.RANDOM_TREE)) {

			int size;

			boolean duplicates;


			if (!constructedMessage) {
				constructMessage();
			}

			// Options
            String[] options = {"OK","Cancel"};

			int confirm = JOptionPane.showOptionDialog(
								this,
								message,
								"Random Tree Generator",
								JOptionPane.OK_CANCEL_OPTION,
								JOptionPane.QUESTION_MESSAGE,
								null,
								options,
								options[0]
								);

			if (confirm != 0) {
				return;
			}


			try {
				size = (Integer.decode(((JTextField)message[6]).getText())).intValue();
			}
			catch (NumberFormatException numberFormatException) {
				JOptionPane.showMessageDialog(this, "You must enter an integer count as the size!", "Type Error", JOptionPane.ERROR_MESSAGE);
				return;
			}

			duplicates = duplicateButton.isSelected();


			makeRandomTree(((JTextField)message[2]).getText(), ((JTextField)message[4]).getText(), size, duplicates);

		}
		else if (e.getActionCommand().equals(OptionEvent.TREE_INFORMATION)) {
 			  if (getSelectedPanel() == bstTreeJPanel) {
				 JOptionPane.showMessageDialog(this, BSTTreeHead.TREE_INFORMATION, "BST Information", JOptionPane.INFORMATION_MESSAGE );
			  }
			  if (getSelectedPanel() == splayTreeJPanel) {
				  JOptionPane.showMessageDialog(this, SplayTreeHead.TREE_INFORMATION, "Splay Information", JOptionPane.INFORMATION_MESSAGE );
			  }
			  if (getSelectedPanel() == balancedBSTTreeJPanel) {
				 JOptionPane.showMessageDialog(this, BalancedBSTTreeHead.TREE_INFORMATION, "Balanced BST Information", JOptionPane.INFORMATION_MESSAGE );
			  }
			  if (getSelectedPanel() == rootInsertionBSTTreeJPanel) {
				JOptionPane.showMessageDialog(this, RootInsertionBSTTreeHead.TREE_INFORMATION, "Root Insertion Information", JOptionPane.INFORMATION_MESSAGE );
			  }
			  if (getSelectedPanel() == redBlackTreeJPanel) {
				JOptionPane.showMessageDialog(this, RedBlackTreeHead.TREE_INFORMATION, "Red Black Information", JOptionPane.INFORMATION_MESSAGE );
			  }

		}
		else if (e.getActionCommand().equals(OptionEvent.PROGRAM_INFORMATION)) {
			JOptionPane.showMessageDialog(this, TreeApplication.PROGRAM_INFORMATION, "Program Information", JOptionPane.INFORMATION_MESSAGE, getIcon("GTIcon.jpg"));
		}
		else if (e.getActionCommand().equals(OptionEvent.ABOUT)) {

			Object[] aboutMessage = new Object[3];

			aboutMessage[0] = getIcon("growingTreeTitle.jpg");
			aboutMessage[1] = TreeApplication.LICENSE_INFORMATION;
			aboutMessage[2] = TreeApplication.CREATOR_INFORMATION;


			// Options
            String[] options = {"Close"};

			int confirm = JOptionPane.showOptionDialog(
								this,
								aboutMessage,
								"About Growing Tree...",
								JOptionPane.DEFAULT_OPTION,
								JOptionPane.PLAIN_MESSAGE,
								null,
								options,
								options[0]
								);

		}
		// Balance all
		else if (e.getActionCommand().equals(OptionEvent.BALANCE_ALL)) {

			OptionEvent optionEvent = new OptionEvent(this, id, OptionEvent.BALANCE);
			// PASS ON BALANCE_ALL
			optionEvent(optionEvent);

		}

		// Get status of all.
		else if (e.getActionCommand().equals(OptionEvent.GET_STATUS_ALL)) {

			String allStatusString = new String(
				bstTreeJPanel.getTreeStatusMessage()+"\n\n"+
				splayTreeJPanel.getTreeStatusMessage()+"\n\n"+
				balancedBSTTreeJPanel.getTreeStatusMessage()+"\n\n"+
				rootInsertionBSTTreeJPanel.getTreeStatusMessage()+"\n\n"+
				redBlackTreeJPanel.getTreeStatusMessage()+"\n\n");

				JOptionPane.showMessageDialog(this,allStatusString,"Status of all Trees", JOptionPane.INFORMATION_MESSAGE);

		}
		// Clear All
		else if (e.getActionCommand().equals(OptionEvent.CLEAR_ALL)) {

			// Confirm
			int response = JOptionPane.showConfirmDialog(this, "Are you sure you wish to clear all trees?", "Clear Tree", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);

			if (response == JOptionPane.YES_OPTION) {
				// Pass on CLEAR_ALL
				optionEvent(e);
			}

		}
		// Input Change Type
		else if (e.getActionCommand().equals(OptionEvent.INPUT_CHANGE_ALL)) {

			// Confirm
			int response = JOptionPane.showConfirmDialog(this, "Are you sure you wish to clear all trees?", "Clear Tree", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);

			if (response == JOptionPane.YES_OPTION) {

				// Pass on INPUT_CHANGE
				optionEvent(e);

				constructedMessage = false;
			}
			else {
				// Reset the key type.
				messageAction(TreeMessageEvent.KEY_TYPE_CHANGE, ((TreeJPanel)getSelectedPanel()).getKeyType());
			}

		}
		else if (e.getActionCommand().equals(OptionEvent.COLOR_SETTINGS)) {

			optionEvent(e, (OptionListener)getSelectedPanel());
		}
		else if (e.getActionCommand().equals(OptionEvent.COLOR_SETTINGS_ALL)) {

			// Make new optionEvent
			OptionEvent optionEvent = new OptionEvent(this, id, OptionEvent.COLOR_SETTINGS, e.getObjectValue());
			// Pass on GET_STATUS
			optionEvent(optionEvent);

		}
		// Input Change Type
		else if ((e.getActionCommand().equals(OptionEvent.BALANCE))
				|| (e.getActionCommand().equals(OptionEvent.SELECT))
				|| (e.getActionCommand().equals(OptionEvent.CLEAR))
				//|| (e.getActionCommand().equals(OptionEvent.ANIMATION_ON))
				//|| (e.getActionCommand().equals(OptionEvent.ANIMATION_OFF))
				|| (e.getActionCommand().equals(OptionEvent.ANIMATION_STEP_ON))
				|| (e.getActionCommand().equals(OptionEvent.ANIMATION_STEP_OFF))
				|| (e.getActionCommand().equals(OptionEvent.GET_INPUT_OPTIONS))
				) {
			optionEvent(e, (OptionListener)getSelectedPanel());
		}
		else if (e.getActionCommand().equals(OptionEvent.GET_STATUS)) {
			JOptionPane.showMessageDialog(this,((TreeJPanel)getSelectedPanel()).getTreeStatusMessage(),((TreeJPanel)getSelectedPanel()).getTitle()+" Status", JOptionPane.INFORMATION_MESSAGE);
		}
		else if (e.getActionCommand().equals(OptionEvent.ANIMATION_PLAY)) {

			// Pass on play
			optionEvent(e);

			//messageAction(TreeMessageEvent.ANIMATION_STEP_CHANGE, new Boolean(((TreeJPanel)getSelectedPanel()).isStep()));

		}
		else if (e.getActionCommand().equals(OptionEvent.ANIMATION_REWIND)) {

			// Pass on rewind
			optionEvent(e);

			//messageAction(TreeMessageEvent.ANIMATION_STEP_CHANGE, new Boolean(((TreeJPanel)getSelectedPanel()).isStep()));

		}
		else if (e.getActionCommand().equals(OptionEvent.ANIMATION_STEP_REWIND)) {
			if (!((TreeJPanel)getSelectedPanel()).isStep()) {

				// Make new optionEvent
				OptionEvent optionEvent = new OptionEvent(this, id, OptionEvent.ANIMATION_STEP_ON);
				// Pass on animation step on
				optionEvent(optionEvent);

			}
			messageAction(TreeMessageEvent.ANIMATION_STEP_CHANGE, new Boolean(((TreeJPanel)getSelectedPanel()).isStep()));

			// Pass on rewind
			optionEvent(e);


		}
		else if (e.getActionCommand().equals(OptionEvent.ANIMATION_STEP_FORWARD)) {
			if (!((TreeJPanel)getSelectedPanel()).isStep()) {

				// Make new optionEvent
				OptionEvent optionEvent = new OptionEvent(this, id, OptionEvent.ANIMATION_STEP_ON);
				// Pass on animation step on
				optionEvent(optionEvent);

			}
			messageAction(TreeMessageEvent.ANIMATION_STEP_CHANGE, new Boolean(((TreeJPanel)getSelectedPanel()).isStep()));

			// Pass on play
			optionEvent(e);

		}
		else if (e.getActionCommand().equals(OptionEvent.COLOR_OPTIONS)) {
			dialog = new ColorPreferencesDialog((JFrame)e.getObjectValue(), colorOptions);

			dialog.pack();

			dialog.setApplied(true);
			dialog.setLocationRelativeTo(this);
			dialog.setVisible(true);
		}
		else if (e.getActionCommand().equals(OptionEvent.RESET)) {
				optionEvent(e, (OptionListener)getSelectedPanel());
		}
		else if (e.getActionCommand().equals(OptionEvent.SAVE_TREE)) {
			optionEvent(e, (OptionListener)getSelectedPanel());
		}
		else if (e.getActionCommand().equals(OptionEvent.SAVE_ALL)) {
			// Make new optionEvent
			OptionEvent currentTreeOptionEvent = new OptionEvent(this, id, OptionEvent.SAVE_TREE);

			optionEvent(currentTreeOptionEvent, (OptionListener)getSelectedPanel());

			// Make new optionEvent
			OptionEvent AllOptionEvent = new OptionEvent(this, id, OptionEvent.SAVE_ALL, getSelectedPanel());
			// Pass on animation step on
			optionEvent(AllOptionEvent);

		}
		else {
			optionEvent(e);
		}
	}

	ColorOptionsJPanel colorOptions;

	private void constructColorOptionsJPanel() {
		colorOptions = new ColorOptionsJPanel();
		colorOptions.setPreferredSize(new Dimension(425, 350));

		colorOptions.addOptionListener(this);

		this.addTreeMessageListener(colorOptions);
	}

	/**
	/************************************************/
	/* Implements ChangeListener                    */
	/************************************************/


	/**
	 * Implements Change Listener. On a state changed, messages are sent out
	 * according to the status of the new panel.
	 *
	 * @param e ChangeEvent passed when the state changes.
	 */
	public void stateChanged(ChangeEvent e) {

		((TreeJPanel)getSelectedPanel()).makeColorSchemeOptions();
		((TreeJPanel)getSelectedPanel()).makeColorSettings();

		messageAction(TreeMessageEvent.ANIMATION_CHANGE, new Boolean(((TreeJPanel)getSelectedPanel()).isAnimating()));
		messageAction(TreeMessageEvent.ANIMATION_STEP_CHANGE, new Boolean(((TreeJPanel)getSelectedPanel()).isStep()));

	}

	/**
	 * Overides the desktop method, to add functionality, by passing information about the changed
	 * frame by tree message.
	 *
	 * @param f JInternalFrame that is selected.
	 */
	public void setSelectedFrame(JInternalFrame f) {

		super.setSelectedFrame(f);

		((TreeJPanel)getSelectedPanel()).makeColorSchemeOptions();
		((TreeJPanel)getSelectedPanel()).makeColorSettings();

		messageAction(TreeMessageEvent.ANIMATION_CHANGE, new Boolean(((TreeJPanel)getSelectedPanel()).isAnimating()));
		messageAction(TreeMessageEvent.ANIMATION_STEP_CHANGE, new Boolean(((TreeJPanel)getSelectedPanel()).isStep()));

	}



}

