package com.tootsville.rahab;

import it.gotoandplay.smartfoxclient.ISFSEventListener;
import it.gotoandplay.smartfoxclient.SFSEvent;
import it.gotoandplay.smartfoxclient.SmartFoxClient;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.Border;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;

import org.json.JSONException;
import org.json.JSONObject;

/**
 * 
 * TODO: Document this type. Nov 12, 2009 : theys
 * 
 * @author <a href="mailto:theys@resinteractive.com">Tim Heys</a>
 * 
 */
final class LogInScreen extends JFrame {

	/**
	 * 
	 */
	private static final long serialVersionUID = -8682369178112663772L;

	/**
	 * 
	 */
	private static final String servers[] = {
			"whitney-comp.tootsville.com",
			"whitney-prime.tootsville.com",
			"whitney-dev.tootsville.com", "localhost" };
	/**
	 * 
	 */
	private final GridLayout layout;
	/**
	 * 
	 */
	private final Rahab mc;
	/**
	 * 
	 */
	private final JPasswordField passwordField;
	/**
	 * 
	 */
	private final JLabel passwordLabel = new JLabel ("Password: ");
	/**
	 * 
	 */
	protected String pw = "";
	/**
	 * 
	 */
	private final JComboBox serverField;
	/**
	 * 
	 */
	private final JLabel serverLabel = new JLabel ("Server: ");

	/**
	 * 
	 */
	private final Dimension size = new Dimension (300, 200);

	/**
	 * 
	 */
	private final JButton submitLogin;

	/**
	 * 
	 */
	protected String un = "";
	/**
	 * 
	 */
	private final JTextField userNameField;
	/**
	 * 
	 */
	private final JLabel userNameLabel = new JLabel ("User Name: ");

	/**
	 * @param matrixClient
	 */
	public LogInScreen (final Rahab matrixClient) {
		super ("Matrix Login");
		mc = matrixClient;
		setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		this.setSize (size);
		setBackground (Rahab.backgroundColor);
		setResizable (false);

		layout = new GridLayout (4, 2);
		layout.setHgap (5);
		layout.setVgap (5);

		userNameField = new JTextField (un);
		passwordField = new JPasswordField (pw);
		serverField = new JComboBox (LogInScreen.servers);
		submitLogin = new JButton ("Login");

		final KeyListener keyAction = new KeyListener () {
			/**
			 * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
			 */
			public void keyPressed (final KeyEvent e) {
				// do nothing
			}

			/**
			 * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
			 */
			public void keyReleased (final KeyEvent e) {
				// do nothing
			}

			/**
			 * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
			 */
			public void keyTyped (final KeyEvent e) {
				if (e.getKeyCode () == KeyEvent.VK_ENTER) {
					LogInScreen.this.submitLogin ();
				}
			}
		};

		passwordField.addKeyListener (keyAction);
		userNameField.addKeyListener (keyAction);
		submitLogin.addActionListener (new ActionListener () {
			/**
			 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
			 */
			public void actionPerformed (final ActionEvent e) {
				LogInScreen.this.submitLogin ();
			}
		});

		setLayout (layout);
		this.add (userNameLabel);
		this.add (userNameField);
		this.add (passwordLabel);
		this.add (passwordField);
		this.add (serverLabel);
		this.add (serverField);
		this.add (new JLabel ());
		this.add (submitLogin);

		setVisible (true);
	}

	/**
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	protected void submitLogin () {
		un = userNameField.getText ();
		pw = new String (passwordField.getPassword ());
		mc.setSpeaker (un, pw);
		mc.setServer ((String) serverField.getSelectedItem ());
		mc.initSmartFoxListener ();

		setEnabled (false);
		setVisible (false);
	}
}

/**
 * <p>
 * Rahab client connects to SmartFox on the whitney
 * 
 * @author theys
 * 
 */
public final class Rahab extends JFrame implements ISFSEventListener,
		MouseListener, KeyListener, ActionListener {

	/**
	 * 
	 * 
	 * TODO: The documentation for this type (Span) is incomplete.
	 * (brpocock, Sep 24, 2009)
	 * 
	 * @author brpocock
	 * 
	 */
	public static class Span {
		/**
		 * TODO: document this field (brpocock, Sep 24, 2009)
		 * 
		 * content (Span)
		 */
		public String content;

		/**
		 * TODO: document this field (brpocock, Sep 24, 2009)
		 * 
		 * styleName (Span)
		 */
		public String styleName;

		/**
		 * @param newStyleName WRITEME
		 * @param content1 WRITEME
		 */
		public Span (final String newStyleName, final String content1) {
			styleName = newStyleName;
			content = content1;
		}
	}

	/**
	 * TODO: document this field (theys)
	 */
	public static final Color backgroundColor = new Color (255, 171,
			211);

	/**
	 * TODO: document this field (theys)
	 */
	private static final String CANNOT_CONNECT = "Unable to establish a connection.";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String CONNECTED = "Connection established!  Attempting to log in as ";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String CONNECTION_LOST = "Connection lost!";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String displayTextName = "displayText";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String FAILED_CONNECTION = "Failed to connect to a zone, the zone parameter is null.";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String FAILED_LOGIN = "Unable to log in with your credentials.  Error Message: ";

	/**
	 * TODO: document this field (theys)
	 */
	public static final Color foregroundColor = new Color (255, 171,
			211);

	/**
	 * TODO: document this field (theys)
	 */
	public static final Color highlightColor = new Color (255, 171, 211);

	/**
	 * TODO: document this field (theys)
	 */
	private static final String JOIN_ROOM = "You have safely arrived in $Eaves.  Recording all chat in zone.";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String LOGIN = "Log in successful!  Joining a zone.";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String loginLobbyZone = "$Eden";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String modActorName = "modActor";

	/**
	 * Static string of the room name for $Eaves
	 */
	private static final String MODERATOR = "$Eaves";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String pubActorName = "pubActor";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String RECEIVE_KEY = "You have found the secret key.  Attemtping to open the door.";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String ROOM_LIST_UPDATE = "Rahab has delivered you into the world of Tootsville.  Gathering information about the world.";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String roomPassword = "Lasciate ogne speranza, voi ch-intrate";

	/**
	 * TODO: document this field (theys, Aug 17, 2009)
	 * 
	 * serialVersionUID (long)
	 */
	private static final long serialVersionUID = -2450199281303825218L;

	/**
	 * TODO: document this field (theys)
	 */
	private static final String sysActorName = "sysActor";

	/**
	 * TODO: document this field (theys)
	 */
	protected static final Speaker SystemOutput = new Speaker ("Rahab",
			"", Speaker.SYSTEM);

	/**
	 * TODO: document this field (theys)
	 */
	protected static Speaker user = null;

	/**
	 * TODO: document this field (theys)
	 */
	private static final String USER_JOIN = "You noticed another spy.";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String USER_LEAVE = "One of the other spies has fled.";

	/**
	 * TODO: document this field (theys)
	 */
	private static final String WELCOME = "Welcome.";

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param args WRITEME
	 */
	public static void main (final String [] args) {
		// Create Matrix UI
		final Rahab mc = new Rahab ();
		new LogInScreen (mc);
	}

	/**
	 * TODO: document this field (theys)
	 */
	transient Timer antiTimeOut;

	/**
	 * TODO: document this field (theys)
	 */
	// FIXME: transient field is not set by deserialization, but class
	// implements Serializable
	transient Border border = BorderFactory
			.createLineBorder (Color.black);

	/**
	 * TODO: document this field (theys)
	 */
	private final JPanel DetailsPane;
	/**
	 * TODO: document this field (theys)
	 */
	private transient final StyledDocument doc;
	/**
	 * TODO: document this field (theys)
	 */
	private final DefaultMutableTreeNode houseList;
	/**
	 * TODO: document this field (theys)
	 */
	private final JScrollPane ibMsgScrollPane;
	/**
	 * TODO: document this field (theys)
	 */
	final JTextPane inboundMsg;
	/**
	 * TODO: document this field (theys)
	 */
	private final JTextField inputText;
	/**
	 * TODO: document this field (theys)
	 */

	boolean isConnected = false;

	/**
	 * TODO: document this field (theys)
	 */
	private String key;
	/**
	 * TODO: document this field (theys)
	 */
	private final Vector <Span []> messages = new Vector <Span []> ();
	String messageSplit = "\u001f";

	/**
	 * TODO: document this field (theys)
	 */
	ImageIcon personIcon = createImageIcon ("images/person-icon.gif",
			"Person"), starIcon = createImageIcon (
			"images/star_icon.gif", "Star"),
			rahabIcon = createImageIcon ("images/rahab.jpg", "Rahab");

	/**
	 * 
	 */
	ISFSEventListener publicMessageEvent = new ISFSEventListener () {
		public void handleEvent (final SFSEvent event) {
			System.out.println ("Public message params: "
					+ event.getParams ().toString ());

			final String publicMessage = event.getParams ().getString (
					"message");

			if (publicMessage.contains (messageSplit)) {
				System.out.println ("Message pre parse: "
						+ publicMessage);

				// Message arrives in $Eaves room as
				// <username>0x1f<roomname>0x1f<message><0x1f>. Split on
				// the 0x1f char.
				Speaker whoSpoke = Rahab.SystemOutput;
				String verb = "";
				String msg = publicMessage;
				try {
					final String [] splits = publicMessage
							.split (messageSplit);
					final String userName = splits [0];
					final String room = splits [1];
					verb = splits [2];
					msg = splits [3];

					// In case we cannot parse the room correctly we
					// won't
					// throw a null pointer exception
					whoSpoke = Rahab.SystemOutput;
					if (room.equals (Rahab.MODERATOR)) {
						whoSpoke = new Speaker (userName,
								Speaker.MODERATOR);
					} else {
						whoSpoke = new Speaker (userName + " (" + room
								+ ")", Speaker.PUBLIC_USER);
					}
				} catch (final IndexOutOfBoundsException e) {
					// Do nothing here.
				}

				appendTextPane (whoSpoke, verb, msg);
			}
		}
	};
	/**
	 * TODO: document this field (theys)
	 */
	private final JLabel rahabLabel;

	/**
	 * TODO: document this field (theys)
	 */
	private final DefaultMutableTreeNode roomList;

	/**
	 * TODO: document this field (theys)
	 */

	private String server;

	/**
	 * TODO: document this field (theys)
	 */
	Dimension size = new Dimension (800, 600);
	/**
	 * Smart Fox Server
	 */
	public transient final SmartFoxClient smartFox;
	/**
	 * TODO: document this field (theys)
	 */
	private final JButton submitText;

	/**
	 * TODO: document this field (theys)
	 */
	transient TimerTask task = new TimerTask () {
		@Override
		public void run () {
			smartFox.sendXtMessage ("Tootsville", "ping",
					new JSONObject ());
			smartFox.sendXtMessage ("Tootsville", "getOnlineUsers",
					new JSONObject ());
			System.out
					.println ("Updating user list and pinging server.");
		}
	};

	/**
	 * TODO: document this field (theys)
	 */
	private final DefaultMutableTreeNode treeList;
	/**
	 * WRITEME
	 */
	@SuppressWarnings ("unused")
	private final HashMap <String, String> userList = new HashMap <String, String> ();

	/**
	 * TODO: document this field (theys)
	 */
	JList userListAlpha;
	/**
	 * WRITEME
	 */
	private final JScrollPane userListAlphaScroll;

	/**
	 * TODO: document this field (theys)
	 */
	JTree userListByRoom;

	/**
	 * WRITEME
	 */
	private final JScrollPane userListByRoomScroll;

	/**
	 * TODO: document this field (theys)
	 */
	JTabbedPane userListView;

	/**
	 * TODO: document this field (theys)
	 */
	private transient Style userNameStyle, modNameStyle, sysNameStyle,
			displayText;
	/** private final JButton warn, kick, ban; */

	/**
	 * TODO: document this field (theys)
	 */
	transient Speaker who = null;

	/**
	 * TODO: document this field (theys)
	 */
	Vector <Zone> zones = new Vector <Zone> ();

	/**
	 * TODO: document this field (theys)
	 */
	public Rahab () {
		super ("Rahab Chat Spy");
		setSize (size);
		setBackground (Rahab.backgroundColor);
		setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		setResizable (false);
		setBackground (Rahab.backgroundColor);
		JFrame.setDefaultLookAndFeelDecorated (true);

		final GridBagConstraints layoutConstraints = new GridBagConstraints ();
		setLayout (new GridBagLayout ());
		setComponentOrientation (ComponentOrientation.LEFT_TO_RIGHT);

		smartFox = new SmartFoxClient (false);

		// init JPanels
		userListView = new JTabbedPane ();
		userListView.setBackground (Rahab.backgroundColor);

		DetailsPane = new JPanel ();
		DetailsPane.setPreferredSize (new Dimension (800, 100));

		rahabLabel = new JLabel (rahabIcon);

		/**
		 * warn = new JButton ("Warn User"); kick = new JButton
		 * ("Kick User"); ban = new JButton ("Ban User");
		 * DetailsPane.add (warn); DetailsPane.add (kick);
		 * DetailsPane.add (ban);
		 */
		DetailsPane.setBackground (Color.white);
		DetailsPane.add (rahabLabel);

		userListAlpha = new JList (refreshUserListAlpha ());
		userListAlphaScroll = new JScrollPane (userListAlpha,
				ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		userListAlphaScroll.setPreferredSize (new Dimension (290, 495));
		userListView.addTab ("Sort By Name", userListAlphaScroll);

		treeList = new DefaultMutableTreeNode ("Tootsville");
		roomList = new DefaultMutableTreeNode ("Rooms");
		houseList = new DefaultMutableTreeNode ("Houses");

		treeList.add (roomList);
		treeList.add (houseList);
		userListByRoom = new JTree (treeList);

		if (null != personIcon && null != starIcon) {
			final DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer ();
			renderer.setLeafIcon (personIcon);
			renderer.setOpenIcon (starIcon);
			renderer.setClosedIcon (starIcon);
			userListByRoom.setCellRenderer (renderer);
		}
		userListByRoomScroll = new JScrollPane (userListByRoom,
				ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		userListByRoomScroll
				.setPreferredSize (new Dimension (290, 495));

		userListView.addTab ("Sort By Room", userListByRoomScroll);

		inboundMsg = new JTextPane ();
		inboundMsg.setPreferredSize (new Dimension (500, 500));
		doc = inboundMsg.getStyledDocument ();
		addStylesToDocument (doc);
		inboundMsg.setEditable (false);
		ibMsgScrollPane = new JScrollPane (inboundMsg,
				ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		ibMsgScrollPane.setPreferredSize (new Dimension (500, 500));
		// initStyles ();
		// ibMsgScrollPane.getVerticalScrollBar ().addAdjustmentListener
		// (
		// new AdjustmentListener () {
		// public void adjustmentValueChanged (
		// final AdjustmentEvent e) {
		// inboundMsg.scrollRectToVisible (new Rectangle (
		// 0, inboundMsg.getHeight () - 2, 1, 1));
		// }
		// });

		inputText = new JTextField ();
		inputText.setPreferredSize (new Dimension (400, 25));
		inputText.addKeyListener (this);

		submitText = new JButton ("Submit");
		submitText.setPreferredSize (new Dimension (100, 24));
		submitText.addMouseListener (this);

		// Tabbed User List
		layoutConstraints.gridx = 0;
		layoutConstraints.gridy = 0;
		layoutConstraints.gridwidth = 1;
		layoutConstraints.gridheight = 2;
		layoutConstraints.weightx = 0.5;
		layoutConstraints.weighty = 0.5;
		layoutConstraints.fill = GridBagConstraints.VERTICAL;
		add (userListView, layoutConstraints);

		// Output Text Box
		layoutConstraints.gridx = 1;
		layoutConstraints.gridy = 0;
		layoutConstraints.gridwidth = 2;
		layoutConstraints.gridheight = 1;
		layoutConstraints.fill = GridBagConstraints.BOTH;
		add (ibMsgScrollPane, layoutConstraints);

		// Input Text Box
		layoutConstraints.gridx = 1;
		layoutConstraints.gridy = 1;
		layoutConstraints.gridwidth = 1;
		layoutConstraints.gridheight = 1;
		add (inputText, layoutConstraints);

		// Submit Button
		layoutConstraints.gridx = 2;
		layoutConstraints.gridy = 1;
		layoutConstraints.gridwidth = 1;
		layoutConstraints.gridheight = 1;
		add (submitText, layoutConstraints);

		// Details Pane
		layoutConstraints.gridx = 0;
		layoutConstraints.gridy = 2;
		layoutConstraints.gridwidth = 3;
		layoutConstraints.gridheight = 1;
		add (DetailsPane, layoutConstraints);

		pack ();
		setVisible (true);
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param e WRITEME
	 */
	public void actionPerformed (final ActionEvent e) {
		String message;
		message = "#" + e.getActionCommand () + " ";
		smartFox.sendPublicMessage (message);
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param newDoc WRITEME
	 */
	protected void addStylesToDocument (final StyledDocument newDoc) {
		final Style defaultStyle = newDoc
				.getStyle (StyleContext.DEFAULT_STYLE);
		final int fontSize = 12;
		final String fontName = "monospaced";

		// Create and add the style for displaying public user's names
		userNameStyle = newDoc.addStyle (Rahab.pubActorName,
				defaultStyle);
		StyleConstants.setFontSize (userNameStyle, fontSize);
		StyleConstants.setFontFamily (userNameStyle, fontName);
		StyleConstants.setForeground (userNameStyle, Color.BLUE);
		StyleConstants.setBold (userNameStyle, true);

		// Create and add the style for displaying moderator's names
		modNameStyle = newDoc.addStyle (Rahab.modActorName,
				defaultStyle);
		StyleConstants.setFontSize (modNameStyle, fontSize);
		StyleConstants.setFontFamily (modNameStyle, fontName);
		StyleConstants.setForeground (modNameStyle, Color.RED);
		StyleConstants.setBold (modNameStyle, true);

		// Create and add the style for displaying system messages
		sysNameStyle = newDoc.addStyle (Rahab.sysActorName,
				defaultStyle);
		StyleConstants.setFontSize (sysNameStyle, fontSize);
		StyleConstants.setFontFamily (sysNameStyle, fontName);
		StyleConstants.setForeground (sysNameStyle, Color.BLACK);
		StyleConstants.setBold (sysNameStyle, true);

		// Create and add the style for displaying text
		displayText = newDoc.addStyle (Rahab.displayTextName,
				defaultStyle);
		StyleConstants.setFontSize (displayText, fontSize);
		StyleConstants.setFontFamily (displayText, fontName);
		StyleConstants.setForeground (displayText, Color.BLACK);
		StyleConstants.setBold (displayText, false);
	}

	/**
	 * @see #appendTextPane(Speaker, String, String)
	 * 
	 * @param speaker
	 * @param spoken
	 */
	protected void appendTextPane (final Speaker speaker,
			final String spoken) {
		appendTextPane (speaker, "", spoken);
	}

	/**
	 * Sorry about the complication of this method. This accepts the
	 * speaker and the message they typed, which is generally retrieved
	 * from the sfs SmartFox object. The filterText method is called to
	 * disect the string searching for words needing the moderator's
	 * attention. The filterText returns an array of Span objects which
	 * are snippets of text.
	 * 
	 * The switch method determines the speaker parameter's user level
	 * to decide how to format the string. The filteredText array of
	 * Span objects is copied to a new array (displayMessage) at
	 * position 1, and then the Span object with the formated user's
	 * name is added to position 0 of displayText.
	 * 
	 * @param speaker The speaker who is speaking
	 * @param verb WRITEME
	 * @param spoken a string of what the speaker typed.
	 */
	protected void appendTextPane (final Speaker speaker,
			final String verb, final String spoken) {
		final Span [] filteredText = filterText (spoken);
		final Span [] textLine = new Span [filteredText.length + 1];
		System.arraycopy (filteredText, 0, textLine, 1,
				filteredText.length);
		switch (speaker.getUserLevel ()) {
			default:
			case Speaker.PUBLIC_USER:
				textLine [0] = new Span (Rahab.pubActorName, speaker
						.getUserName ()
						+ " " + verb + " ");
				messages.add (textLine);
				break;
			case Speaker.MODERATOR:
				textLine [0] = new Span (Rahab.modActorName, speaker
						.getUserName ()
						+ " " + verb + " ");
				messages.add (textLine);
				break;
			case Speaker.SYSTEM:
				textLine [0] = new Span (Rahab.sysActorName, speaker
						.getUserName ()
						+ " " + verb + " ");
				messages.add (textLine);
				break;
		}
		for (int i = 0; i < messages.size (); i++ ) {
			final Span [] displayMessage = messages.get (i);
			for (final Span element : displayMessage) {
				try {
					doc.insertString (doc.getLength (),
							element.content, doc
									.getStyle (element.styleName));
				} catch (final BadLocationException e) {
					System.err.println (e.getMessage ());
				}
			}
			try {
				doc.insertString (doc.getLength (), "\n", doc
						.getStyle ("displayText"));
			} catch (final BadLocationException e) {
				System.err.println (e.getMessage ());
			}
		}
		messages.clear ();
		inboundMsg.scrollRectToVisible (new Rectangle (0, inboundMsg
				.getHeight () - 2, 1, 1));
	}

	/**
	 * Returns an ImageIcon, or null if the path was invalid.
	 * 
	 * @param path WRITEME
	 * @param description WRITEME
	 * @return WRITEME
	 */
	protected ImageIcon createImageIcon (final String path,
			final String description) {
		final java.net.URL imgURL = getClass ().getResource (path);
		if (imgURL != null) return new ImageIcon (imgURL, description);
		System.err.println ("Couldn't find file: " + path);
		return null;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param string WRITEME
	 * @return WRITEME
	 */
	public Span [] filterText (final String string) {
		final Span filteredText[] = { new Span (Rahab.displayTextName,
				string) };
		// TODO: write handler for filtering text. add bold style to bad
		// words.
		return filteredText;
	}

	/**
	 * TODO: document this method (brpocock, Aug 18, 2009)
	 * 
	 * @param pass WRITEME
	 * 
	 * @return WRITEME
	 */
	private String getApple (final String pass) {
		/* Check the CHAP key hex code sequence */

		byte [] sha1digest = {};
		try {
			final MessageDigest stomach = MessageDigest
					.getInstance ("SHA1");
			stomach.reset ();
			try {
				stomach.update ( (key + pass).getBytes ("US-ASCII"));
			} catch (final UnsupportedEncodingException e) {
				e.printStackTrace ();
			}
			sha1digest = stomach.digest ();
		} catch (final NoSuchAlgorithmException e) {
			e.printStackTrace ();
		} catch (final NumberFormatException e) {
			e.printStackTrace ();
		}
		final StringBuilder sha1hex = new StringBuilder (
				sha1digest.length * 2);
		for (final byte element : sha1digest) {
			sha1hex.append (Integer.toString (
					(element & 0xff) + 0x100, 16).substring (1));
		}
		return sha1hex.toString ();
	}

	/**
	 * receive (and handle) an event from the Smartfox server.
	 * 
	 * TODO: rewrite each even handler in a seperate method, ie
	 * sfsOnAdminMessage().
	 * 
	 * @see it.gotoandplay.smartfoxclient.ISFSEventListener#handleEvent(it.gotoandplay.smartfoxclient.SFSEvent)
	 */
	@SuppressWarnings ("unchecked")
	public void handleEvent (final SFSEvent event) {
		try {
			final String evName = event.getName ();
			System.out.println ("New event (" + evName + ")");

			// onConnection event is thrown on sfs.connect()
			if (evName.equals (SFSEvent.onConnection)) {
				if (event.getParams ().getBool ("success")) {
					appendTextPane (Rahab.SystemOutput, Rahab.CONNECTED
							+ Rahab.user.getUserName () + ".");
					isConnected = true;
				} else {
					appendTextPane (Rahab.SystemOutput,
							Rahab.CANNOT_CONNECT);
					retryLogin ();
				}
				return;
				// onExtension is thrown by several events, but we're
				// interested
				// in successful or unsuccessful login attempts
			} else if (evName.equals (SFSEvent.onExtensionResponse)) {
				// params and dataObj are used to read parameters with
				// onExtensionRespons
				final JSONObject dataObj = new JSONObject (event
						.getParams ().toString ())
						.getJSONObject ("dataObj");
				System.out.println ("dataObj as string: "
						+ dataObj.toString ());
				if (dataObj.has ("_cmd"))
				// logOK param for successful log in attempt
					if (dataObj.getString ("_cmd").equals ("logOK")
							|| dataObj.getString ("_cmd").equals (

							"loginComplete")) {
						if (dataObj.has ("zoneList")) {
							/**
							 * Only $Eden zone provides a zone list. We
							 * connect to $Eden first to get a zone
							 * list, but then connect to another zone
							 * from there. If dataObj doesn't have a
							 * zoneList property we assume we've
							 * connected to a different zone.
							 * 
							 */
							final JSONObject zoneList = dataObj
									.getJSONObject ("zoneList");
							appendTextPane (Rahab.SystemOutput,
									Rahab.LOGIN);
							initZoneList (zoneList);
							// open window for user to select zone to
							// join
							try {
								new ZoneSelect (zones, this);
								zones.clear ();
							} catch (final Exception e) {
								e.printStackTrace ();
							}
							return;
						}
						System.out
								.println ("Requesting list of rooms.");
						smartFox.getRoomList ();

						return;

						// logKO for unsuccessful log attempt
					} else if (dataObj.getString ("_cmd").equals (
							"logKO")) {
						appendTextPane (Rahab.SystemOutput,
								Rahab.FAILED_LOGIN
										+ dataObj.getString ("msg"));
						retryLogin ();
						return;
					} else
					// do nothing for now
					return;
				// Receiving a user list.
				if (dataObj.has ("userList")) {
					final JSONObject userList = dataObj
							.getJSONObject ("userList");
					updateRoomList (userList);

					final JSONObject incomingRoomList = dataObj
							.getJSONObject ("rooms");

					final Iterator <String> roomKey = incomingRoomList
							.keys ();
					final Vector <String> userRoomList = new Vector <String> ();
					userListByRoom.removeAll ();
					houseList.removeAllChildren ();
					while (roomKey.hasNext ()) {
						String room = roomKey.next ();

						final Iterator <String> i = incomingRoomList
								.getJSONObject (room).keys ();
						System.out.println (incomingRoomList
								.getJSONObject (room));
						while (i.hasNext ()) {
							final String userName = incomingRoomList
									.getJSONObject (room).getString (
											i.next ());
							userRoomList.add (userName);
						}
						if (room.equals ("$Eaves")) {
							room = "MODERATORS";
						} else

						if (room.equals ("nowhere")) {
							room = "PLAYING/POV";
						}
						updateRoomList (room, userRoomList);
						userRoomList.clear ();

					}
				}
			} else if (evName.equals (SFSEvent.onRandomKey)) {
				key = event.getParams ().getString ("key");
				appendTextPane (Rahab.SystemOutput, Rahab.RECEIVE_KEY);
				login (Rahab.loginLobbyZone);
				return;
			} else if (evName.equals (SFSEvent.onRoomListUpdate)) {
				appendTextPane (Rahab.SystemOutput,
						Rahab.ROOM_LIST_UPDATE);
				// Join $Eaves room to spy on all rooms
				smartFox.joinRoom ("$Eaves", Rahab.roomPassword, false);
				return;
			} else if (evName.equals (SFSEvent.onJoinRoom)) {
				appendTextPane (Rahab.SystemOutput, Rahab.JOIN_ROOM);

				antiTimeOut = new Timer ();
				antiTimeOut
						.scheduleAtFixedRate (task, 0, 1 * 1000 * 60);

				return;
			} else if (evName.equals (SFSEvent.onJoinRoomError)) {
				appendTextPane (Rahab.SystemOutput,
						"Unable to join $Eaves: "
								+ event.getParams ().toString ());

				return;
			} else if (evName == SFSEvent.onUserEnterRoom) {
				appendTextPane (Rahab.SystemOutput, Rahab.USER_JOIN);
				System.out.println ("User joined room: "
						+ event.getParams ().toString ());

				return;
			} else if (evName == SFSEvent.onUserCountChange)
				return;
			else if (evName == SFSEvent.onUserLeaveRoom) {
				appendTextPane (Rahab.SystemOutput, Rahab.USER_LEAVE);
				System.out.println (event.getParams ().toString ());

				return;
			} else if (evName == SFSEvent.onAdminMessage) {
				final String msg = event.getParams ().getString (
						"message");
				appendTextPane (Rahab.SystemOutput, msg);

			} else if (evName == SFSEvent.onConnectionLost) {
				appendTextPane (Rahab.SystemOutput,
						Rahab.CONNECTION_LOST);
				isConnected = false;
				antiTimeOut.cancel ();
				retryLogin ();

				return;
			} else {
				System.out.println ("SFS: unhandled event: "
						+ event.toString ());
			}
		} catch (final JSONException e) {
			System.err.println ("Can't interpret JSON data"
					+ e.getMessage ());
			return;
		}
	}

	/**
	 * TODO: document this field (theys)
	 */
	void initSmartFoxListener () {
		appendTextPane (Rahab.SystemOutput, Rahab.WELCOME);
		smartFox.setDebug (true);
		smartFox.smartConnect = false; // Don't use BlueBox proxy
		smartFox.addEventListener (SFSEvent.onAdminMessage, this);
		smartFox.addEventListener (SFSEvent.onBuddyList, this);
		smartFox.addEventListener (SFSEvent.onBuddyListError, this);
		smartFox.addEventListener (SFSEvent.onBuddyListUpdate, this);
		smartFox.addEventListener (SFSEvent.onBuddyPermissionRequest,
				this);
		smartFox.addEventListener (SFSEvent.onBuddyRoom, this);
		smartFox.addEventListener (SFSEvent.onConfigLoadFailure, this);
		smartFox.addEventListener (SFSEvent.onConfigLoadSuccess, this);
		smartFox.addEventListener (SFSEvent.onConnection, this);
		smartFox.addEventListener (SFSEvent.onConnectionLost, this);
		smartFox.addEventListener (SFSEvent.onCreateRoomError, this);
		smartFox.addEventListener (SFSEvent.onDebugMessage, this);
		smartFox.addEventListener (SFSEvent.onExtensionResponse, this);
		smartFox.addEventListener (SFSEvent.onJoinRoom, this);
		smartFox.addEventListener (SFSEvent.onJoinRoomError, this);
		smartFox.addEventListener (SFSEvent.onLogin, this);
		smartFox.addEventListener (SFSEvent.onLogout, this);
		smartFox.addEventListener (SFSEvent.onModeratorMessage, this);
		smartFox.addEventListener (SFSEvent.onObjectReceived, this);
		smartFox.addEventListener (SFSEvent.onPlayerSwitched, this);
		smartFox.addEventListener (SFSEvent.onPublicMessage,
				publicMessageEvent);
		smartFox.addEventListener (SFSEvent.onPrivateMessage, this);
		smartFox.addEventListener (SFSEvent.onRandomKey, this);
		smartFox.addEventListener (SFSEvent.onRoomListUpdate, this);
		smartFox.addEventListener (SFSEvent.onUserCountChange, this);
		smartFox.addEventListener (SFSEvent.onUserEnterRoom, this);
		smartFox.addEventListener (SFSEvent.onUserLeaveRoom, this);
		smartFox
				.addEventListener (SFSEvent.onUserVariablesUpdate, this);
		smartFox.addEventListener (SFSEvent.onRandomKey, this);

		appendTextPane (Rahab.SystemOutput,
				"You are connecting to the Smartfox server (" + server
						+ ")...");
		try {
			smartFox.connect (server, 2770);
			smartFox.getRandomKey ();
		} catch (final Exception e) {
			appendTextPane (Rahab.SystemOutput, "Unable to connect.");
			appendTextPane (Rahab.SystemOutput, e.getMessage ());
			retryLogin ();
		}

	}

	/**
	 * public void initStyles () { final Style defaultStyle = sc
	 * .getStyle (StyleContext.DEFAULT_STYLE); final int fontSize = 12;
	 * final String fontName = "monospaced";
	 * 
	 * // Create and add the style for displaying public user's names
	 */

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param zoneList WRITEME
	 * @throws JSONException WRITEME
	 */
	@SuppressWarnings ("unchecked")
	private void initZoneList (final JSONObject zoneList)
		throws JSONException {
		System.out.println ("Number of zones: " + zoneList.length ());
		final Iterator <String> keys = zoneList.keys ();
		String keyName = "";
		String zoneName = "", zoneHost = "";
		int usersOn;
		while (keys.hasNext ()) {
			keyName = keys.next ();
			System.out.println ("New zone #" + keyName);
			final JSONObject zoneObj = zoneList.getJSONObject (keyName);
			zoneName = zoneObj.getString ("name");
			zoneHost = zoneObj.getString ("host");
			usersOn = Integer.parseInt (zoneObj.getString ("usersOn"));
			System.out.println (zoneName + "\n" + zoneHost + "\n"
					+ usersOn);
			zones.add (new Zone (zoneName, zoneHost, usersOn));
		}
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param e WRITEME
	 */
	public void keyPressed (final KeyEvent e) {
		final int keyPressed = e.getKeyCode ();
		final int mod = e.getModifiersEx ();
		if (keyPressed == KeyEvent.VK_ENTER && mod == 0) {
			submitMessage ();
		}
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param e WRITEME
	 */
	public void keyReleased (final KeyEvent e) {
		// TODO Auto-generated method stub (theys, Sep 3, 2009)

	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param e WRITEME
	 */
	public void keyTyped (final KeyEvent e) {
		// No op
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param zone WRITEME
	 */
	public void login (final String zone) {
		if (null != zone && !zone.equals (Rahab.loginLobbyZone)) {
			smartFox.logout ();
			appendTextPane (Rahab.SystemOutput, "Connecting to " + zone);
			smartFox.login (zone, Rahab.user.getUserName (),
					getApple (Rahab.user.getPassword ()));
		} else if (null != zone) {
			smartFox.login (zone, Rahab.user.getUserName (),
					getApple (Rahab.user.getPassword ()));
		} else {
			System.err.println (Rahab.FAILED_CONNECTION);
		}
	}

	public void mouseClicked (final MouseEvent e) {
		submitMessage ();

	}

	/**
	 * This is an overriding method.
	 * 
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered (final MouseEvent arg0) {
		// TODO Auto-generated method stub (brpocock, Sep 17, 2009)

	}

	/**
	 * This is an overriding method.
	 * 
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited (final MouseEvent arg0) {
		// TODO Auto-generated method stub (brpocock, Sep 17, 2009)

	}

	/**
	 * This is an overriding method.
	 * 
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mousePressed (final MouseEvent arg0) {
		// TODO Auto-generated method stub (brpocock, Sep 17, 2009)

	}

	/**
	 * This is an overriding method.
	 * 
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased (final MouseEvent arg0) {
		// TODO Auto-generated method stub (brpocock, Sep 17, 2009)

	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @return WRITEME
	 */
	private String [] refreshUserListAlpha () {
		final String [] userListArray = { "", "" };
		return userListArray;

	}

	/**
	 * TODO: document this field (theys)
	 */
	private void retryLogin () {
		// Login failed, retry login
		new LogInScreen (this);
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param newServer WRITEME
	 */
	public void setServer (final String newServer) {
		server = newServer;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param userName WRITEME
	 * @param password WRITEME
	 */
	public void setSpeaker (final String userName, final String password) {
		Rahab.user = new Speaker (userName, password, Speaker.MODERATOR);
		setTitle ("Rahab Chat Spy (" + Rahab.user.getUserName () + ")");
	}

	/**
	 * TODO: document this field (theys)
	 */
	private void submitMessage () {
		final String input = inputText.getText ();
		smartFox.sendPublicMessage (input);
		inputText.setText ("");
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param userList WRITEME
	 */
	@SuppressWarnings ("unchecked")
	private void updateRoomList (final JSONObject userList) {
		final Vector <String> userConversion = new Vector <String> ();
		final Iterator <String> j = userList.keys ();
		while (j.hasNext ()) {
			userConversion.add (j.next ());
		}

		userListAlpha.removeAll ();
		userListAlpha.setListData (userConversion);
		userListAlphaScroll.revalidate ();
		userListAlphaScroll.repaint ();
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param room WRITEME
	 * @param userList WRITEME
	 */
	private void updateRoomList (final String room,
			final Vector <String> userList) {
		if ( !userList.isEmpty () && !room.contains ("user~")) {
			DefaultMutableTreeNode roomNode = null;
			DefaultMutableTreeNode userNode = null;

			roomNode = new DefaultMutableTreeNode (room);

			for (final String user1 : userList) {
				userNode = new DefaultMutableTreeNode (user1, false);
				roomNode.add (userNode);
			}

			roomList.add (roomNode);
			userListByRoom.revalidate ();
		} else if ( !userList.isEmpty () && room.contains ("user~")) {
			final String [] splits = room.split ("~");
			try {
				final String userRoomName = splits [1] + "'s House";
				DefaultMutableTreeNode roomNode = null;
				DefaultMutableTreeNode userNode = null;

				roomNode = new DefaultMutableTreeNode (userRoomName);

				for (final String user1 : userList) {
					userNode = new DefaultMutableTreeNode (user1, false);
					roomNode.add (userNode);
				}

				houseList.add (roomNode);
				userListByRoom.revalidate ();
			} catch (final IndexOutOfBoundsException e) {
				System.err
						.println ("Error: index out of bounds splitting on '~' for "
								+ room);
				e.printStackTrace ();
			}
		}
	}
}

/**
 * TODO: document this field (theys)
 */
class Speaker {
	/**
	 * TODO: document this field (theys)
	 */
	public static final int MODERATOR = 1;
	/**
	 * TODO: document this field (theys)
	 */
	public static final int PUBLIC_USER = 0;
	/**
	 * TODO: document this field (theys)
	 */
	public static final int SYSTEM = 2;

	/**
	 * TODO: document this field (theys)
	 */
	private String password = "";

	/**
	 * TODO: document this field (theys)
	 */
	private final int userLevel;
	/**
	 * TODO: document this field (theys)
	 */
	private String userName = "";

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param theSpeaker WRITEME
	 */
	public Speaker (final String theSpeaker) {
		this (theSpeaker, Speaker.PUBLIC_USER);
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param newUserName WRITEME
	 * @param newUserLevel WRITEME
	 */
	public Speaker (final String newUserName, final int newUserLevel) {
		setUserName (newUserName);
		userLevel = newUserLevel;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param userName1 WRITEME
	 * @param password1 WRITEME
	 * @param userLevel1 WRITEME
	 */
	public Speaker (final String userName1, final String password1,
			final int userLevel1) {
		setUserName (userName1);
		setPassword (password1);
		userLevel = userLevel1;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @return WRITEME
	 */
	public String getPassword () {
		// default getter (theys, Aug 17, 2009)
		return password;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @return WRITEME
	 */
	public int getUserLevel () {
		// default getter (theys, Aug 17, 2009)
		return userLevel;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @return WRITEME
	 */
	public String getUserName () {
		// default getter (theys, Aug 17, 2009)
		return userName;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param password1 WRITEME
	 */
	private void setPassword (final String password1) {
		password = password1;

	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param userName1 WRITEME
	 */
	public void setUserName (final String userName1) {
		// default setter (theys, Aug 17, 2009)
		userName = userName1;
	}
}

/**
 * TODO: document this field (theys)
 */
class Zone {

	/**
	 * TODO: document this field (theys)
	 */
	private int numberOfUsers = 0;
	/**
	 * TODO: document this field (theys)
	 */
	private String zoneHost = "";

	/**
	 * TODO: document this field (theys)
	 */
	private String zoneName = "";

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param newZone WRITEME
	 * @param newZoneHost WRITEME
	 * @param newZoneUsers WRITEME
	 */
	public Zone (final String newZone, final String newZoneHost,
			final int newZoneUsers) {
		zoneName = newZone;
		zoneHost = newZoneHost;
		numberOfUsers = newZoneUsers;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @return WRITEME
	 */
	public int getNumberOfUsers () {
		// default getter (theys, Aug 19, 2009)
		return numberOfUsers;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @return WRITEME
	 */
	public String getZoneHost () {
		// default getter (theys, Aug 19, 2009)
		return zoneHost;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @return WRITEME
	 */
	public String getZoneName () {
		// default getter (theys, Aug 19, 2009)
		return zoneName;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param numberOfUsers1 WRITEME
	 */
	public void setNumberOfUsers (final int numberOfUsers1) {
		// default setter (theys, Aug 19, 2009)
		numberOfUsers = numberOfUsers1;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param zoneHost1 WRITEME
	 */
	public void setZoneHost (final String zoneHost1) {
		// default setter (theys, Aug 19, 2009)
		zoneHost = zoneHost1;
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param zoneName1 WRITEME
	 */
	public void setZoneName (final String zoneName1) {
		// default setter (theys, Aug 19, 2009)
		zoneName = zoneName1;
	}
}

/**
 * TODO: document this field (theys)
 */
class ZoneSelect extends JFrame implements ActionListener {

	/**
	 * TODO: document this field (theys, Aug 19, 2009)
	 * 
	 * serialVersionUID (long)
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * TODO: document this field (theys)
	 */
	private final JLabel desc = new JLabel (
			"Please select a zone to join:");
	/**
	 * TODO: document this field (theys)
	 */
	private final Vector <String> listItems;
	/**
	 * TODO: document this field (theys)
	 */
	private final Vector <String> listNames;

	/**
	 * TODO: document this field (theys)
	 */
	private final JPanel listPane, buttonPane;
	/**
	 * TODO: document this field (theys)
	 */
	private final Rahab mc;
	/**
	 * TODO: document this field (theys)
	 */
	Dimension size = new Dimension (300, 200);
	/**
	 * TODO: document this field (theys)
	 */
	private final JButton submit;
	/**
	 * TODO: document this field (theys)
	 */
	private final JList zoneList;

	/**
	 * TODO: document this field (theys)
	 */
	private final JScrollPane zoneListScroll;
	/**
	 * TODO: document this field (theys)
	 */
	private final Vector <Zone> zones;

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param zones1 WRITEME
	 * @param mc1 WRITEME
	 */
	public ZoneSelect (final Vector <Zone> zones1, final Rahab mc1) {
		super ("Please select a zone");
		System.out.println ("zone list created");
		zones = zones1;
		mc = mc1;
		setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
		this.setSize (size);
		setResizable (false);

		listPane = new JPanel ();
		buttonPane = new JPanel ();

		listItems = new Vector <String> ();
		listNames = new Vector <String> ();
		int index = 0;
		for (final Zone element : zones) {
			listItems.add (index, element.getZoneName () + " ("
					+ element.getZoneHost () + ") Users: "
					+ element.getNumberOfUsers ());
			listNames.add (index, element.getZoneName ());
			index++ ;
		}
		zoneList = new JList (listItems);
		zoneList.setPreferredSize (new Dimension (100, 250));
		zoneList
				.setSelectionMode (ListSelectionModel.SINGLE_INTERVAL_SELECTION);
		zoneList.setLayoutOrientation (JList.HORIZONTAL_WRAP);
		zoneListScroll = new JScrollPane (zoneList);

		listPane.setLayout (new BoxLayout (listPane,
				BoxLayout.PAGE_AXIS));
		listPane.add (desc);
		listPane.add (Box.createRigidArea (new Dimension (0, 5)));
		listPane.add (zoneListScroll);
		listPane.setBorder (BorderFactory.createEmptyBorder (10, 10,
				10, 10));

		buttonPane.setLayout (new BoxLayout (buttonPane,
				BoxLayout.LINE_AXIS));
		buttonPane.setBorder (BorderFactory.createEmptyBorder (0, 10,
				10, 10));
		submit = new JButton ("Connect");
		submit.addActionListener (this);
		buttonPane.add (Box.createHorizontalGlue ());
		buttonPane.add (Box.createRigidArea (new Dimension (10, 0)));
		buttonPane.add (submit);

		add (listPane, BorderLayout.CENTER);
		add (buttonPane, BorderLayout.PAGE_END);

		setVisible (true);
	}

	/**
	 * TODO: document this field (theys)
	 * 
	 * @param zoneForm
	 */
	public void actionPerformed (final ActionEvent zoneForm) {
		final int index = zoneList.getSelectedIndex ();
		System.out.println ("Selected: " + listNames.elementAt (index));
		mc.login (listNames.elementAt (index));

		setEnabled (false);
		setVisible (false);
	}

}
