package it.gotoandplay.smartfoxclient;

import it.gotoandplay.smartfoxclient.data.Buddy;
import it.gotoandplay.smartfoxclient.data.Room;
import it.gotoandplay.smartfoxclient.data.RoomVariableRequest;
import it.gotoandplay.smartfoxclient.data.SFSObject;
import it.gotoandplay.smartfoxclient.data.SFSVariable;
import it.gotoandplay.smartfoxclient.data.User;
import it.gotoandplay.smartfoxclient.handlers.ExtHandler;
import it.gotoandplay.smartfoxclient.handlers.IMessageHandler;
import it.gotoandplay.smartfoxclient.handlers.SysHandler;
import it.gotoandplay.smartfoxclient.http.HttpConnection;
import it.gotoandplay.smartfoxclient.http.HttpEvent;
import it.gotoandplay.smartfoxclient.util.Entities;
import it.gotoandplay.smartfoxclient.util.SFSObjectSerializer;
import it.gotoandplay.utils.net.xmlsocket.IXMLSocketEventHandler;
import it.gotoandplay.utils.net.xmlsocket.XMLSocket;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.n3.nanoxml.IXMLElement;
import net.n3.nanoxml.IXMLParser;
import net.n3.nanoxml.IXMLReader;
import net.n3.nanoxml.StdXMLReader;
import net.n3.nanoxml.XMLException;
import net.n3.nanoxml.XMLParserFactory;

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

/**
 * <p>
 * SmartFoxClient is the main class in the SmartFoxServer API. This class is
 * responsible for connecting to the server and handling all related events.
 * </p>
 * 
 * <p>
 * <b>NOTE</b>: in the provided examples, {@code smartFox} always indicates a
 * SmartFoxClient instance.
 * </p>
 * 
 * @see SFSEvent#onAdminMessage
 * @see SFSEvent#onDebugMessage
 * @see SFSEvent#onExtensionResponse
 * @see SFSEvent#onRoomDeleted
 * @see SFSEvent#onUserEnterRoom
 * @see SFSEvent#onUserLeaveRoom
 * @see SFSEvent#onUserCountChange
 * 
 * @version 1.5.8
 * 
 * @author The gotoAndPlay() Team<br>
 *         <a href="http://www.smartfoxserver.com">http://www.smartfoxserver
 *         .com</a><br>
 *         <a href="http://www.gotoandplay.it">http://www.gotoandplay.it< /a><br>
 */
public class SmartFoxClient extends SFSEventDispatcher implements
		IXMLSocketEventHandler, ISFSEventListener {
	// -------------------------------------------------------
	// Constants
	// -------------------------------------------------------

	private static String blueBoxSeparator = "\n";
	/**
	 * Connection mode: "disconnected".
	 * <p>
	 * The client is currently disconnected from SmartFoxServer.
	 * </p>
	 * 
	 * @see #getConnectionMode
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public static final String CONNECTION_MODE_DISCONNECTED = "disconnected";
	/**
	 * Connection mode: "http".
	 * <p>
	 * The client is currently connected to SmartFoxServer via http.
	 * </p>
	 * 
	 * @see #getConnectionMode
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public static final String CONNECTION_MODE_HTTP = "http";
	/**
	 * Connection mode: "socket".
	 * <p>
	 * The client is currently connected to SmartFoxServer via socket.
	 * </p>
	 * 
	 * @see #getConnectionMode
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public static final String CONNECTION_MODE_SOCKET = "socket";
	/**
	 * 
	 */
	private static int DEFAULT_POLL_SPEED = 750;
	/**
	 * 
	 */
	private static String HTTP_POLL_REQUEST = "poll";
	/**
	 * 
	 */
	private static final String MESSAGE_HEADER_EXTENSION = "xt";
	/**
	 * 
	 */
	private static final String MESSAGE_HEADER_SYSTEM = "sys";
	/**
	 * Moderator message type: "to room".
	 * <p>
	 * The Moderator message is sent to all the users in a room.
	 * </p>
	 * 
	 * @see #sendModeratorMessage
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public final static String MODMSG_TO_ROOM = "r";

	/**
	 * Moderator message type: "to user".
	 * <p>
	 * The Moderator message is sent to a single user.
	 * </p>
	 * 
	 * @see #sendModeratorMessage
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public static final String MODMSG_TO_USER = "u";

	/**
	 * Moderator message type: "to zone".
	 * <p>
	 * The Moderator message is sent to all the users in a zone.
	 * </p>
	 * 
	 * @see #sendModeratorMessage
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public static String MODMSG_TO_ZONE = "z";
	/**
	 * 	
	 */
	private static final char MSG_JSON = '{';
	/**
	 * 
	 */
	private static char MSG_STR = '%';
	/**
	 * 
	 */
	private static final char MSG_XML = '<';

	/**
	 * Server-side extension request/response protocol: JSON.
	 * 
	 * @see SFSEvent#onExtensionResponse
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public static final String XTMSG_TYPE_JSON = "json";

	/**
	 * Server-side extension request/response protocol: String (aka
	 * "raw protocol").
	 * 
	 * @see SFSEvent#onExtensionResponse
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public static final String XTMSG_TYPE_STR = "str";

	/**
	 * Server-side extension request/response protocol: XML.
	 * 
	 * @see SFSEvent#onExtensionResponse
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public static final String XTMSG_TYPE_XML = "xml";

	// -------------------------------------------------------
	// Properties
	// -------------------------------------------------------

	private int _httpPollSpeed = SmartFoxClient.DEFAULT_POLL_SPEED; // bbox
	// poll
	// speed
	/**
	 * The property stores the id of the last room joined by the current user.
	 * <p>
	 * In most multiuser applications users can join one room at a time: in this
	 * case this property represents the id of the current room. If multi-room
	 * join is allowed, the application should track the various id(s) and this
	 * property should be ignored.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to retrieve the current room object (as
	 * an alternative to the {@link #getActiveRoom} method).
	 * 
	 * <pre>
	 * Room room = smartFox.getRoom (smartFox.activeRoomId);
	 * System.out.println (&quot;Current room is: &quot; + room.getName ());
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #getActiveRoom
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public int activeRoomId = -1;
	/**
	 * A boolean flag indicating if the user is recognized as Moderator.
	 * 
	 * <p>
	 * The following example shows how to check if the current user is a
	 * Moderator in the current SmartFoxServer zone.
	 * 
	 * <pre>
	 * if (smartfox.amIModerator)
	 * 	System.out.println (&quot;I'm a Moderator in this zone&quot;);
	 * else
	 * 	System.out.println (&quot;I'm a standard user&quot;);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #sendModeratorMessage
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public boolean amIModerator;

	private long benchStartTime;
	/**
	 * The BlueBox IP address.
	 * 
	 * @see #smartConnect
	 * @see #loadConfig
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public String blueBoxIpAddress;

	/**
	 * The BlueBox connection port.
	 * 
	 * @see #smartConnect
	 * @see #loadConfig
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public int blueBoxPort;
	/**
	 * A {@code List} containing {@link Buddy} objects representing each buddy
	 * of the user's buddy list.
	 * <p>
	 * Specific buddy can be retrieved by means of the {@link #getBuddyById} and
	 * {@link #getBuddyByName} methods.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: This property and all the buddy-related method are available
	 * only if the buddy list feature is enabled for the current zone. Check the
	 * SmartFoxServer server-side configuration.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to retrieve the properties of each buddy
	 * in the buddy list.
	 * 
	 * <pre>
	 * for (Buddy buddy : smartFox.buddyList) {
	 * 
	 * 	// Trace buddy properties
	 * 	System.out.println (&quot;Buddy id: &quot; + buddy.getId ());
	 * 	System.out.println (&quot;Buddy name: &quot; + buddy.getName ());
	 * 	System.out.println (&quot;Is buddy online? &quot;
	 * 			+ (buddy.isOnline () ? &quot;Yes&quot; : &quot;No&quot;));
	 * 	System.out.println (&quot;Is buddy blocked? &quot;
	 * 			+ (buddy.isBlocked () ? &quot;Yes&quot; : &quot;No&quot;));
	 * 
	 * 	// Trace all Buddy Variables
	 * 	for (String key : buddy.getVariables ().keySet ()) {
	 * 		System.out.println (&quot;\t&quot; + key + &quot; --&gt; &quot;
	 * 				+ buddy.getVariables ().get (key));
	 * 	}
	 * }
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see Buddy
	 * @see #myBuddyVars
	 * @see #loadBuddyList
	 * @see #getBuddyById
	 * @see #getBuddyByName
	 * @see #removeBuddy
	 * @see #setBuddyBlockStatus
	 * @see #setBuddyVariables
	 * @see SFSEvent#onBuddyList
	 * @see SFSEvent#onBuddyListUpdate
	 * 
	 * @since SmartFoxServer Basic (except block status) / Pro v1.x.x
	 */
	public List <Buddy> buddyList;
	/*
	 * A boolean flag indicating if the process of joining a new room is in
	 * progress.
	 */
	public boolean changingRoom;

	private boolean connected;
	private boolean debug;

	/**
	 * The default login zone.
	 * 
	 * @see #loadConfig
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public String defaultZone;
	private ExtHandler extHandler;

	private final HttpConnection httpConnection; // the http connection

	/**
	 * The TCP port used by the embedded webserver.
	 * <p>
	 * The default port is <b>8080</b>; if the webserver is listening on a
	 * different port number, this property should be set to that value.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to retrieve the webserver's current http
	 * port.
	 * 
	 * <pre>
	 * System.out.println (&quot;HTTP port is: &quot; + smartfox.httpPort);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @since SmartFoxServer Pro v1.5.0
	 */
	public int httpPort = 8080;

	/**
	 * The SmartFoxServer IP address.
	 * 
	 * @see #connect
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public String ipAddress;

	// --- BlueBox settings (start)
	// ---------------------------------------------------------------------

	private boolean isHttpMode = false; // connection mode
	private final Logger logger;
	private final int majVersion;
	private final Map <String, IMessageHandler> messageHandlers;
	private final int minVersion;

	/**
	 * The current user's Buddy Variables.
	 * <p>
	 * This is a {@code Map} containing the current user's properties when
	 * he/she is present in the buddy lists of other users. See the
	 * {@link #setBuddyVariables} method for more details.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to read the current user's own Buddy
	 * Variables.
	 * 
	 * <pre>
	 * for (String key : smartFox.myBuddyVars.keySet ()) {
	 * 	System.out.println (&quot;Variable&quot; + key + &quot; --&gt; &quot;
	 * 			+ smartFox.myBuddyVars.get (key));
	 * }
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #setBuddyVariables
	 * @see #getBuddyById
	 * @see #getBuddyByName
	 * @see SFSEvent#onBuddyList
	 * @see SFSEvent#onBuddyListUpdate
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public Map <String, String> myBuddyVars;

	/**
	 * The current user's SmartFoxServer id.
	 * <p>
	 * The id is assigned to a user on the server-side as soon as the client
	 * connects to SmartFoxServer successfully.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE:</b> client-side, the <b>myUserId</b> property is available only
	 * after a successful login is performed using the default login procedure.
	 * If a custom login process is implemented, this property must be manually
	 * set after the successful login!
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to retrieve the user's own SmartFoxServer
	 * id.
	 * 
	 * <pre>
	 * System.out.println (&quot;My user ID is: &quot; + smartFox.myUserId);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #myUserName
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public int myUserId;

	/**
	 * The current user's SmartFoxServer username.
	 * 
	 * <p>
	 * <b>NOTE</b>: client-side, the <b>myUserName</b> property is available
	 * only after a successful login is performed using the default login
	 * procedure. If a custom login process is implemented, this property must
	 * be manually set after the successful login!
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to retrieve the user's own SmartFoxServer
	 * username.
	 * 
	 * <pre>
	 * System.out.println (&quot;I logged in as: &quot; + smartFox.myUserName);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #myUserId
	 * @see #login
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public String myUserName;

	// --- BlueBox settings (end)
	// ---------------------------------------------------------------------

	/**
	 * The current user's id as a player in a game room.
	 * <p>
	 * The <b>playerId</b> is available only after the user successfully joined
	 * a game room. This id is 1-based (player 1, player 2, etc.), but if the
	 * user is a spectator or the room is not a game room, its value is -1.
	 * </p>
	 * <p>
	 * When a user joins a game room, a player id (or "slot") is assigned to
	 * him/her, based on the slots available in the room at the moment in which
	 * the user entered it; for example:
	 * <ul>
	 * <li>
	 * in a game room for 2 players, the first user who joins it becomes player
	 * one (playerId = 1) and the second user becomes player two (player = 2);</li>
	 * <li>
	 * in a game room for 4 players where only player three is missing, the next
	 * user who will join the room will be player three (playerId = 3);</li>
	 * </ul>
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: if multi-room join is allowed, this property contains only
	 * the last player id assigned to the user, and so it's useless. In this
	 * case the {@link Room#getMyPlayerIndex} method should be used to retrieve
	 * the player id for each joined room.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to retrieve the user's own player id.
	 * 
	 * <pre>
	 * System.out.println (&quot;I'm player &quot; + smartFox.playerId);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see Room#getMyPlayerIndex
	 * @see Room#isGame
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public int playerId;

	private TimerTask poolTask;

	private Timer poolTimer;

	/**
	 * The SmartFoxServer connection port. The default port is <b>9339</b>.
	 * 
	 * @see #connect
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public int port = 9339;

	private Map <Integer, Room> roomList;

	/**
	 * A boolean flag indicating if the BlueBox http connection should be used
	 * in case a socket connection is not available.
	 * <p>
	 * The default value is {@code true}.
	 * </p>
	 * 
	 * @see #loadConfig
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public boolean smartConnect = true;

	private final XMLSocket socketConnection;

	private final int subVersion;

	private SysHandler sysHandler;

	/**
	 * The SmartFoxClient contructor.
	 * 
	 * @param debug turn on the debug messages.
	 */
	public SmartFoxClient (final boolean debug) {
		this.debug = debug;
		logger = Logger.getLogger (SmartFoxClient.class.getName ());
		logger.setLevel (Level.INFO);
		// Initialize properties
		majVersion = 1;
		minVersion = 5;
		subVersion = 8;

		activeRoomId = -1;

		messageHandlers = new HashMap <String, IMessageHandler> ();
		setupMessageHandlers ();

		// Initialize socket object
		socketConnection = new XMLSocket ();
		socketConnection.setEventHandler (this);

		// Initialize HttpConnection
		httpConnection = new HttpConnection ();
		httpConnection.addEventListener (HttpEvent.onHttpConnect, this);
		httpConnection.addEventListener (HttpEvent.onHttpClose, this);
		httpConnection.addEventListener (HttpEvent.onHttpData, this);
		httpConnection.addEventListener (HttpEvent.onHttpError, this);
	}

	public void __logout () {
		initialize (true);
	}

	/**
	 * Add a user to the buddy list.
	 * <p>
	 * Since SmartFoxServer Pro 1.6.0, the buddy list feature can be configured
	 * to use a <i>basic</i> or <i>advanced</i> security mode (see the
	 * SmartFoxServer server-side configuration file). Check the following usage
	 * notes for details on the behavior of the <b>addBuddy</b> method in the
	 * two cases.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE:</b><br>
	 * Before you can add or remove any buddy from the list you must load the
	 * buddy-list from the server. Always make sure to call
	 * {@link #loadBuddyList} before interacting with the buddy-list.<br>
	 * <i>Basic security mode</i><br>
	 * When a buddy is added, if the buddy list is already full, the
	 * {@link SFSEvent#onBuddyListError} event is fired; otherwise the buddy
	 * list is updated and the {@link SFSEvent#onBuddyList} event is fired.
	 * <hr>
	 * <i>Advanced security mode</i><br>
	 * If the {@code <addBuddyPermission>} parameter is set to {@code true} in
	 * the buddy list configuration section of a zone, before the user is
	 * actually added to the buddy list he/she must grant his/her permission.
	 * The permission request is sent if the user is online only; the user
	 * receives the {@link SFSEvent#onBuddyPermissionRequest} event. When the
	 * permission is granted, the buddy list is updated and the
	 * {@link SFSEvent#onBuddyList} event is fired. If the permission is not
	 * granted (or the buddy didn't receive the permission request), the
	 * <b>addBuddy</b> method can be called again after a certain amount of time
	 * only. This time is set in the server configuration {@code
	 * <permissionTimeOut>} parameter. Also, if the {@code <mutualAddBuddy>}
	 * parameter is set to {@code true}, when user A adds user B to the buddy
	 * list, he/she is automatically added to user B's buddy list. Lastly, if
	 * the buddy list is full, the {@link SFSEvent#onBuddyListError} event is
	 * fired.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to add a user to the buddy list.
	 * 
	 * <pre>
	 * smartFox.addBuddy (&quot;jack&quot;);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param buddyName the name of the user to be added to the buddy list.
	 * 
	 * @see #buddyList
	 * @see #removeBuddy
	 * @see #setBuddyBlockStatus
	 * @see SFSEvent#onBuddyList
	 * @see SFSEvent#onBuddyListError
	 * @see SFSEvent#onBuddyPermissionRequest
	 * 
	 * @since SmartFoxServer Basic (except <i>advanced mode</i>) / Pro v1.x.x
	 */
	public void addBuddy (final String buddyName) {
		if (!buddyName.equals (myUserName)
				&& !checkBuddyDuplicates (buddyName)) {
			final String xmlMsg = "<n>" + buddyName + "</n>";
			send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "addB", -1,
					xmlMsg);
		}
	}

	private void addMessageHandler (final String key,
			final IMessageHandler handler) {
		if (messageHandlers.get (key) == null) {
			messageHandlers.put (key, handler);
		} else {
			debugMessage ("Warning, message handler called: " + key
					+ " already exist!", Level.WARNING);
		}
	}

	/**
	 * Automatically join the the default room (if existing) for the current
	 * zone.
	 * <p>
	 * A default room can be specified in the SmartFoxServer server-side
	 * configuration by adding the {@code autoJoin = "true"} attribute to one of
	 * the {@code <Room>} tags in a zone. When a room is marked as
	 * <i>autoJoin</i> it becomes the default room where all clients are joined
	 * when this method is called.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to join the default room in the current
	 * zone.
	 * 
	 * <pre>
	 * smartFox.autoJoin ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #joinRoom
	 * @see SFSEvent#onJoinRoom
	 * @see SFSEvent#onJoinRoomError
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void autoJoin () {
		if (!checkRoomList ())
			return;

		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "autoJoin",
				activeRoomId, "");
	}

	private boolean checkBuddyDuplicates (final String buddyName) {
		// Check for buddy duplicates in the current buddy list

		boolean res = false;

		for (final Buddy buddy : buddyList) {
			if (buddy.getName ().equals (buddyName)) {
				res = true;
				break;
			}
		}

		return res;
	}

	// -------------------------------------------------------
	// Constructor
	// -------------------------------------------------------

	private boolean checkJoin () {
		boolean success = true;

		if (activeRoomId < 0) {
			success = false;
			errorTrace ("You haven't joined any rooms!\nIn order to interact with the server you should join at least one room.\nPlease consult the documentation for more infos.");
		}

		return success;
	}

	// -------------------------------------------------------
	// Public methods
	// -------------------------------------------------------

	private boolean checkRoomList () {
		boolean success = true;

		if (roomList == null || roomList.size () == 0) {
			success = false;
			errorTrace ("The room list is empty!\nThe client API cannot function properly until the room list is populated.\nPlease consult the documentation for more infos.");
		}

		return success;
	}

	/**
	 * Remove all users from the buddy list.
	 * 
	 * <p>
	 * The following example shows how to clear the buddy list.
	 * 
	 * <pre>
	 * smartFox.clearBuddyList ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @deprecated In order to avoid conflits with the buddy list <i>advanced
	 *             security mode</i> implemented since SmartFoxServer Pro 1.6.0,
	 *             buddies should be removed one by one, by iterating through
	 *             the buddy list.
	 * 
	 * @see #buddyList
	 * @see SFSEvent#onBuddyList
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	@Deprecated
	public void clearBuddyList () {
		buddyList = new CopyOnWriteArrayList <Buddy> ();
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "clearB", -1, "");

		// Fire event!
		final SFSObject params = new SFSObject ();
		params.put ("list", buddyList);

		final SFSEvent evt = new SFSEvent (SFSEvent.onBuddyList, params);
		dispatchEvent (evt);
	}

	public void clearRoomList () {
		roomList = new ConcurrentHashMap <Integer, Room> ();
	}

	private String closeHeader () {
		return "</msg>";
	}

	/**
	 * Establish a connection to SmartFoxServer using the default port 9339.
	 * <p>
	 * The client usually gets connected to SmartFoxServer through a socket
	 * connection. In SmartFoxServer Pro, if a socket connection is not
	 * available and the {@link #smartConnect} property is set to {@code true},
	 * an http connection to the BlueBox module is attempted.
	 * </p>
	 * <p>
	 * When a successful connection is established, the
	 * {@link #getConnectionMode} can be used to check the current connection
	 * mode.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to connect to SmartFoxServer.
	 * 
	 * <pre>
	 * smartFox.connect (&quot;127.0.0.1&quot;);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param ipAdr the SmartFoxServer ip address.
	 * 
	 * @see #disconnect
	 * @see #getConnectionMode
	 * @see #smartConnect
	 * @see SFSEvent#onConnection
	 * 
	 * @since SmartFoxServer Basic (except BlueBox connection) / Pro v1.x.x
	 */
	public void connect (final String ipAdr) {
		connect (ipAdr, 9339);
	}

	/**
	 * Establish a connection to SmartFoxServer.
	 * <p>
	 * The client usually gets connected to SmartFoxServer through a socket
	 * connection. In SmartFoxServer Pro, if a socket connection is not
	 * available and the {@link #smartConnect} property is set to {@code true},
	 * an http connection to the BlueBox module is attempted.
	 * </p>
	 * <p>
	 * When a successful connection is established, the
	 * {@link #getConnectionMode} can be used to check the current connection
	 * mode.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to connect to SmartFoxServer.
	 * 
	 * <pre>
	 * smartFox.connect (&quot;127.0.0.1&quot;);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param sfsAddress the SmartFoxServer ip address.
	 * @param sfsPortNumber the SmartFoxServer TCP port.
	 * 
	 * @see #disconnect
	 * @see #getConnectionMode
	 * @see #smartConnect
	 * @see SFSEvent#onConnection
	 * 
	 * @since SmartFoxServer Basic (except BlueBox connection) / Pro v1.x.x
	 */
	public void connect (final String sfsAddress,
			final int sfsPortNumber) {
		if (!connected) {
			initialize ();
			ipAddress = sfsAddress;
			port = sfsPortNumber;

			socketConnection.connect (sfsAddress, sfsPortNumber);
		} else {
			debugMessage ("*** ALREADY CONNECTED ***", Level.WARNING);
		}
	}

	/**
	 * Dynamically create a new room in the current zone.
	 * 
	 * <p>
	 * <b>NOTE</b>: if the newly created room is a game room, the user is joined
	 * automatically upon successful room creation.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to create a new room.
	 * 
	 * <pre>
	 * Map &lt;String, Object&gt; roomProperties = new HashMap &lt;String, Object&gt; ();
	 * roomProperties.put (&quot;isGame&quot;, true);
	 * Map &lt;String, RoomVariableRequest&gt; variables = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * variables.put (&quot;ogres&quot;, new RoomVariableRequest (&quot;5&quot;,
	 * 		SFSVariable.TYPE_NUMBER, true));
	 * variables.put (&quot;skeletons&quot;, new RoomVariableRequest (&quot;4&quot;,
	 * 		SFSVariable.TYPE_NUMBER));
	 * roomProperties.put (&quot;vars&quot;, variables);
	 * smartFox.createRoom (&quot;The Cave&quot;, 15, roomProperties);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param name the room name.
	 * @param maxUsers the maximum number of users that can join the room.
	 * @param roomProperties a {@code Map} with the following key/value pairs:
	 *        <ul>
	 *        <li>
	 *        password: ({@code String}) a password to make the room private
	 *        (optional, default: none).</li>
	 *        <li>
	 *        maxSpectators: ({@code Integer}) in game rooms only, the maximum
	 *        number of spectators that can join the room (optional, default
	 *        value: 0).</li>
	 *        <li>
	 *        isGame: ({@code Boolean}) if {@code true}, the room is a game room
	 *        (optional, default value: {@code false}).</li>
	 *        <li>
	 *        exitCurrentRoom: ({@code Boolean}) if {@code true} and in case of
	 *        game room, the new room is joined after creation (optional,
	 *        default value: {@code true}).</li>
	 *        <li>
	 *        joinAsSpectator: ({@code Boolean}) if {@code true} and in case of
	 *        game room, allows to join the new room as spectator (optional,
	 *        default value: {@code false}).</li>
	 *        <li>
	 *        uCount: ({@code Boolean}) if {@code true}, the new room will
	 *        receive the {@link SFSEvent#onUserCountChange} notifications
	 *        (optional, default <u>recommended</u> value: {@code false}).</li>
	 *        <li>
	 *        vars: ({@code Map<String, RoomVariableRequest>}) a {@code Map} of
	 *        Room Variables, as described in the {@link #setRoomVariables}
	 *        method documentation (optional, default: none).</li>
	 *        <li>
	 *        A Room-level extension can be attached to any room during creation
	 *        using the following key/value pairs (optional)
	 *        <ul>
	 *        <li>
	 *        extensionName: ({@code String}) the name used to reference the
	 *        extension (see the SmartFoxServer server-side configuration).</li>
	 *        <li>
	 *        extensionScript: ({@code String}) the file name of the extension
	 *        script (for Actionscript and Python); if Java is used, the fully
	 *        qualified name of the extension must be provided. The file name is
	 *        relative to the root of the extension folder ("sfsExtensions/" for
	 *        Actionscript and Python, "javaExtensions/" for Java).</li>
	 *        </ul>
	 *        </li>
	 *        </ul>
	 * 
	 * @see SFSEvent#onRoomAdded
	 * @see SFSEvent#onCreateRoomError
	 * @see SFSEvent#onUserCountChange
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void createRoom (final String name, final int maxUsers,
			final Map <String, Object> roomProperties) {
		createRoom (name, maxUsers, roomProperties, activeRoomId);
	}

	/**
	 * Dynamically create a new room in the current zone.
	 * 
	 * <p>
	 * <b>NOTE</b>: if the newly created room is a game room, the user is joined
	 * automatically upon successful room creation.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to create a new room.
	 * 
	 * <pre>
	 * Map &lt;String, Object&gt; roomProperties = new HashMap &lt;String, Object&gt; ();
	 * roomProperties.put (&quot;isGame&quot;, true);
	 * Map &lt;String, RoomVariableRequest&gt; variables = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * variables.put (&quot;ogres&quot;, new RoomVariableRequest (&quot;5&quot;,
	 * 		SFSVariable.TYPE_NUMBER, true));
	 * variables.put (&quot;skeletons&quot;, new RoomVariableRequest (&quot;4&quot;,
	 * 		SFSVariable.TYPE_NUMBER));
	 * roomProperties.put (&quot;vars&quot;, variables);
	 * smartFox.createRoom (&quot;The Cave&quot;, 15, roomProperties, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param name the room name.
	 * @param maxUsers the maximum number of users that can join the room.
	 * @param roomProperties a {@code Map} with the following key/value pairs:
	 *        <ul>
	 *        <li>
	 *        password: ({@code String}) a password to make the room private
	 *        (optional, default: none).</li>
	 *        <li>
	 *        maxSpectators: ({@code Integer}) in game rooms only, the maximum
	 *        number of spectators that can join the room (optional, default
	 *        value: 0).</li>
	 *        <li>
	 *        isGame: ({@code Boolean}) if {@code true}, the room is a game room
	 *        (optional, default value: {@code false}).</li>
	 *        <li>
	 *        exitCurrentRoom: ({@code Boolean}) if {@code true} and in case of
	 *        game room, the new room is joined after creation (optional,
	 *        default value: {@code true}).</li>
	 *        <li>
	 *        joinAsSpectator: ({@code Boolean}) if {@code true} and in case of
	 *        game room, allows to join the new room as spectator (optional,
	 *        default value: {@code false}).</li>
	 *        <li>
	 *        uCount: ({@code Boolean}) if {@code true}, the new room will
	 *        receive the {@link SFSEvent#onUserCountChange} notifications
	 *        (optional, default <u>recommended</u> value: {@code false}).</li>
	 *        <li>
	 *        vars: ({@code Map<String, RoomVariableRequest>}) a {@code Map} of
	 *        Room Variables, as described in the {@link #setRoomVariables}
	 *        method documentation (optional, default: none).</li>
	 *        <li>
	 *        A Room-level extension can be attached to any room during creation
	 *        using the following key/value pairs (optional)
	 *        <ul>
	 *        <li>
	 *        extensionName: ({@code String}) the name used to reference the
	 *        extension (see the SmartFoxServer server-side configuration).</li>
	 *        <li>
	 *        extensionScript: ({@code String}) the file name of the extension
	 *        script (for Actionscript and Python); if Java is used, the fully
	 *        qualified name of the extension must be provided. The file name is
	 *        relative to the root of the extension folder ("sfsExtensions/" for
	 *        Actionscript and Python, "javaExtensions/" for Java).</li>
	 *        </ul>
	 *        </li>
	 *        </ul>
	 * 
	 * @param roomId the id of the room from where the request is originated, in
	 *        case the application allows multi-room join.
	 * 
	 * @see SFSEvent#onRoomAdded
	 * @see SFSEvent#onCreateRoomError
	 * @see SFSEvent#onUserCountChange
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	@SuppressWarnings ("unchecked")
	public void createRoom (final String name, final int maxUsers,
			final Map <String, Object> roomProperties, final int roomId) {
		if (!checkRoomList () || !checkJoin ())
			return;

		try {
			String isGame = "0";
			String exitCurrentRoom = "1";
			String joinAsSpectator = "0";
			final Boolean isGameBool = (Boolean) roomProperties
					.get ("isGame");
			if (isGameBool != null) {
				if (isGameBool) {
					isGame = "1";
					final Boolean exitCurrentRoomBool = (Boolean) roomProperties
							.get ("exitCurrentRoom");
					if (exitCurrentRoomBool != null) {
						exitCurrentRoom = exitCurrentRoomBool ? "1"
								: "0";
					}
					final Boolean joinAsSpectatorBool = (Boolean) roomProperties
							.get ("joinAsSpectator");
					if (joinAsSpectatorBool != null) {
						joinAsSpectator = joinAsSpectatorBool ? "1"
								: "0";
					}

				}
			}

			String maxSpectators = "0";
			final Integer maxSpectatorsInt = (Integer) roomProperties
					.get ("maxSpectators");
			if (maxSpectatorsInt != null) {
				maxSpectators = maxSpectatorsInt.toString ();
			}

			final String password = (String) roomProperties
					.get ("password");

			String uCount = "0";
			final Boolean uCountBool = (Boolean) roomProperties
					.get ("uCount");
			if (uCountBool != null) {
				uCount = uCountBool ? "1" : "0";
			}

			final String extensionName = (String) roomProperties
					.get ("extensionName");
			final String extensionScript = (String) roomProperties
					.get ("extensionScript");

			String xmlMsg = "<room tmp='1' gam='" + isGame + "' spec='"
					+ maxSpectators + "' exit='" + exitCurrentRoom
					+ "' jas='" + joinAsSpectator + "'>";

			xmlMsg += "<name><![CDATA[" + (name == null ? "" : name)
					+ "]]></name>";
			xmlMsg += "<pwd><![CDATA["
					+ (password == null ? "" : password) + "]]></pwd>";
			xmlMsg += "<max>" + maxUsers + "</max>";
			xmlMsg += "<uCnt>" + uCount + "</uCnt>";

			// Set extension for room
			if (extensionName != null && extensionScript != null) {
				xmlMsg += "<xt n='" + extensionName;
				xmlMsg += "' s='" + extensionScript + "' />";
			}

			final Map <String, RoomVariableRequest> vars = (Map <String, RoomVariableRequest>) roomProperties
					.get ("vars");

			// Set Room Variables on creation
			if (vars == null) {
				xmlMsg += "<vars></vars>";
			} else {
				xmlMsg += "<vars>";

				for (final String varName : vars.keySet ()) {
					xmlMsg += getXmlRoomVariable (varName, vars
							.get (varName));
				}

				xmlMsg += "</vars>";
			}

			xmlMsg += "</room>";

			send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "createRoom",
					roomId, xmlMsg);
		} catch (final ClassCastException ex) {
			debugMessage (
					"Room creation parameter is not with the required type: "
							+ ex.getMessage (), ex, Level.SEVERE);
		}
	}

	private void debugMessage (final String message,
			final Exception ex, final Level level) {
		if (debug) {
			logger.log (level, message, ex);

			final SFSObject param = new SFSObject ();
			param.put ("message", message);
			final SFSEvent evt = new SFSEvent (SFSEvent.onDebugMessage,
					param);
			dispatchEvent (evt);
		}
	}

	private void debugMessage (final String message, final Level level) {
		if (debug) {
			logger.log (level, message);

			final SFSObject param = new SFSObject ();
			param.put ("message", message);
			final SFSEvent evt = new SFSEvent (SFSEvent.onDebugMessage,
					param);
			dispatchEvent (evt);
		}
	}

	/**
	 * Close the current connection to SmartFoxServer.
	 * 
	 * <p>
	 * The following example shows how to disconnect from SmartFoxServer.
	 * 
	 * <pre>
	 * smartFox.disconnect ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #connect
	 * @see SFSEvent#onConnectionLost
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void disconnect () {
		connected = false;

		if (!isHttpMode) {
			socketConnection.close ();
		} else {
			httpConnection.close ();
		}
	}

	private void dispatchConnectionError () {
		final SFSObject params = new SFSObject ();
		params.put ("success", false);
		params.put ("error", "I/O Error");

		final SFSEvent sfse = new SFSEvent (SFSEvent.onConnection,
				params);
		dispatchEvent (sfse);
	}

	private void errorTrace (final String msg) {
		logger.log (Level.WARNING, "Internal error: " + msg);
	}

	/**
	 * Get the currently active {@link Room} object.
	 * 
	 * <p>
	 * SmartFoxServer allows users to join two or more rooms at the same time
	 * (multi-room join). If this feature is used, then this method is useless
	 * and the application should track the various room id(s) manually, for
	 * example by keeping them in an array.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to retrieve the current room object.
	 * 
	 * <pre>
	 * Room room = smartFox.getActiveRoom ();
	 * System.out.println (&quot;Current room is: &quot; + room.getName ());
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @return the {@link Room} object of the currently active room; if the user
	 *         joined more than one room, the last joined room is returned.
	 * 
	 * @see #activeRoomId
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public Room getActiveRoom () {
		if (!checkRoomList ())
			return null;

		return roomList.get (activeRoomId);
	}

	/**
	 * Get the list of rooms in the current zone.
	 * <p>
	 * Unlike the {@link #getRoomList} method, this method returns the list of
	 * {@link Room} objects already stored on the client, so no request is sent
	 * to the server.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to retrieve the room list.
	 * 
	 * <pre>
	 * Map&amp;ltInteger, Room&amp;gt rooms = smartFox.getAllRooms();
	 * for(Integer id : rooms.keySet())
	 * {
	 *     System.out.println(&quot;Room: &quot; + rooms.get(id).getName());
	 * }
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @return The list of rooms available in the current zone.
	 * 
	 * @see #getRoomList
	 * @see Room
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public Map <Integer, Room> getAllRooms () {
		return roomList;
	}

	public long getBenchStartTime () {
		return benchStartTime;
	}

	/**
	 * Get a buddy from the buddy list, using the user id as key.
	 * 
	 * <p>
	 * The following example shows how to retrieve a buddy from the buddy list.
	 * 
	 * <pre>
	 * Buddy buddy = smartFox.getBuddyById(25);
	 * System.out.println(&quot;Buddy id: &quot; + buddy.getId());
	 * System.out.println(&quot;Buddy name: &quot; + buddy.getName());
	 * System.out.println(&quot;Is buddy online? &quot; + (buddy.isOnline() ? &quot;Yes&quot; : &quot;No&quot;));
	 * System.out.println(&quot;Is buddy blocked? &quot; + (buddy.isBlocked() ? &quot;Yes&quot; : &quot;No&quot;));
	 * System.out.println(&quot;Buddy Variables:&quot;));
	 * // Trace all Buddy Variables
	 * for(String key : buddy.getVariables().keySet())
	 * {
	 *     System.out.println(&quot;\t&quot; + key + &quot; --&gt; &quot; + buddy.getVariables().get(key));
	 * }
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param id the user id of the buddy.
	 * 
	 * @return The buddy object.
	 * 
	 * @see #buddyList
	 * @see #getBuddyByName
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public Buddy getBuddyById (final int id) {
		for (final Buddy buddy : buddyList) {
			if (buddy.getId () == id)
				return buddy;
		}
		return null;
	}

	/**
	 * Get a buddy from the buddy list, using the buddy's username as key.
	 * 
	 * <p>
	 * The following example shows how to retrieve a buddy from the buddy list.
	 * 
	 * <pre>
	 * Buddy buddy = smartFox.getBuddyByName(&quot;jack&quot;);
	 * System.out.println(&quot;Buddy id: &quot; + buddy.getId());
	 * System.out.println(&quot;Buddy name: &quot; + buddy.getName());
	 * System.out.println(&quot;Is buddy online? &quot; + (buddy.isOnline() ? &quot;Yes&quot; : &quot;No&quot;));
	 * System.out.println(&quot;Is buddy blocked? &quot; + (buddy.isBlocked() ? &quot;Yes&quot; : &quot;No&quot;));
	 * System.out.println(&quot;Buddy Variables:&quot;));
	 * // Trace all Buddy Variables
	 * for(String key : buddy.getVariables().keySet())
	 * {
	 *     System.out.println(&quot;\t&quot; + key + &quot; --&gt; &quot; + buddy.getVariables().get(key));
	 * }
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param buddyName the username of the buddy.
	 * 
	 * @return The buddy object.
	 * 
	 * @see #buddyList
	 * @see #getBuddyById
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public Buddy getBuddyByName (final String buddyName) {
		for (final Buddy buddy : buddyList) {
			if (buddy.getName ().equals (buddyName))
				return buddy;
		}
		return null;
	}

	/**
	 * Request the room id(s) of the room(s) where a buddy is currently located
	 * into.
	 * 
	 * <p>
	 * The following example shows how to join the same room of a buddy.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onBuddyRoom,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				smartFox.joinRoom ( ((int []) evt.getParams ().get (
	 * 						&quot;idList&quot;)) [0]);
	 * 			}
	 * 		});
	 * Buddy buddy = smartFox.getBuddyByName (&quot;jack&quot;);
	 * smartFox.getBuddyRoom (buddy);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param buddy a buddy object taken from the {@link #buddyList}.
	 * 
	 * @see #buddyList
	 * @see SFSEvent#onBuddyRoom
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void getBuddyRoom (final Buddy buddy) {
		// If buddy is active...
		if (buddy.getId () != -1) {
			send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "roomB", -1,
					"<b id='" + buddy.getId () + "' />");
		}
	}

	/**
	 * Get the current connection mode.
	 * 
	 * <p>
	 * The following example shows how to check the current connection mode.
	 * 
	 * <pre>
	 * smartFox.addEventListener(SFSEvent.onConnection, new ISFSEventHandler()
	 *   {
	 *       public void handleEvent(SFSEvent evt)
	 *       {
	 *           System.out.println(&quot;Connection mode: &quot; + smartFox.getConnectionMode());
	 *       }
	 *   });
	 * smartFox.connect(&quot;127.0.0.1&quot;, 9339)
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @return The current connection mode, expressed by one of the following
	 *         constants: {@link #CONNECTION_MODE_DISCONNECTED} (disconnected),
	 *         {@link #CONNECTION_MODE_SOCKET} (socket mode),
	 *         {@link #CONNECTION_MODE_HTTP} (http mode).
	 * 
	 * @see #CONNECTION_MODE_DISCONNECTED
	 * @see #CONNECTION_MODE_SOCKET
	 * @see #CONNECTION_MODE_HTTP
	 * @see #connect
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public String getConnectionMode () {
		String mode = SmartFoxClient.CONNECTION_MODE_DISCONNECTED;

		if (isConnected ()) {
			if (isHttpMode) {
				mode = SmartFoxClient.CONNECTION_MODE_HTTP;
			} else {
				mode = SmartFoxClient.CONNECTION_MODE_SOCKET;
			}
		}

		return mode;
	}

	/**
	 * Returns the minimum interval between two polling requests when connecting
	 * to SmartFoxServer via BlueBox module.
	 * 
	 * @return The minimum interval between two polling requests when connecting
	 *         to SmartFoxServer via BlueBox module.
	 * 
	 * @see #smartConnect
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public int getHttpPollSpeed () {
		return _httpPollSpeed;
	}

	/**
	 * Returns the {@code Logger} used to log the SmatFoxCLient debug messages.
	 * 
	 * @return the {@code Logger} used to log the SmatFoxCLient debug messages.
	 * 
	 * @see #setDebug(boolean)
	 */
	public Logger getLogger () {
		return logger;
	}

	/**
	 * Retrieve a random string key from the server.
	 * <p>
	 * This key is also referred in the SmartFoxServer documentation as the
	 * "secret key". It's a unique key, valid for the current session only. It
	 * can be used to create a secure login system.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to handle the request a random key to the
	 * server.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onRandomKey,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				System.out.println (&quot;Random key received from server: &quot;
	 * 						+ evt.getParams ().getString (&quot;key&quot;));
	 * 			}
	 * 		});
	 * smartFox.getRandomKey ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see SFSEvent#onRandomKey
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public void getRandomKey () {
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "rndK", -1, "");
	}

	/**
	 * Returns the character used as separator for the String (raw) protocol.
	 * 
	 * @return The character used as separator for the String (raw) protocol.
	 * 
	 * @see #XTMSG_TYPE_STR
	 * @see #setRawProtocolSeparator
	 * @see #sendXtMessage
	 * 
	 * @since SmartFoxServer Pro v1.5.5
	 */
	public char getRawProtocolSeparator () {
		return SmartFoxClient.MSG_STR;
	}

	/**
	 * Get a {@link Room} object, using its id as key.
	 * 
	 * <p>
	 * The following example shows how to retrieve a room from its id.
	 * 
	 * <pre>
	 * Room roomObj = smartFox.getRoom (15);
	 * System.out.println (&quot;Room name: &quot; + roomObj.getName ()
	 * 		+ &quot;, max users: &quot; + roomObj.getMaxUsers ());
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param roomId the id of the room.
	 * 
	 * @return The {@link Room} object.
	 * 
	 * @see #getRoomByName
	 * @see #getAllRooms
	 * @see #getRoomList
	 * @see Room
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public Room getRoom (final int roomId) {
		if (!checkRoomList ())
			return null;

		return roomList.get (roomId);
	}

	/**
	 * Get a {@link Room} object, using its name as key.
	 * 
	 * <p>
	 * The following example shows how to retrieve a room from its name.
	 * 
	 * <pre>
	 * Room roomObj = smartFox.getRoom (&quot;The Entrance&quot;);
	 * System.out.println (&quot;Room name: &quot; + roomObj.getName ()
	 * 		+ &quot;, max users: &quot; + roomObj.getMaxUsers ());
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param roomName the name of the room.
	 * 
	 * @return The {@link Room} object.
	 * 
	 * @see #getRoom
	 * @see #getAllRooms
	 * @see #getRoomList
	 * @see Room
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public Room getRoomByName (final String roomName) {
		if (!checkRoomList ())
			return null;

		Room room = null;

		for (final int key : roomList.keySet ()) {
			final Room r = roomList.get (key);
			if (r.getName ().equals (roomName)) {
				room = r;
				break;
			}
		}

		return room;
	}

	/**
	 * Retrieve the updated list of rooms in the current zone.
	 * <p>
	 * Unlike the {@link #getAllRooms} method, this method sends a request to
	 * the server, which then sends back the complete list of rooms with all
	 * their properties and server-side variables (Room Variables).
	 * </p>
	 * 
	 * <p>
	 * If the default login mechanism provided by SmartFoxServer is used, then
	 * the updated list of rooms is received right after a successful login,
	 * without the need to call this method. Instead, if a custom login handler
	 * is implemented, the room list must be manually requested to the server
	 * using this method.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to retrieve the room list from the
	 * server.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onRoomListUpdate,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				// Dump the names of the available rooms in the current zone
	 * 				for (Integer rId : evt.getParams ().get (&quot;roomList&quot;)
	 * 						.keySet ())
	 * 					System.out.println (evt.getParams ().get (
	 * 							&quot;roomList&quot;).get (r).getName ());
	 * 			}
	 * 		});
	 * smartFox.getRoomList ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #getRoom
	 * @see #getRoomByName
	 * @see #getAllRooms
	 * @see SFSEvent#onRoomListUpdate
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void getRoomList () {
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "getRmList",
				activeRoomId, "");
	}

	/**
	 * Get the default upload path of the embedded webserver.
	 * 
	 * <p>
	 * The following example shows how to get the default upload path.
	 * 
	 * <pre>
	 * String path = smartFox.getUploadPath ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @return The http address of the default folder in which files are
	 *         uploaded.
	 * 
	 * @since SmartFoxServer Pro v1.5.0
	 */
	public String getUploadPath () {
		return "http://" + ipAddress + ":" + httpPort
				+ "/default/uploads/";
	}

	/**
	 * Get the SmartFoxServer Flash API version.
	 * 
	 * <p>
	 * The following example shows how to display the SmartFoxServer API
	 * version.
	 * 
	 * <pre>
	 * System.out.println (&quot;Current API version: &quot; + smartFox.getVersion ());
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @return The current version of the SmartFoxServer client API.
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public String getVersion () {
		return majVersion + "." + minVersion + "." + subVersion;
	}

	private String getXmlRoomVariable (final String vName,
			final RoomVariableRequest rVar) {
		// Get properties for this var
		String vValue = rVar.getValue ();
		final String vPrivate = rVar.isPrivate () ? "1" : "0";
		final String vPersistent = rVar.isPersistent () ? "1" : "0";

		String t = null;
		final String type = rVar.getType ();

		// Check type
		if (type.equals ("boolean")) {
			t = "b";
			vValue = vValue.equals ("true") ? "1" : "0";
		} else if (type.equals ("number")) {
			t = "n";
		} else if (type.equals ("string")) {
			t = "s";
		} else if (vValue == null) {
			t = "x";
			vValue = "";
		}

		if (t != null)
			return "<var n='" + vName + "' t='" + t + "' pr='"
					+ vPrivate + "' pe='" + vPersistent + "'><![CDATA["
					+ vValue + "]]></var>";
		return "";
	}

	private String getXmlUserVariable (
			final Map <String, SFSVariable> uVars) {
		String xmlStr = "<vars>";
		String val;
		String t;
		String type;

		for (final String key : uVars.keySet ()) {
			val = uVars.get (key).getValue ();
			type = uVars.get (key).getType ();
			t = null;

			// Check types
			if (type.equals ("boolean")) {
				t = "b";
				val = val.equals ("true") ? "1" : "0";
			} else if (type.equals ("number")) {
				t = "n";
			} else if (type.equals ("string")) {
				t = "s";
			} else if (val == null) {
				t = "x";
				val = "";
			}

			if (t != null) {
				xmlStr += "<var n='" + key + "' t='" + t
						+ "'><![CDATA[" + val + "]]></var>";
			}
		}

		xmlStr += "</vars>";

		return xmlStr;
	}

	void handleDelayedPoll () {
		httpConnection.send (SmartFoxClient.HTTP_POLL_REQUEST);
	}

	public void handleEvent (final SFSEvent event) {
		if (event.getName ().equals (HttpEvent.onHttpConnect)) {
			onConnect (true);
			connected = true;
			httpConnection.send (SmartFoxClient.HTTP_POLL_REQUEST);
		} else if (event.getName ().equals (HttpEvent.onHttpClose)) {
			// Clear data
			initialize ();

			// Fire event
			final SFSEvent sfse = new SFSEvent (
					SFSEvent.onConnectionLost, new SFSObject ());
			dispatchEvent (sfse);
		} else if (event.getName ().equals (HttpEvent.onHttpData)) {
			final String data = event.getParams ().getString ("data");
			final String [] messages = data
					.split (SmartFoxClient.blueBoxSeparator);
			String message;

			if (messages [0].length () > 0) {
				for (int i = 0; i < messages.length - 1; i++ ) {
					message = messages [i];

					if (message.length () > 0) {
						handleMessage (message);
					}
				}

				poolTask.cancel ();
				poolTimer.purge ();
				poolTask = new TimerTask () {
					@Override
					public void run () {
						handleDelayedPoll ();
					}
				};
				poolTimer.schedule (poolTask, _httpPollSpeed);
			}
		} else if (event.getName ().equals (HttpEvent.onHttpError)) {
			if (!connected) {
				dispatchConnectionError ();
			}
		}
	}

	/*
	 * Analyze incoming message
	 */
	private void handleMessage (final String msg) {
		if (!msg.equals ("ok")) {
			debugMessage ("[ RECEIVED ]: " + msg + ", (len: "
					+ msg.length () + ")", Level.INFO);
		}

		final char type = msg.charAt (0);

		if (type == SmartFoxClient.MSG_XML) {
			xmlReceived (msg);
		} else if (type == SmartFoxClient.MSG_STR) {
			strReceived (msg);
		} else if (type == SmartFoxClient.MSG_JSON) {
			jsonReceived (msg);
		}
	}

	private void initialize () {
		initialize (false);
	}

	private void initialize (final boolean isLogOut) {
		// Clear local properties
		changingRoom = false;
		amIModerator = false;
		playerId = -1;
		activeRoomId = -1;
		myUserId = -1;
		myUserName = "";

		// Clear data structures
		roomList = new ConcurrentHashMap <Integer, Room> ();
		buddyList = new CopyOnWriteArrayList <Buddy> ();
		myBuddyVars = new ConcurrentHashMap <String, String> ();

		// Set connection status
		if (!isLogOut) {
			connected = false;
			isHttpMode = false;
		}
	}

	/**
	 * Returns {@code true} if the current user is connected to the server,
	 * {@code false} if the current user is not connected to the server.
	 * 
	 * <p>
	 * The following example shows how to check the connection status.
	 * 
	 * <pre>
	 * System.out.println (&quot;My connection status: &quot;
	 * 		+ (smartFox.isConnected () ? &quot;connected&quot; : &quot;not connected&quot;));
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @return {@code true} if the current user is connected to the server,
	 *         {@code false} if the current user is not connected to the server.
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public boolean isConnected () {
		return connected;
	}

	/**
	 * Return {@code true} if the debug messages are enabled, otherwise returns
	 * {@code false}.
	 * 
	 * @return {@code true} if the debug messages are enabled, otherwise {@code
	 *         false}.
	 */
	public boolean isDebug () {
		return debug;
	}

	/**
	 * Join a room.
	 * 
	 * @param newRoom the id of the room to join.
	 * 
	 *        <p>
	 *        In the following example the user requests to join a room with id
	 *        = 10.
	 * 
	 *        <pre>
	 * smartFox.joinRoom (10);
	 * </pre>
	 * 
	 *        </p>
	 * 
	 * @see SFSEvent#onJoinRoom
	 * @see SFSEvent#onJoinRoomError
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void joinRoom (final int newRoom) {
		joinRoom (newRoom, null, false, false, -1);
	}

	/**
	 * Join a room.
	 * 
	 * <p>
	 * In the following example the user requests to join a room with id = 12
	 * and password = "mypassword"; SmartFoxServer will disconnect him from the
	 * previous room.
	 * 
	 * <pre>
	 * smartFox.joinRoom (12, &quot;mypassword&quot;, false);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * In the following example the user requests to join the room with id = 15
	 * and passes {@code true} to the <i>dontLeave</i> flag; this will join the
	 * user in the new room while keeping him in the old room as well.
	 * 
	 * <pre>
	 * smartFox.joinRoom (15, &quot;&quot;, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param newRoom the id of the room to join.
	 * @param pword the room's password, if it's a private room or empty {@code
	 *        String} if it's not.
	 * @param dontLeave a boolean flag indicating if the current room must be
	 *        left after successfully joining the new room.
	 * 
	 * @see SFSEvent#onJoinRoom
	 * @see SFSEvent#onJoinRoomError
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void joinRoom (final int newRoom, final String pword,
			final boolean dontLeave) {
		joinRoom (newRoom, pword, false, dontLeave, -1);
	}

	/**
	 * Join a room.
	 * 
	 * <p>
	 * In the following example the user requests to join the game room with id
	 * = 15 as spectator and passes {@code true} to the <i>dontLeave</i> flag -
	 * this will join the user in the new room while keeping him in the old room
	 * as well.
	 * 
	 * <pre>
	 * smartFox.joinRoom (15, &quot;&quot;, true, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param newRoom the id of the room to join.
	 * @param pword the room's password, if it's a private room or empty {@code
	 *        String} if it's not.
	 * @param isSpectator a boolean flag indicating wheter you join as a
	 *        spectator or not.
	 * @param dontLeave a boolean flag indicating if the current room must be
	 *        left after successfully joining the new room.
	 * 
	 * @see SFSEvent#onJoinRoom
	 * @see SFSEvent#onJoinRoomError
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void joinRoom (final int newRoom, final String pword,
			final boolean isSpectator, final boolean dontLeave) {
		joinRoom (newRoom, pword, isSpectator, dontLeave, -1);
	}

	/**
	 * Join a room.
	 * 
	 * <p>
	 * In the following example the user requests to join the room with id = 15
	 * and leaves the romm with id = 25
	 * 
	 * <pre>
	 * smartFox.joinRoom (15, &quot;&quot;, false, false, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param newRoom the id of the room to join.
	 * @param pword the room's password, if it's a private room or empty {@code
	 *        String} if it's not.
	 * @param isSpectator a boolean flag indicating wheter you join as a
	 *        spectator or not.
	 * @param dontLeave a boolean flag indicating if the current room must be
	 *        left after successfully joining the new room.
	 * @param oldRoom the id of the room to leave.
	 * 
	 * @see SFSEvent#onJoinRoom
	 * @see SFSEvent#onJoinRoomError
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void joinRoom (final int newRoom, final String pword,
			final boolean isSpectator, final boolean dontLeave,
			final int oldRoom) {
		if (!checkRoomList ())
			return;

		final int isSpec = isSpectator ? 1 : 0;

		if (!changingRoom) {
			String leaveCurrRoom = dontLeave ? "0" : "1";

			// Set the room to leave
			int roomToLeave = oldRoom > -1 ? oldRoom : activeRoomId;

			// CHECK: activeRoomId == -1 no room has already been
			// entered
			if (activeRoomId == -1) {
				leaveCurrRoom = "0";
				roomToLeave = -1;
			}

			final String message = "<room id='" + newRoom + "' pwd='"
					+ pword + "' spec='" + isSpec + "' leave='"
					+ leaveCurrRoom + "' old='" + roomToLeave + "' />";

			send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "joinRoom",
					activeRoomId, message);
			changingRoom = true;
		}
	}

	/**
	 * Join a room.
	 * 
	 * <p>
	 * In the following example the user requests to join a room with name =
	 * "The Hall" and password = "mypassword"; SmartFoxServer will disconnect
	 * him from the previous room.
	 * 
	 * <pre>
	 * smartFox.joinRoom (&quot;The Hall&quot;, &quot;mypassword&quot;, false);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * In the following example the user requests to join the room with name =
	 * "The Hall" and passes {@code true} to the <i>dontLeave</i> flag; this
	 * will join the user in the new room while keeping him in the old room as
	 * well.
	 * 
	 * <pre>
	 * smartFox.joinRoom (&quot;The Hall&quot;, &quot;&quot;, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param newRoom the name of the room to join.
	 * @param pword the room's password, if it's a private room or empty {@code
	 *        String} if it's not.
	 * @param dontLeave a boolean flag indicating if the current room must be
	 *        left after successfully joining the new room.
	 * 
	 * @see SFSEvent#onJoinRoom
	 * @see SFSEvent#onJoinRoomError
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void joinRoom (final String newRoom, final String pword,
			final boolean dontLeave) {
		joinRoom (newRoom, pword, false, dontLeave, -1);
	}

	/**
	 * Join a room.
	 * 
	 * <p>
	 * In the following example the user requests to join the game room with
	 * name = "The Hall" as spectator and passes {@code true} to the
	 * <i>dontLeave</i> flag - this will join the user in the new room while
	 * keeping him in the old room as well.
	 * 
	 * <pre>
	 * smartFox.joinRoom (&quot;The Hall&quot;, &quot;&quot;, true, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param newRoom the name of the room to join.
	 * @param pword the room's password, if it's a private room or empty {@code
	 *        String} if it's not.
	 * @param isSpectator a boolean flag indicating wheter you join as a
	 *        spectator or not.
	 * @param dontLeave a boolean flag indicating if the current room must be
	 *        left after successfully joining the new room.
	 * 
	 * @see SFSEvent#onJoinRoom
	 * @see SFSEvent#onJoinRoomError
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void joinRoom (final String newRoom, final String pword,
			final boolean isSpectator, final boolean dontLeave) {
		joinRoom (newRoom, pword, isSpectator, dontLeave, -1);
	}

	/**
	 * Join a room.
	 * 
	 * <p>
	 * In the following example the user requests to join the room with name=
	 * "The Hall" and leaves the romm with id = 25
	 * 
	 * <pre>
	 * smartFox.joinRoom (&quot;The Hall&quot;, &quot;&quot;, false, false, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param newRoom the name of the room to join.
	 * @param pword the room's password, if it's a private room or empty {@code
	 *        String} if it's not.
	 * @param isSpectator a boolean flag indicating wheter you join as a
	 *        spectator or not.
	 * @param dontLeave a boolean flag indicating if the current room must be
	 *        left after successfully joining the new room.
	 * @param oldRoom the id of the room to leave.
	 * 
	 * @see SFSEvent#onJoinRoom
	 * @see SFSEvent#onJoinRoomError
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void joinRoom (final String newRoom, final String pword,
			final boolean isSpectator, final boolean dontLeave,
			final int oldRoom) {
		if (!checkRoomList ())
			return;

		int newRoomId = -1;
		final int isSpec = isSpectator ? 1 : 0;

		if (!changingRoom) {
			// Search the room
			for (final Integer roomId : roomList.keySet ()) {
				final Room r = roomList.get (roomId);
				if (r.getName ().equals (newRoom)) {
					newRoomId = r.getId ();
					break;
				}
			}

			if (newRoomId != -1) {
				String leaveCurrRoom = dontLeave ? "0" : "1";

				// Set the room to leave
				int roomToLeave = oldRoom > -1 ? oldRoom : activeRoomId;

				// CHECK: activeRoomId == -1 no room has already been
				// entered
				if (activeRoomId == -1) {
					leaveCurrRoom = "0";
					roomToLeave = -1;
				}

				final String message = "<room id='" + newRoomId
						+ "' pwd='" + pword + "' spec='" + isSpec
						+ "' leave='" + leaveCurrRoom + "' old='"
						+ roomToLeave + "' />";

				send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "joinRoom",
						activeRoomId, message);
				changingRoom = true;
			} else {
				debugMessage (
						"SmartFoxError: requested room to join does not exist!",
						Level.SEVERE);
			}
		}
	}

	private void jsonReceived (final String msg) {
		try {

			final JSONObject jso = new JSONObject (msg);

			final String handlerId = jso.getString ("t");
			final IMessageHandler handler = messageHandlers
					.get (handlerId);

			if (handler != null) {
				handler.handleMessage (jso.getJSONObject ("b"),
						SmartFoxClient.XTMSG_TYPE_JSON);
			}
		} catch (final JSONException ex) {
			debugMessage ("Error parsing JSON string: "
					+ ex.getMessage (), ex, Level.SEVERE);
		}
	}

	/**
	 * Disconnect the user from the given room.
	 * <p>
	 * This method should be used only when users are allowed to be present in
	 * more than one room at the same time (multi-room join feature).
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to make a user leave a room.
	 * 
	 * <pre>
	 * smartFox.leaveRoom (15);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param roomId the id of the room to leave.
	 * 
	 * @see #joinRoom
	 * @see SFSEvent#onRoomLeft
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void leaveRoom (final int roomId) {
		if (!checkRoomList () || !checkJoin ())
			return;

		final String xmlMsg = "<rm id='" + roomId + "' />";

		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "leaveRoom",
				roomId, xmlMsg);
	}

	/**
	 * Load the buddy list for the current user.
	 * 
	 * <p>
	 * The following example shows how to load the current user's buddy list.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onBuddyList,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				for (Buddy buddy : smartFox.buddyList) {
	 * 
	 * 					// Trace buddy properties
	 * 					System.out.println (&quot;Buddy id: &quot; + buddy.getId ());
	 * 					System.out.println (&quot;Buddy name: &quot;
	 * 							+ buddy.getName ());
	 * 					System.out.println (&quot;Is buddy online? &quot;
	 * 							+ (buddy.isOnline () ? &quot;Yes&quot; : &quot;No&quot;));
	 * 					System.out.println (&quot;Is buddy blocked? &quot;
	 * 							+ (buddy.isBlocked () ? &quot;Yes&quot; : &quot;No&quot;));
	 * 
	 * 					// Trace all Buddy Variables
	 * 					for (String key : buddy.getVariables ().keySet ()) {
	 * 						System.out.println (&quot;\t&quot; + key + &quot; --&gt; &quot;
	 * 								+ buddy.getVariables ().get (key));
	 * 					}
	 * 				}
	 * 			}
	 * 		});
	 * smartFox.loadBuddyList ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #buddyList
	 * @see SFSEvent#onBuddyList
	 * @see SFSEvent#onBuddyListError
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void loadBuddyList () {
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "loadB", -1, "");
	}

	/**
	 * Load a client configuration file.
	 * <p>
	 * The SmartFoxClient instance can be configured through an external xml
	 * configuration file loaded at run-time. On loading completion the
	 * {@link #connect} method is automatically called by the API. In case of
	 * loading error, the {@link SFSEvent#onConfigLoadFailure} event is fired.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: the SmartFoxClient configuration file (client-side) should
	 * not be confused with the SmartFoxServer configuration file (server-side).
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: The external xml configuration file has the following
	 * structure: ip and zone parameters are mandatory, all other parameters are
	 * optional.
	 * 
	 * <pre>
	 * &lt;SmartFoxClient&gt;
	 *     &lt;ip&gt;127.0.0.1&lt;/ip&gt;
	 *     &lt;port&gt;9339&lt;/port&gt;
	 *     &lt;zone&gt;simpleChat&lt;/zone&gt;
	 *     &lt;debug&gt;true&lt;/debug&gt;
	 *     &lt;blueBoxIpAddress&gt;127.0.0.1&lt;/blueBoxIpAddress&gt;
	 *     &lt;blueBoxPort&gt;9339&lt;/blueBoxPort&gt;
	 *     &lt;smartConnect&gt;true&lt;/smartConnect&gt;
	 *     &lt;httpPort&gt;8080&lt;/httpPort&gt;
	 *     &lt;httpPollSpeed&gt;750&lt;/httpPollSpeed&gt;
	 *     &lt;rawProtocolSeparator&gt;%&lt;/rawProtocolSeparator&gt;
	 * &lt;/SmartFoxClient&gt;
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to load an external configuration file.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onConfigLoadFailure,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				System.out.println (&quot;Failed loading config file: &quot;
	 * 						+ evt.getParams ().getString (&quot;message&quot;));
	 * 			}
	 * 		});
	 * smartFox.loadConfig (&quot;testEnvironmentConfig.xml&quot;);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param configFileName external xml configuration file name.
	 * 
	 * @see #ipAddress
	 * @see #port
	 * @see #defaultZone
	 * @see #debug
	 * @see #blueBoxIpAddress
	 * @see #blueBoxPort
	 * @see #smartConnect
	 * @see SFSEvent#onConfigLoadFailure
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public void loadConfig (final String configFileName) {
		loadConfig (configFileName, true);
	}

	/**
	 * Load a client configuration file.
	 * <p>
	 * The SmartFoxClient instance can be configured through an external xml
	 * configuration file loaded at run-time. If the <i>autoConnect</i>
	 * parameter is set to {@code true}, on loading completion the
	 * {@link #connect} method is automatically called by the API, otherwise the
	 * {@link SFSEvent#onConfigLoadSuccess} event is dispatched. In case of
	 * loading error, the {@link SFSEvent#onConfigLoadFailure} event is fired.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: the SmartFoxClient configuration file (client-side) should
	 * not be confused with the SmartFoxServer configuration file (server-side).
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: The external xml configuration file has the following
	 * structure: ip and zone parameters are mandatory, all other parameters are
	 * optional.
	 * 
	 * <pre>
	 * &lt;SmartFoxClient&gt;
	 *     &lt;ip&gt;127.0.0.1&lt;/ip&gt;
	 *     &lt;port&gt;9339&lt;/port&gt;
	 *     &lt;zone&gt;simpleChat&lt;/zone&gt;
	 *     &lt;debug&gt;true&lt;/debug&gt;
	 *     &lt;blueBoxIpAddress&gt;127.0.0.1&lt;/blueBoxIpAddress&gt;
	 *     &lt;blueBoxPort&gt;9339&lt;/blueBoxPort&gt;
	 *     &lt;smartConnect&gt;true&lt;/smartConnect&gt;
	 *     &lt;httpPort&gt;8080&lt;/httpPort&gt;
	 *     &lt;httpPollSpeed&gt;750&lt;/httpPollSpeed&gt;
	 *     &lt;rawProtocolSeparator&gt;%&lt;/rawProtocolSeparator&gt;
	 * &lt;/SmartFoxClient&gt;
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to load an external configuration file.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onConfigLoadSuccess,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				smartFox.connect (smartFox.ipAddress, smartFox.port);
	 * 			}
	 * 		});
	 * smartFox.addEventListener (SFSEvent.onConfigLoadFailure,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				System.out.println (&quot;Failed loading config file: &quot;
	 * 						+ evt.getParams ().getString (&quot;message&quot;));
	 * 			}
	 * 		});
	 * smartFox.loadConfig (&quot;testEnvironmentConfig.xml&quot;, false);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param configFileName external xml configuration file name.
	 * @param autoConnect a boolean flag indicating if the connection to
	 *        SmartFoxServer must be attempted upon configuration loading
	 *        completion.
	 * 
	 * @see #ipAddress
	 * @see #port
	 * @see #defaultZone
	 * @see #debug
	 * @see #blueBoxIpAddress
	 * @see #blueBoxPort
	 * @see #smartConnect
	 * @see #httpPort
	 * @see SFSEvent#onConfigLoadSuccess
	 * @see SFSEvent#onConfigLoadFailure
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public void loadConfig (final String configFileName,
			final boolean autoConnect) {
		try {
			final IXMLParser configParser = XMLParserFactory
					.createDefaultXMLParser ();
			final IXMLReader configReader = StdXMLReader
					.fileReader (configFileName);
			configParser.setReader (configReader);
			parseConfig ((IXMLElement) configParser.parse (),
					autoConnect);
		} catch (final XMLException ex) {
			onConfigLoadFailure ("Error parsing config XML file: ", ex);
		} catch (final FileNotFoundException ex) {
			onConfigLoadFailure ("Error reading config XML file: ", ex);
		} catch (final IOException ex) {
			onConfigLoadFailure ("Error reading config XML file: ", ex);
		} catch (final ClassNotFoundException ex) {
			onConfigLoadFailure ("Error parsing config XML file: ", ex);
		} catch (final InstantiationException ex) {
			onConfigLoadFailure ("Error parsing config XML file: ", ex);
		} catch (final IllegalAccessException ex) {
			onConfigLoadFailure ("Error parsing config XML file: ", ex);
		}
	}

	/**
	 * Load a client configuration file.
	 * <p>
	 * The SmartFoxClient instance can be configured through an external xml
	 * configuration file loaded at run-time. On loading completion the
	 * {@link #connect} method is automatically called by the API. In case of
	 * loading error, the {@link SFSEvent#onConfigLoadFailure} event is fired.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: the SmartFoxClient configuration file (client-side) should
	 * not be confused with the SmartFoxServer configuration file (server-side).
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: The external xml configuration file has the following
	 * structure: ip and zone parameters are mandatory, all other parameters are
	 * optional.
	 * 
	 * <pre>
	 * &lt;SmartFoxClient&gt;
	 *     &lt;ip&gt;127.0.0.1&lt;/ip&gt;
	 *     &lt;port&gt;9339&lt;/port&gt;
	 *     &lt;zone&gt;simpleChat&lt;/zone&gt;
	 *     &lt;debug&gt;true&lt;/debug&gt;
	 *     &lt;blueBoxIpAddress&gt;127.0.0.1&lt;/blueBoxIpAddress&gt;
	 *     &lt;blueBoxPort&gt;9339&lt;/blueBoxPort&gt;
	 *     &lt;smartConnect&gt;true&lt;/smartConnect&gt;
	 *     &lt;httpPort&gt;8080&lt;/httpPort&gt;
	 *     &lt;httpPollSpeed&gt;750&lt;/httpPollSpeed&gt;
	 *     &lt;rawProtocolSeparator&gt;%&lt;/rawProtocolSeparator&gt;
	 * &lt;/SmartFoxClient&gt;
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to load an external configuration file.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onConfigLoadFailure,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				System.out.println (&quot;Failed loading config file: &quot;
	 * 						+ evt.getParams ().getString (&quot;message&quot;));
	 * 			}
	 * 		});
	 * smartFox.loadConfig (this.getClass ().getClassLoader ().getResource (
	 * 		&quot;config.xml&quot;));
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param configFileUrl external xml configuration file URL.
	 * 
	 * @see #ipAddress
	 * @see #port
	 * @see #defaultZone
	 * @see #debug
	 * @see #blueBoxIpAddress
	 * @see #blueBoxPort
	 * @see #smartConnect
	 * @see #httpPort
	 * @see SFSEvent#onConfigLoadFailure
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public void loadConfig (final URL configFileUrl) {
		loadConfig (configFileUrl, true);
	}

	/**
	 * Load a client configuration file.
	 * <p>
	 * The SmartFoxClient instance can be configured through an external xml
	 * configuration file loaded at run-time. If the <i>autoConnect</i>
	 * parameter is set to {@code true}, on loading completion the
	 * {@link #connect} method is automatically called by the API, otherwise the
	 * {@link SFSEvent#onConfigLoadSuccess} event is dispatched. In case of
	 * loading error, the {@link SFSEvent#onConfigLoadFailure} event is fired.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: the SmartFoxClient configuration file (client-side) should
	 * not be confused with the SmartFoxServer configuration file (server-side).
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: The external xml configuration file has the following
	 * structure: ip and zone parameters are mandatory, all other parameters are
	 * optional.
	 * 
	 * <pre>
	 * &lt;SmartFoxClient&gt;
	 *     &lt;ip&gt;127.0.0.1&lt;/ip&gt;
	 *     &lt;port&gt;9339&lt;/port&gt;
	 *     &lt;zone&gt;simpleChat&lt;/zone&gt;
	 *     &lt;debug&gt;true&lt;/debug&gt;
	 *     &lt;blueBoxIpAddress&gt;127.0.0.1&lt;/blueBoxIpAddress&gt;
	 *     &lt;blueBoxPort&gt;9339&lt;/blueBoxPort&gt;
	 *     &lt;smartConnect&gt;true&lt;/smartConnect&gt;
	 *     &lt;httpPort&gt;8080&lt;/httpPort&gt;
	 *     &lt;httpPollSpeed&gt;750&lt;/httpPollSpeed&gt;
	 *     &lt;rawProtocolSeparator&gt;%&lt;/rawProtocolSeparator&gt;
	 * &lt;/SmartFoxClient&gt;
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to load an external configuration file.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onConfigLoadSuccess,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				smartFox.connect (smartFox.ipAddress, smartFox.port);
	 * 			}
	 * 		});
	 * smartFox.addEventListener (SFSEvent.onConfigLoadFailure,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				System.out.println (&quot;Failed loading config file: &quot;
	 * 						+ evt.getParams ().getString (&quot;message&quot;));
	 * 			}
	 * 		});
	 * smartFox.loadConfig (this.getClass ().getClassLoader ().getResource (
	 * 		&quot;config.xml&quot;), false);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param configFileUrl external xml configuration file URL.
	 * @param autoConnect a boolean flag indicating if the connection to
	 *        SmartFoxServer must be attempted upon configuration loading
	 *        completion.
	 * 
	 * @see #ipAddress
	 * @see #port
	 * @see #defaultZone
	 * @see #debug
	 * @see #blueBoxIpAddress
	 * @see #blueBoxPort
	 * @see #smartConnect
	 * @see #httpPort
	 * @see SFSEvent#onConfigLoadSuccess
	 * @see SFSEvent#onConfigLoadFailure
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public void loadConfig (final URL configFileUrl,
			final boolean autoConnect) {
		try {
			final URLConnection configConnection = configFileUrl
					.openConnection ();
			final BufferedReader in = new BufferedReader (
					new InputStreamReader (configConnection
							.getInputStream ()));
			final StringBuilder configStr = new StringBuilder ();
			final char [] cbuf = new char [1];
			while (in.read (cbuf) != -1) {
				configStr.append (cbuf);
			}
			if (configStr.length () > 0) {
				final IXMLParser configParser = XMLParserFactory
						.createDefaultXMLParser ();
				final IXMLReader configReader = StdXMLReader
						.stringReader (configStr.toString ());
				configParser.setReader (configReader);
				parseConfig ((IXMLElement) configParser.parse (),
						autoConnect);
			} else {
				onConfigLoadFailure ("Config file seems to be empty.");
			}
		} catch (final XMLException ex) {
			onConfigLoadFailure ("Error parsing config XML file: ", ex);
		} catch (final FileNotFoundException ex) {
			onConfigLoadFailure ("Error reading config XML file: ", ex);
		} catch (final IOException ex) {
			onConfigLoadFailure ("Error reading config XML file: ", ex);
		} catch (final ClassNotFoundException ex) {
			onConfigLoadFailure ("Error parsing config XML file: ", ex);
		} catch (final InstantiationException ex) {
			onConfigLoadFailure ("Error parsing config XML file: ", ex);
		} catch (final IllegalAccessException ex) {
			onConfigLoadFailure ("Error parsing config XML file: ", ex);
		}
	}

	/**
	 * Perform the default login procedure.
	 * <p>
	 * The standard SmartFoxServer login procedure accepts guest users. If a
	 * user logs in with an empty username, the server automatically creates a
	 * name for the client using the format <i>guest_n</i>, where <i>n</i> is a
	 * progressive number. Also, the provided username and password are checked
	 * against the moderators list (see the SmartFoxServer server-side
	 * configuration) and if a user matches it, he is set as a Moderator.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE 1</b>: duplicate names in the same zone are not allowed.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE 2</b>: for SmartFoxServer Basic, where a server-side custom login
	 * procedure can't be implemented due to the lack of <i>extensions</i>
	 * support, a custom client-side procedure can be used, for example to check
	 * usernames against a database using a php/asp page. In this case, this
	 * should be done BEFORE calling the <b>login</b> method. This way, once the
	 * client is validated, the stadard login procedure can be used.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to login into a zone.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onLogin, new ISFSEventHandler () {
	 * 	public void handleEvent (SFSEvent evt) {
	 * 		if (evt.getParams ().getBool (&quot;success&quot;)) {
	 * 			System.out.println (&quot;Successfully logged in as &quot;
	 * 					+ evt.getParams ().getString (&quot;name&quot;));
	 * 		} else {
	 * 			System.out
	 * 					.println (&quot;Zone login error; the following error occurred: &quot;
	 * 							+ evt.getParams ().getString (&quot;error&quot;));
	 * 		}
	 * 	}
	 * });
	 * smartFox.login (&quot;simpleChat&quot;, &quot;jack&quot;, &quot;&quot;);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param zone the name of the zone to log into.
	 * @param name the user name.
	 * @param pass the user password. Empty {@code String} if no password.
	 * 
	 * @see #logout
	 * @see SFSEvent#onLogin
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void login (final String zone, final String name,
			final String pass) {
		final String message = "<login z='" + zone
				+ "'><nick><![CDATA[" + name
				+ "]]></nick><pword><![CDATA[" + pass
				+ "]]></pword></login>";

		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "login", 0, message);
	}

	/**
	 * Log the user out of the current zone.
	 * <p>
	 * After a successful logout the user is still connected to the server, but
	 * he/she has to login again into a zone, in order to be able to interact
	 * with the server.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to logout from a zone.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onLogout, new ISFSEventHandler () {
	 * 	public void handleEvent (SFSEvent evt) {
	 * 		System.out.println (&quot;Logged out successfully&quot;);
	 * 	}
	 * });
	 * smartFox.logout ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #login
	 * @see SFSEvent#onLogout
	 * 
	 * @since SmartFoxServer Pro v1.5.5
	 */
	public void logout () {
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "logout", -1, "");
	}

	private String makeXmlHeader (final String header) {
		String xmlData = "<msg";
		xmlData += " t ='" + header + "'";
		xmlData += ">";

		return xmlData;
	}

	public void onClose () {
		// Clear data
		initialize ();

		// Fire event
		final SFSEvent sfse = new SFSEvent (SFSEvent.onConnectionLost,
				new SFSObject ());
		dispatchEvent (sfse);
	}

	private void onConfigLoadFailure (final String msg) {
		debugMessage (msg, Level.SEVERE);
		final SFSObject params = new SFSObject ();
		params.put ("message", msg);
		final SFSEvent sfsEvt = new SFSEvent (
				SFSEvent.onConfigLoadFailure, params);
		dispatchEvent (sfsEvt);
	}

	private void onConfigLoadFailure (final String msg,
			final Exception ex) {
		debugMessage (msg + ex.getMessage (), ex, Level.SEVERE);
		final SFSObject params = new SFSObject ();
		params.put ("message", ex.getMessage ());
		final SFSEvent sfsEvt = new SFSEvent (
				SFSEvent.onConfigLoadFailure, params);
		dispatchEvent (sfsEvt);
	}

	public void onConnect (final boolean success) {
		if (success) {
			final String xmlMsg = "<ver v='" + majVersion + minVersion
					+ subVersion + "' />";
			send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "verChk", 0,
					xmlMsg);
		} else {
			tryBlueBoxConnection ();
		}
	}

	public void onData (final String message) {
		handleMessage (message);
	}

	public void onError (final Exception e) {
		debugMessage ("Socket Error: " + e.getMessage (), e,
				Level.WARNING);
		tryBlueBoxConnection ();
	}

	private void parseConfig (final IXMLElement configXML,
			final boolean autoConnect) {
		final IXMLElement ipXML = configXML.getFirstChildNamed ("ip");
		if (ipXML != null) {
			ipAddress = ipXML.getContent ();
		} else {
			onConfigLoadFailure ("\"ip\" tag is missing.");
			return;
		}

		final IXMLElement portXML = configXML
				.getFirstChildNamed ("port");
		if (portXML != null) {
			port = Integer.parseInt (portXML.getContent ());
		} else {
			port = 9339;
		}

		final IXMLElement zoneXML = configXML
				.getFirstChildNamed ("zone");
		if (zoneXML != null) {
			defaultZone = zoneXML.getContent ();
		} else {
			onConfigLoadFailure ("\"zone\" tag is missing.");
			return;
		}

		final IXMLElement blueBoxXML = configXML
				.getFirstChildNamed ("blueBoxIpAddress");
		if (blueBoxXML != null) {
			blueBoxIpAddress = blueBoxXML.getContent ();
		}

		final IXMLElement blueBoxPortXML = configXML
				.getFirstChildNamed ("blueBoxPort");
		if (blueBoxPortXML != null) {
			blueBoxPort = Integer.parseInt (blueBoxPortXML
					.getContent ());
		}

		final IXMLElement debugXML = configXML
				.getFirstChildNamed ("debug");
		if (debugXML != null) {
			debug = debugXML.getContent ().toLowerCase ().equals (
					"true");
		}

		final IXMLElement smartConnectXML = configXML
				.getFirstChildNamed ("smartConnect");
		if (smartConnectXML != null) {
			smartConnect = smartConnectXML.getContent ().toLowerCase ()
					.equals ("true");
		}

		final IXMLElement httpPortXML = configXML
				.getFirstChildNamed ("httpPort");
		if (httpPortXML != null) {
			httpPort = Integer.parseInt (httpPortXML.getContent ());
		}

		final IXMLElement httpPollSpeedXML = configXML
				.getFirstChildNamed ("httpPort");
		if (httpPollSpeedXML != null) {
			_httpPollSpeed = Integer.parseInt (httpPollSpeedXML
					.getContent ());
		}

		final IXMLElement separatorXML = configXML
				.getFirstChildNamed ("rawProtocolSeparator");
		if (separatorXML != null) {
			setRawProtocolSeparator (separatorXML.getContent ().charAt (
					0));
		}

		if (autoConnect) {
			this.connect (ipAddress, port);
		} else {
			// Dispatch onConfigLoadSuccess event
			final SFSEvent sfsEvt = new SFSEvent (
					SFSEvent.onConfigLoadSuccess, new SFSObject ());
			dispatchEvent (sfsEvt);
		}
	}

	/**
	 * Remove a buddy from the buddy list.
	 * <p>
	 * Since SmartFoxServer Pro 1.6.0, the buddy list feature can be configured
	 * to use a <i>basic</i> or <i>advanced</i> security mode (see the
	 * SmartFoxServer server-side configuration file). Check the following usage
	 * notes for details on the behavior of the <b>removeBuddy</b> method in the
	 * two cases.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE:</b><br>
	 * Before you can add or remove any buddy from the list you must load the
	 * buddy-list from the server. Always make sure to call
	 * {@link #loadBuddyList} before interacting with the buddy-list.<br>
	 * <i>Basic security mode</i><br>
	 * When a buddy is removed, the buddy list is updated and the
	 * {@link SFSEvent#onBuddyList} event is fired.
	 * <hr>
	 * <i>Advanced security mode</i><br>
	 * In addition to the basic behavior, if the {@code <mutualRemoveBuddy>}
	 * server-side configuration parameter is set to {@code true}, when user A
	 * removes user B from the buddy list, he/she is automatically removed from
	 * user B's buddy list.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to remove a user from the buddy list.
	 * 
	 * <pre>
	 * String buddyName = &quot;jack&quot;;
	 * smartFox.removeBuddy (buddyName);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param buddyName the name of the user to be removed from the buddy list.
	 * 
	 * @see #buddyList
	 * @see #addBuddy
	 * @see SFSEvent#onBuddyList
	 * 
	 * @since SmartFoxServer Basic (except <i>advanced mode</i>) / Pro v1.x.x
	 */
	public void removeBuddy (final String buddyName) {
		boolean found = false;

		for (final Buddy buddy : buddyList) {
			if (buddy.getName ().equals (buddyName)) {
				buddyList.remove (buddy);
				found = true;
				break;
			}
		}

		if (found) {
			final String xmlMsg = "<n>" + buddyName + "</n>";

			send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "remB", -1,
					xmlMsg);

			// Fire event!
			final SFSObject params = new SFSObject ();
			params.put ("list", buddyList);

			final SFSEvent evt = new SFSEvent (SFSEvent.onBuddyList,
					params);
			dispatchEvent (evt);
		}
	}

	/**
	 * Send a roundtrip request to the server to test the connection' speed.
	 * <p>
	 * The roundtrip request sends a small packet to the server which
	 * immediately responds with another small packet, and causing the
	 * {@link SFSEvent#onRoundTripResponse} event to be fired. The time taken by
	 * the packet to travel forth and back is called "roundtrip time" and can be
	 * used to calculate the average network lag of the client. A good way to
	 * measure the network lag is to send continuos requests (every 3 or 5
	 * seconds) and then calculate the average roundtrip time on a fixed number
	 * of responses (i.e. the last 10 measurements).
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to check the average network lag time.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onRoundTripResponse,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				long lag = (Long) evt.getParams ().get (&quot;elapsed&quot;);
	 * 				System.out.println (&quot;Lag: &quot; + lag + &quot;milliseconds.&quot;);
	 * 			}
	 * 		});
	 * smartFox.roundTripBench ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see SFSEvent#onRoundTripResponse
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void roundTripBench () {
		benchStartTime = System.currentTimeMillis ();
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "roundTrip",
				activeRoomId, "");
	}

	private void send (final String header, final String action,
			final int fromRoom, final String message) {
		// Setup Msg Header
		String xmlMsg = makeXmlHeader (header);

		// Setup Body
		xmlMsg += "<body action='" + action + "' r='" + fromRoom + "'>"
				+ message + "</body>" + closeHeader ();

		debugMessage ("[Sending]: " + xmlMsg, Level.INFO);

		if (isHttpMode) {
			httpConnection.send (xmlMsg);
		} else {
			socketConnection.send (xmlMsg);
		}
	}

	/**
	 * Grant current user permission to be added to a buddy list.
	 * <p>
	 * If the SmartFoxServer Pro 1.6.0 <i>advanced</i> security mode is used
	 * (see the SmartFoxServer server-side configuration), when a user wants to
	 * add a buddy to his/her buddy list, a permission request is sent to the
	 * buddy. Once the {@link SFSEvent#onBuddyPermissionRequest} event is
	 * received, this method must be used by the buddy to grant or refuse
	 * permission. When the permission is granted, the requester's buddy list is
	 * updated.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to grant permission to be added to a
	 * buddy list once request is received.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onBuddyPermissionRequest,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				smartFox.sendBuddyPermissionResponse (true, evt
	 * 						.getParams ().getString (&quot;sender&quot;));
	 * 			}
	 * 		});
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param allowBuddy {@code true} to grant permission, {@code false} to
	 *        refuse to be added to the requester's buddy list.
	 * @param targetBuddy the username of the requester.
	 * 
	 * @see #addBuddy
	 * @see SFSEvent#onBuddyPermissionRequest
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public void sendBuddyPermissionResponse (final boolean allowBuddy,
			final String targetBuddy) {
		final String xmlMsg = "<n res='" + (allowBuddy ? "g" : "r")
				+ "'>" + targetBuddy + "</n>";
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "bPrm", -1, xmlMsg);
	}

	public void sendJson (final String jsMessage) {
		debugMessage ("[Sending - JSON]: " + jsMessage, Level.INFO);

		if (isHttpMode) {
			httpConnection.send (jsMessage);
		} else {
			socketConnection.send (jsMessage);
		}
	}

	/**
	 * Send a Moderator message to the current zone, the current room or a
	 * specific user in the current room.
	 * 
	 * <p>
	 * The following example shows how to send a Moderator message.
	 * 
	 * <pre>
	 * smartFox.sendModeratorMessage (&quot;Greetings from the Moderator&quot;,
	 * 		SmartFoxClient.MODMSG_TO_ROOM, smartFox.getActiveRoom ());
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * In order to send these kind of messages, the user must have Moderator's
	 * privileges, which are set by SmartFoxServer when the user logs in (see
	 * the {@link #login} method).
	 * </p>
	 * 
	 * @param message the text of the message.
	 * @param type the type of message. The following constants can be passed:
	 *        {@link #MODMSG_TO_USER}, {@link #MODMSG_TO_ROOM} and
	 *        {@link #MODMSG_TO_ZONE}, to send the message to a user, to the
	 *        current room or to the entire current zone respectively.
	 * @param id the id of the recipient room or user (ignored if the message is
	 *        sent to the zone).
	 * 
	 * @see #login
	 * @see #MODMSG_TO_USER
	 * @see #MODMSG_TO_ROOM
	 * @see #MODMSG_TO_ZONE
	 * @see SFSEvent#onModeratorMessage
	 * 
	 * @since SmartFoxServer Pro v1.4.5
	 */
	public void sendModeratorMessage (final String message,
			final String type, final int id) {
		if (!checkRoomList () || !checkJoin ())
			return;

		final String xmlMsg = "<txt t='" + type + "' id='" + id
				+ "'><![CDATA[" + Entities.encodeEntities (message)
				+ "]]></txt>";
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "modMsg",
				activeRoomId, xmlMsg);
	}

	/**
	 * Send an object to the other users in the current room.
	 * <p>
	 * This method can be used to send complex/nested data structures to
	 * clients, like a game move or a game status change.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send a simple object to the other
	 * users.
	 * 
	 * <pre>
	 * SFSObject move = new SFSObject ();
	 * move.putNumber (&quot;x&quot;, 150);
	 * move.putNumber (&quot;y&quot;, 250);
	 * move.putNumber (&quot;speed&quot;, 8);
	 * smartFox.sendObject (move);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param obj the object to be sent. Supported classes are: Boolean, Double,
	 *        String, SFSObject.
	 * 
	 * @see #sendObjectToGroup
	 * @see SFSEvent#onObjectReceived
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void sendObject (final SFSObject obj) {
		sendObject (obj, activeRoomId);
	}

	/**
	 * Send an object to the other users in the current room.
	 * <p>
	 * This method can be used to send complex/nested data structures to
	 * clients, like a game move or a game status change.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send a simple object to the other
	 * users.
	 * 
	 * <pre>
	 * SFSObject move = new SFSObject ();
	 * move.putNumber (&quot;x&quot;, 150);
	 * move.putNumber (&quot;y&quot;, 250);
	 * move.putNumber (&quot;speed&quot;, 8);
	 * smartFox.sendObject (move, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param obj the object to be sent. Supported classes are: Boolean, Double,
	 *        String, SFSObject.
	 * @param roomId the id of the target room, in case of multi-room join.
	 * 
	 * @see #sendObjectToGroup
	 * @see SFSEvent#onObjectReceived
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void sendObject (final SFSObject obj, final int roomId) {
		if (!checkRoomList () || !checkJoin ())
			return;

		final String xmlData = "<![CDATA["
				+ SFSObjectSerializer.serialize (obj) + "]]>";

		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "asObj", roomId,
				xmlData);
	}

	/**
	 * Send an Actionscript object to a group of users in the room.
	 * <p>
	 * See {@link #sendObject} for more info.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send a simple object with primitive
	 * data to two users.
	 * 
	 * <pre>
	 * SFSObject move = new SFSObject ();
	 * move.putNumber (&quot;x&quot;, 150);
	 * move.putNumber (&quot;y&quot;, 250);
	 * move.putNumber (&quot;speed&quot;, 8);
	 * smartFox.sendObjectToGroup (move, new int [] { 11, 12 });
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param obj the object to be sent. Supported classes are: Boolean, Double,
	 *        String, SFSObject.
	 * @param userList an array containing the id(s) of the recipients.
	 * 
	 * @see #sendObject
	 * @see SFSEvent#onObjectReceived
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void sendObjectToGroup (final SFSObject obj,
			final int [] userList) {
		sendObjectToGroup (obj, userList, activeRoomId);
	}

	/**
	 * Send an Actionscript object to a group of users in the room.
	 * <p>
	 * See {@link #sendObject} for more info.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send a simple object with primitive
	 * data to two users.
	 * 
	 * <pre>
	 * SFSObject move = new SFSObject ();
	 * params.put (&quot;type&quot;, &quot;bullet&quot;);
	 * move.putNumber (&quot;x&quot;, 150);
	 * move.putNumber (&quot;y&quot;, 250);
	 * move.putNumber (&quot;speed&quot;, 8);
	 * smartFox.sendObjectToGroup (move, new int [] { 11, 12 }, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param obj the object to be sent. Supported classes are: Boolean, Double,
	 *        String, SFSObject.
	 * @param userList
	 * @param roomId the id of the target room, in case of multi-room join.
	 * 
	 * @see #sendObject
	 * @see SFSEvent#onObjectReceived
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void sendObjectToGroup (final SFSObject obj,
			final int [] userList, final int roomId) {
		if (!checkRoomList () || !checkJoin ())
			return;

		String strList = "";

		for (final int id : userList) {
			strList += id + ",";
		}

		// remove last comma
		strList = strList.substring (0, strList.length ());

		obj.put ("_$$_", strList);

		final String xmlMsg = "<![CDATA["
				+ SFSObjectSerializer.serialize (obj) + "]]>";

		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "asObjG", roomId,
				xmlMsg);
	}

	// -------------------------------------------------------
	// Private methods
	// -------------------------------------------------------

	/**
	 * Send a private message to a user.
	 * <p>
	 * The message is broadcasted to the recipient and the sender.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send and receive a private message.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onPrivateMessage,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				System.out.println (&quot;User &quot;
	 * 						+ ((User) evt.getParams ().getObj (&quot;sender&quot;))
	 * 								.getName ()
	 * 						+ &quot; sent the following private message: &quot;
	 * 						+ evt.getParams ().getString (&quot;message&quot;));
	 * 			}
	 * 		});
	 * smartFox.sendPrivateMessage (&quot;Hello world!&quot;);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param message the text of the private message.
	 * @param recipientId the id of the recipient user.
	 * 
	 * @see #sendPublicMessage
	 * @see SFSEvent#onPrivateMessage
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void sendPrivateMessage (final String message,
			final int recipientId) {
		sendPrivateMessage (message, recipientId, activeRoomId);
	}

	/**
	 * Send a private message to a user.
	 * <p>
	 * The message is broadcasted to the recipient and the sender.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send and receive a private message.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onPublicMessage,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				System.out.println (&quot;User &quot;
	 * 						+ ((User) evt.getParams ().getObj (&quot;sender&quot;))
	 * 								.getName ()
	 * 						+ &quot; sent the following private message: &quot;
	 * 						+ evt.getParams ().getString (&quot;message&quot;));
	 * 			}
	 * 		});
	 * smartFox.sendPrivateMessage (&quot;Hello world!&quot;, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param message the text of the private message.
	 * @param recipientId the id of the recipient user.
	 * @param roomId the id of the room from where the message is sent, in case
	 *        of multi-room join.
	 * 
	 * @see #sendPublicMessage
	 * @see SFSEvent#onPrivateMessage
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void sendPrivateMessage (final String message,
			final int recipientId, final int roomId) {
		if (!checkRoomList () || !checkJoin ())
			return;

		final String xmlMsg = "<txt rcp='" + recipientId
				+ "'><![CDATA[" + Entities.encodeEntities (message)
				+ "]]></txt>";
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "prvMsg", roomId,
				xmlMsg);
	}

	/**
	 * Send a public message.
	 * <p>
	 * The message is broadcasted to all users in the current room, including
	 * the sender.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send and receive a public message.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onPublicMessage,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				System.out.println (&quot;User &quot;
	 * 						+ ((User) evt.getParams ().getObj (&quot;sender&quot;))
	 * 								.getName () + &quot; said: &quot;
	 * 						+ evt.getParams ().getString (&quot;message&quot;));
	 * 			}
	 * 		});
	 * smartFox.sendPublicMessage (&quot;Hello world!&quot;);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param message the text of the public message.
	 * 
	 * @see #sendPrivateMessage
	 * @see SFSEvent#onPublicMessage
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void sendPublicMessage (final String message) {
		sendPublicMessage (message, activeRoomId);
	}

	/**
	 * Send a public message.
	 * <p>
	 * The message is broadcasted to all users in the current room, including
	 * the sender.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send and receive a public message.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onPublicMessage,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				System.out.println (&quot;User &quot;
	 * 						+ ((User) evt.getParams ().getObj (&quot;sender&quot;))
	 * 								.getName () + &quot; said: &quot;
	 * 						+ evt.getParams ().getString (&quot;message&quot;));
	 * 			}
	 * 		});
	 * smartFox.sendPublicMessage (&quot;Hello world!&quot;, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param message the text of the public message.
	 * @param roomId the id of the target room, in case of multi-room join.
	 * 
	 * @see #sendPrivateMessage
	 * @see SFSEvent#onPublicMessage
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void sendPublicMessage (final String message,
			final int roomId) {
		if (!checkRoomList () || !checkJoin ())
			return;

		final String xmlMsg = "<txt><![CDATA["
				+ Entities.encodeEntities (message) + "]]></txt>";
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "pubMsg", roomId,
				xmlMsg);
	}

	public void sendString (final String strMessage) {
		debugMessage ("[Sending - STR]: " + strMessage, Level.INFO);

		if (isHttpMode) {
			httpConnection.send (strMessage);
		} else {
			socketConnection.send (strMessage);
		}
	}

	/**
	 * Send a request to a server side extension using JSON protocol.
	 * 
	 * <p>
	 * <b>NOTE</b>: the use JSON instead of XML is highly recommended, as it can
	 * save a lot of bandwidth.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to notify a multiplayer game server-side
	 * extension that a game action occurred.
	 * 
	 * <pre>
	 * // A bullet is being fired
	 * try
	 * {
	 *     JSONObject params = new JSONObject(&quot;{ &quot;type&quot; : &quot;bullet&quot;, &quot;posx&quot; : 100, &quot;posy&quot; : 200, &quot;speed&quot; : 10, &quot;angle&quot; : 45 }&quot;);
	 *     // Invoke &quot;fire&quot; command on the extension called &quot;gameExt&quot;, using JSON protocol
	 *     smartFox.sendXtMessage(&quot;gameExt&quot;, &quot;fire&quot;, params);
	 * }
	 * catch(JSONException ex)
	 * {
	 *     ex.printStackTrace();
	 * }
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param xtName the name of the extension (see also the {@link #createRoom}
	 *        method).
	 * @param cmd the name of the action/command to execute in the extension.
	 * @param paramObj an object containing the data to be passed to the
	 *        extension (set to empty object if no data is required).
	 * 
	 * @see #XTMSG_TYPE_JSON
	 * @see SFSEvent#onExtensionResponse
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public void sendXtMessage (final String xtName, final String cmd,
			final JSONObject paramObj) {
		sendXtMessage (xtName, cmd, paramObj, activeRoomId);
	}

	/**
	 * Send a request to a server side extension using JSON protocol.
	 * 
	 * <p>
	 * <b>NOTE</b>: the use JSON instead of XML is highly recommended, as it can
	 * save a lot of bandwidth.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to notify a multiplayer game server-side
	 * extension that a game action occurred.
	 * 
	 * <pre>
	 * // A bullet is being fired
	 * try
	 * {
	 *     JSONObject params = new JSONObject(&quot;{ &quot;type&quot; : &quot;bullet&quot;, &quot;posx&quot; : 100, &quot;posy&quot; : 200, &quot;speed&quot; : 10, &quot;angle&quot; : 45 }&quot;);
	 *     // Invoke &quot;fire&quot; command on the extension called &quot;gameExt&quot;, using JSON protocol
	 *     smartFox.sendXtMessage(&quot;gameExt&quot;, &quot;fire&quot;, params);
	 * }
	 * catch(JSONException ex)
	 * {
	 *     ex.printStackTrace();
	 * }
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param xtName the name of the extension (see also the {@link #createRoom}
	 *        method).
	 * @param cmd the name of the action/command to execute in the extension.
	 * @param paramObj an object containing the data to be passed to the
	 *        extension (set to empty object if no data is required).
	 * @param roomId the id of the room where the request was originated, in
	 *        case of multi-room join.
	 * 
	 * @see #XTMSG_TYPE_JSON
	 * @see SFSEvent#onExtensionResponse
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public void sendXtMessage (final String xtName, final String cmd,
			final JSONObject paramObj, final int roomId) {
		if (!checkRoomList ())
			return;

		try {
			final JSONObject body = new JSONObject ();
			body.put ("x", xtName);
			body.put ("c", cmd);
			body.put ("r", roomId);
			body.put ("p", paramObj);

			final JSONObject obj = new JSONObject ();
			obj.put ("t", "xt");
			obj.put ("b", body);

			final String msg = obj.toString ();
			sendJson (msg);
		} catch (final JSONException ex) {
			debugMessage ("Error sending JSON extension message: "
					+ ex.getMessage (), ex, Level.SEVERE);
		}
	}

	/**
	 * Send a request to a server side extension using XML protocol.
	 * 
	 * <p>
	 * <b>NOTE</b>: the use JSON instead of XML is highly recommended, as it can
	 * save a lot of bandwidth.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to notify a multiplayer game server-side
	 * extension that a game action occurred.
	 * 
	 * <pre>
	 * // A bullet is being fired
	 * SFSObject params = new SFSObject ();
	 * params.put (&quot;type&quot;, &quot;bullet&quot;);
	 * params.putNumber (&quot;posx&quot;, 100);
	 * params.putNumber (&quot;posy&quot;, 200);
	 * params.putNumber (&quot;speed&quot;, 10);
	 * params.putNumber (&quot;angle&quot;, 45);
	 * // Invoke &quot;fire&quot; command on the extension called &quot;gameExt&quot;, using XML protocol
	 * smartFox.sendXtMessage (&quot;gameExt&quot;, &quot;fire&quot;, params);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param xtName the name of the extension (see also the {@link #createRoom}
	 *        method).
	 * @param cmd the name of the action/command to execute in the extension.
	 * @param paramObj an object containing the data to be passed to the
	 *        extension (set to empty object if no data is required). Supported
	 *        classes are: Boolean, Double, String, SFSObject.
	 * 
	 * @see #XTMSG_TYPE_XML
	 * @see SFSEvent#onExtensionResponse
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public void sendXtMessage (final String xtName, final String cmd,
			final SFSObject paramObj) {
		sendXtMessage (xtName, cmd, paramObj, activeRoomId);
	}

	/**
	 * Send a request to a server side extension using XML protocol.
	 * 
	 * <p>
	 * <b>NOTE</b>: the use JSON instead of XML is highly recommended, as it can
	 * save a lot of bandwidth.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to notify a multiplayer game server-side
	 * extension that a game action occurred.
	 * 
	 * <pre>
	 * // A bullet is being fired
	 * SFSObject params = new SFSObject ();
	 * params.put (&quot;type&quot;, &quot;bullet&quot;);
	 * params.putNumber (&quot;posx&quot;, 100);
	 * params.putNumber (&quot;posy&quot;, 200);
	 * params.putNumber (&quot;speed&quot;, 10);
	 * params.putNumber (&quot;angle&quot;, 45);
	 * // Invoke &quot;fire&quot; command on the extension called &quot;gameExt&quot;, using XML protocol
	 * smartFox.sendXtMessage (&quot;gameExt&quot;, &quot;fire&quot;, params, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param xtName the name of the extension (see also the {@link #createRoom}
	 *        method).
	 * @param cmd the name of the action/command to execute in the extension.
	 * @param paramObj an object containing the data to be passed to the
	 *        extension (set to empty object if no data is required). Supported
	 *        classes are: Boolean, Double, String, SFSObject.
	 * @param roomId the id of the room where the request was originated, in
	 *        case of multi-room join.
	 * 
	 * @see #XTMSG_TYPE_XML
	 * @see SFSEvent#onExtensionResponse
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public void sendXtMessage (final String xtName, final String cmd,
			final SFSObject paramObj, final int roomId) {
		if (!checkRoomList ())
			return;

		// Encapsulate message
		final SFSObject xtReq = new SFSObject ();
		xtReq.put ("name", xtName);
		xtReq.put ("cmd", cmd);
		xtReq.put ("param", paramObj);
		final String xmlmsg = "<![CDATA["
				+ SFSObjectSerializer.serialize (xtReq) + "]]>";

		send (SmartFoxClient.MESSAGE_HEADER_EXTENSION, "xtReq", roomId,
				xmlmsg);
	}

	/**
	 * Send a request to a server side extension using String protocol.
	 * 
	 * <p>
	 * <b>NOTE</b>: The String-based protocol can be very useful for realtime
	 * applications/games where reducing the amount of data is the highest
	 * priority.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to notify a multiplayer game server-side
	 * extension that a game action occurred.
	 * 
	 * <pre>
	 * // A bullet is being fired
	 * 
	 * // The array contains the type, x and y position, speed and the angle.
	 * String [] params = { &quot;bullet&quot;, &quot;100&quot;, &quot;200&quot;, &quot;10&quot;, &quot;45&quot; };
	 * 
	 * // Invoke &quot;fire&quot; command on the extension called &quot;gameExt&quot;, using String protocol
	 * smartFox.sendXtMessage (&quot;gameExt&quot;, &quot;fire&quot;, params);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param xtName the name of the extension (see also the {@link #createRoom}
	 *        method).
	 * @param cmd the name of the action/command to execute in the extension.
	 * @param params an array that contains the data to be passed to the
	 *        extension (set to empty array if no data is required).
	 * 
	 * @see #XTMSG_TYPE_STR
	 * @see SFSEvent#onExtensionResponse
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public void sendXtMessage (final String xtName, final String cmd,
			final String [] params) {
		sendXtMessage (xtName, cmd, params, activeRoomId);
	}

	/**
	 * Send a request to a server side extension using String protocol.
	 * 
	 * <p>
	 * <b>NOTE</b>: The String-based protocol can be very useful for realtime
	 * applications/games where reducing the amount of data is the highest
	 * priority.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to notify a multiplayer game server-side
	 * extension that a game action occurred.
	 * 
	 * <pre>
	 * // A bullet is being fired
	 * 
	 * // The array contains the x and y position, speed and the angle.
	 * String [] params = { &quot;bullet&quot;, &quot;100&quot;, &quot;200&quot;, &quot;10&quot;, &quot;45&quot; };
	 * 
	 * // Invoke &quot;fire&quot; command on the extension called &quot;gameExt&quot;, using String protocol
	 * smartFox.sendXtMessage (&quot;gameExt&quot;, &quot;fire&quot;, params, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param xtName the name of the extension (see also the {@link #createRoom}
	 *        method).
	 * @param cmd the name of the action/command to execute in the extension.
	 * @param params an array that contains the data to be passed to the
	 *        extension (set to empty array if no data is required).
	 * @param roomId the id of the room where the request was originated, in
	 *        case of multi-room join.
	 * 
	 * @see #XTMSG_TYPE_STR
	 * @see SFSEvent#onExtensionResponse
	 * 
	 * @since SmartFoxServer Pro v1.x.x
	 */
	public void sendXtMessage (final String xtName, final String cmd,
			final String [] params, final int roomId) {
		if (!checkRoomList ())
			return;

		String hdr = SmartFoxClient.MSG_STR + "xt"
				+ SmartFoxClient.MSG_STR + xtName
				+ SmartFoxClient.MSG_STR + cmd + SmartFoxClient.MSG_STR
				+ roomId + SmartFoxClient.MSG_STR;

		for (final String param : params) {
			hdr += param + SmartFoxClient.MSG_STR;
		}

		sendString (hdr);
	}

	/**
	 * Block or unblock a user in the buddy list.
	 * <p>
	 * When a buddy is blocked, SmartFoxServer does not deliver private messages
	 * from/to that user.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to block a user from the buddy list.
	 * 
	 * <pre>
	 * smartFox.setBuddyBlockStatus (&quot;jack&quot;, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param buddyName the name of the buddy to be blocked or unblocked.
	 * @param status {@code true} to block the buddy, {@code false} to unblock
	 *        the buddy.
	 * 
	 * @see #buddyList
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public void setBuddyBlockStatus (final String buddyName,
			final boolean status) {
		final Buddy b = getBuddyByName (buddyName);

		if (b != null) {
			if (b.isBlocked () != status) {
				b.setBlocked (status);

				final String xmlMsg = "<n x='" + (status ? "1" : "0")
						+ "'>" + buddyName + "</n>";
				send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "setB", -1,
						xmlMsg);

				// Fire internal update
				final SFSObject params = new SFSObject ();
				params.put ("buddy", b);

				final SFSEvent evt = new SFSEvent (
						SFSEvent.onBuddyListUpdate, params);
				dispatchEvent (evt);
			}
		}
	}

	/**
	 * Set the current user's Buddy Variables.
	 * <p>
	 * This method allows to set a number of properties of the current user as
	 * buddy of other users; in other words these variables will be received by
	 * the other users who have the current user as a buddy.
	 * </p>
	 * 
	 * <p>
	 * Buddy Variables are the best way to share user's informations with all
	 * the other users having him/her in their buddy list.: for example the
	 * nickname, the current audio track the user is listening to, etc. The most
	 * typical usage is to set a variable containing the current user status,
	 * like "available", "occupied", "away", "invisible", etc.
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: before the release of SmartFoxServer Pro v1.6.0, Buddy
	 * Variables could not be stored, and existed during the user session only.
	 * SmartFoxServer Pro v1.6.0 introduced the ability to persist (store) all
	 * Buddy Variables and the possibility to save "offline Buddy Variables"
	 * (see the following usage notes).
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: let's assume that three users (A, B and C) use an
	 * "istant messenger"-like application, and user A is part of the buddy
	 * lists of users B and C. <br>
	 * If user A sets his own variables (using the {@link #setBuddyVariables}
	 * method), the {@link #myBuddyVars} list on his client gets populated and a
	 * {@link SFSEvent#onBuddyListUpdate} event is dispatched to users B and C.
	 * User B and C can then read those variables in their own buddy lists by
	 * means of the <b>variables</b> property on the buddy object (which can be
	 * retrieved from the {@link #buddyList} list by means of the
	 * {@link #getBuddyById} or {@link #getBuddyByName} methods).
	 * 
	 * <hr />
	 * 
	 * If the buddy list's <i>advanced security mode</i> is used (see the
	 * SmartFoxServer server-side configuration), Buddy Variables persistence is
	 * enabled: in this way regular variables are saved when a user goes offline
	 * and they are restored (and dispatched to the other users) when their
	 * owner comes back online. Also, setting the {@code
	 * <offLineBuddyVariables>} parameter to {@code true}, offline variables can
	 * be used: this kind of Buddy Variables is loaded regardless the buddy is
	 * online or not, providing further informations for each entry in the buddy
	 * list. A typical usage for offline variables is to define a buddy image or
	 * additional informations such as country, email, rank, etc. To creare an
	 * offline Buddy Variable, the "$" character must be placed before the
	 * variable name.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to set three variables containing the
	 * user's status, the current audio track the user listening to and the
	 * user's rank. The last one is an offline variable.
	 * 
	 * <pre>
	 * Map &lt;String, String&gt; bVars = new HashMap ();
	 * bVars.put (&quot;status&quot;, &quot;away&quot;);
	 * bVars.put (&quot;track&quot;, &quot;One Of These Days&quot;);
	 * bVars.put (&quot;$rank&quot;, &quot;guru&quot;);
	 * smartFox.setBuddyVariables (bVars);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param varList an associative array, where the key is the name of the
	 *        variable and the value is the variable's value. Buddy Variables
	 *        should all be strings. If you need to use other data types you
	 *        should apply the appropriate type casts.
	 * 
	 * @see #myBuddyVars
	 * @see SFSEvent#onBuddyListUpdate
	 * 
	 * @since SmartFoxServer Basic (except <i>advanced mode</i>) / Pro v1.x.x
	 */
	public void setBuddyVariables (final Map <String, String> varList) {
		// Encapsulate Variables
		String xmlMsg = "<vars>";

		// Reference to the user setting the variables
		for (final java.util.Map.Entry <String, String> var : varList
				.entrySet ()) {

			// if variable is new or updated send it and update locally
			if (!myBuddyVars.get (var.getKey ()).equals (
					var.getValue ())) {
				myBuddyVars.put (var.getKey (), var.getValue ());
				xmlMsg += "<var n='" + var.getKey () + "'><![CDATA["
						+ var.getValue () + "]]></var>";
			}
		}

		xmlMsg += "</vars>";

		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "setBvars", -1,
				xmlMsg);
	}

	/**
	 * Sets the {@code connected} flag.
	 * 
	 * @param connected {@code true} if the current user is connected to the
	 *        server, {@code false} if the current user is not connected to the
	 *        server.
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void setConnected (final boolean connected) {
		this.connected = connected;
	}

	/**
	 * Toggle the client-side debugging informations.
	 * <p>
	 * When turned on, the developer is able to inspect all server messages that
	 * are sent and received by the client in the log output. This allows a
	 * better debugging of the interaction with the server during application
	 * developement.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to turn on SmartFoxServer API debugging.
	 * 
	 * <pre>
	 * SmartFoxClient smartFox = new SmartFoxClient (false);
	 * if (runningLocally) {
	 * 	smartFox.setDebug (true);
	 * 	ip = &quot;127.0.0.1&quot;;
	 * 	port = 9339;
	 * } else {
	 * 	ip = &quot;100.101.102.103&quot;;
	 * 	port = 9333;
	 * }
	 * smartFox.connect (ip, port);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param debug
	 * 
	 * @see SFSEvent#onDebugMessage
	 */
	public void setDebug (final boolean debug) {
		this.debug = debug;
	}

	/**
	 * Sets the minimum interval between two polling requests when connecting to
	 * SmartFoxServer via BlueBox module.
	 * <p>
	 * The default value is 750 milliseconds. Accepted values are between 0 and
	 * 10000 milliseconds (10 seconds).
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to set the polling speed.
	 * 
	 * <pre>
	 * smartFox.setHttpPollSpeed (200);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: <i>Which is the optimal value for polling speed?</i> <br>
	 * A value between 750-1000 ms is very good for chats, turn-based games and
	 * similar kind of applications. It adds minimum lag to the client
	 * responsiveness and it keeps the server CPU usage low. Lower values
	 * (200-500 ms) can be used where a faster responsiveness is necessary. For
	 * super fast real-time games values between 50 ms and 100 ms can be tried.
	 * With settings &lt; 200 ms the CPU usage will grow significantly as the
	 * http connection and packet wrapping/unwrapping is more expensive than
	 * using a persistent connection. Using values below 50 ms is not
	 * recommended.
	 * </p>
	 * 
	 * @param pollSpeed The minimum interval between two polling requests
	 * 
	 * @see #smartConnect
	 * 
	 * @since SmartFoxServer Pro v1.6.0
	 */
	public void setHttpPollSpeed (final int pollSpeed) {
		// Acceptable values: 0 <= sp <= 10sec
		if (pollSpeed >= 0 && pollSpeed <= 10000) {
			_httpPollSpeed = pollSpeed;
		}
	}

	/**
	 * Sete the character used as separator for the String (raw) protocol.
	 * <p>
	 * The default value is <b>%</b> (percentage character).
	 * </p>
	 * 
	 * <p>
	 * <b>NOTE</b>: this separator must match the one set in the SmartFoxServer
	 * server-side configuration file through the {@code <RawProtocolSeparator>}
	 * parameter.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to set the raw protocol separator.
	 * 
	 * <pre>
	 * smartFox.setRawProtocolSeparator ('|');
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param value the character used as separator for the String (raw)
	 *        protocol.
	 * 
	 * @see #XTMSG_TYPE_STR
	 * @see #sendXtMessage
	 * 
	 * @since SmartFoxServer Pro v1.5.5
	 */
	public void setRawProtocolSeparator (final char value) {
		if (value != '<' && value != '{') {
			SmartFoxClient.MSG_STR = value;
		}
	}

	/**
	 * Set one or more Room Variables.
	 * <p>
	 * Room Variables are a useful feature to share data across the clients,
	 * keeping it in a centralized place on the server. When a user
	 * sets/updates/deletes one or more Room Variables, all the other users in
	 * the same room are notified. Allowed data types for Room Variables are
	 * Numbers, Strings and Booleans; in order save bandwidth, Arrays and
	 * Objects are not supported. Nevertheless, an array of values can be
	 * simulated, for example, by using an index in front of the name of each
	 * variable (check one of the following examples). <br>
	 * If a Room Variable is set to {@code null}, it is deleted from the server.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save a persistent Room Variable called
	 * "score". This variable won't be destroyed when its creator leaves the
	 * room.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest variable = new RoomVariableRequest (&quot;2500&quot;,
	 * 		SFSVariable.TYPE_NUMBER, false, true);
	 * rVars.put (&quot;score&quot;, variable);
	 * smartFox.setRoomVariables (rVars);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save a persistent Room Variable called
	 * "score". This variable won't be destroyed when its creator leaves the
	 * room.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest variable = new RoomVariableRequest (&quot;2500&quot;,
	 * 		SFSVariable.TYPE_NUMBER, false, true);
	 * rVars.put (&quot;score&quot;, variable);
	 * smartFox.setRoomVariables (rVars);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save two Room Variables at once. The
	 * one called "bestTime" is private and no other user except its owner can
	 * modify it.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest bestTime = new RoomVariableRequest (&quot;2500&quot;,
	 * 		SFSVariable.TYPE_NUMBER, true);
	 * rVars.put (&quot;bestTime&quot;, bestTime);
	 * RoomVariableRequest bestLap = new RoomVariableRequest (&quot;120&quot;,
	 * 		SFSVariable.TYPE_NUMBER);
	 * rVars.put (&quot;bestLap&quot;, bestLap);
	 * smartFox.setRoomVariables (rVars);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to delete a Room Variable called
	 * "bestTime" by setting its value to {@code null}.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest variable = new RoomVariableRequest (null,
	 * 		SFSVariable.TYPE_NULL);
	 * rVars.put (&quot;bestTime&quot;, variable);
	 * smartFox.setRoomVariables (rVars);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send an array-like set of data without
	 * consuming too much bandwidth.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * String [] names = { &quot;john&quot;, &quot;dave&quot;, &quot;sam&quot; };
	 * for (int i = 0; i &lt; names.lenght; i++ ) {
	 * 	RoomVariableRequest variable = new RoomVariableRequest (names [i],
	 * 			SFSVariable.TYPE_STRING);
	 * 	rVars.put (&quot;name&quot; + i, variable);
	 * }
	 * smartFox.setRoomVariables (rVars);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to handle the data sent in the previous
	 * example when the {@link SFSEvent#onRoomVariablesUpdate} event is
	 * received.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onRoomVariablesUpdate,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				Set &lt;String&gt; changedVars = (Set &lt;String&gt;) evt
	 * 						.getParams ().get (&quot;changedVars&quot;);
	 * 				Room room = (Room) evt.getParams ().get (&quot;room&quot;);
	 * 				for (String v : changedVars) {
	 * 					System.out
	 * 							.println (v
	 * 									+ &quot; room variable was updated; new value is: &quot;
	 * 									+ room.getVariable (v).getValue ());
	 * 				}
	 * 			}
	 * 		});
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param vars {@code Map} containing the room variables name as key and the
	 *        room variables as value.
	 * 
	 * @see Room#getVariable
	 * @see Room#getVariables
	 * @see SFSEvent#onRoomVariablesUpdate
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void setRoomVariables (
			final Map <String, RoomVariableRequest> vars) {
		setRoomVariables (vars, activeRoomId, true);
	}

	/**
	 * Set one or more Room Variables.
	 * <p>
	 * Room Variables are a useful feature to share data across the clients,
	 * keeping it in a centralized place on the server. When a user
	 * sets/updates/deletes one or more Room Variables, all the other users in
	 * the same room are notified. Allowed data types for Room Variables are
	 * Numbers, Strings and Booleans; in order save bandwidth, Arrays and
	 * Objects are not supported. Nevertheless, an array of values can be
	 * simulated, for example, by using an index in front of the name of each
	 * variable (check one of the following examples). <br>
	 * If a Room Variable is set to {@code null}, it is deleted from the server.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save a persistent Room Variable called
	 * "score". This variable won't be destroyed when its creator leaves the
	 * room.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest variable = new RoomVariableRequest (&quot;2500&quot;,
	 * 		SFSVariable.TYPE_NUMBER, false, true);
	 * rVars.put (&quot;score&quot;, variable);
	 * smartFox.setRoomVariables (rVars, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save a persistent Room Variable called
	 * "score". This variable won't be destroyed when its creator leaves the
	 * room.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest variable = new RoomVariableRequest (&quot;2500&quot;,
	 * 		SFSVariable.TYPE_NUMBER, false, true);
	 * rVars.put (&quot;score&quot;, variable);
	 * smartFox.setRoomVariables (rVars, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save two Room Variables at once. The
	 * one called "bestTime" is private and no other user except its owner can
	 * modify it.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest bestTime = new RoomVariableRequest (&quot;2500&quot;,
	 * 		SFSVariable.TYPE_NUMBER, true);
	 * rVars.put (&quot;bestTime&quot;, bestTime);
	 * RoomVariableRequest bestLap = new RoomVariableRequest (&quot;120&quot;,
	 * 		SFSVariable.TYPE_NUMBER);
	 * rVars.put (&quot;bestLap&quot;, bestLap);
	 * smartFox.setRoomVariables (rVars, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to delete a Room Variable called
	 * "bestTime" by setting its value to {@code null}.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest variable = new RoomVariableRequest (null,
	 * 		SFSVariable.TYPE_NULL);
	 * rVars.put (&quot;bestTime&quot;, variable);
	 * smartFox.setRoomVariables (rVars, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send an array-like set of data without
	 * consuming too much bandwidth.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * String [] names = { &quot;john&quot;, &quot;dave&quot;, &quot;sam&quot; };
	 * for (int i = 0; i &lt; names.lenght; i++ ) {
	 * 	RoomVariableRequest variable = new RoomVariableRequest (names [i],
	 * 			SFSVariable.TYPE_STRING);
	 * 	rVars.put (&quot;name&quot; + i, variable);
	 * }
	 * smartFox.setRoomVariables (rVars, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to handle the data sent in the previous
	 * example when the {@link SFSEvent#onRoomVariablesUpdate} event is
	 * received.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onRoomVariablesUpdate,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				Set &lt;String&gt; changedVars = (Set &lt;String&gt;) evt
	 * 						.getParams ().get (&quot;changedVars&quot;);
	 * 				Room room = (Room) evt.getParams ().get (&quot;room&quot;);
	 * 				for (String v : changedVars) {
	 * 					System.out
	 * 							.println (v
	 * 									+ &quot; room variable was updated; new value is: &quot;
	 * 									+ room.getVariable (v).getValue ());
	 * 				}
	 * 			}
	 * 		});
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param vars {@code Map} containing the room variables name as key and the
	 *        room variables as value.
	 * @param roomId the id of the room where the variables should be set, in
	 *        case of molti-room join.
	 * 
	 * @see Room#getVariable
	 * @see Room#getVariables
	 * @see SFSEvent#onRoomVariablesUpdate
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void setRoomVariables (
			final Map <String, RoomVariableRequest> vars,
			final int roomId) {
		setRoomVariables (vars, roomId, true);
	}

	/**
	 * Set one or more Room Variables.
	 * <p>
	 * Room Variables are a useful feature to share data across the clients,
	 * keeping it in a centralized place on the server. When a user
	 * sets/updates/deletes one or more Room Variables, all the other users in
	 * the same room are notified. Allowed data types for Room Variables are
	 * Numbers, Strings and Booleans; in order save bandwidth, Arrays and
	 * Objects are not supported. Nevertheless, an array of values can be
	 * simulated, for example, by using an index in front of the name of each
	 * variable (check one of the following examples). <br>
	 * If a Room Variable is set to {@code null}, it is deleted from the server.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save a persistent Room Variable called
	 * "score". This variable won't be destroyed when its creator leaves the
	 * room.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest variable = new RoomVariableRequest (&quot;2500&quot;,
	 * 		SFSVariable.TYPE_NUMBER, false, true);
	 * rVars.put (&quot;score&quot;, variable);
	 * smartFox.setRoomVariables (rVars, activeRoomId, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save a persistent Room Variable called
	 * "score". This variable won't be destroyed when its creator leaves the
	 * room.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest variable = new RoomVariableRequest (&quot;2500&quot;,
	 * 		SFSVariable.TYPE_NUMBER, false, true);
	 * rVars.put (&quot;score&quot;, variable);
	 * smartFox.setRoomVariables (rVars, activeRoomId, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save two Room Variables at once. The
	 * one called "bestTime" is private and no other user except its owner can
	 * modify it.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest bestTime = new RoomVariableRequest (&quot;2500&quot;,
	 * 		SFSVariable.TYPE_NUMBER, true);
	 * rVars.put (&quot;bestTime&quot;, bestTime);
	 * RoomVariableRequest bestLap = new RoomVariableRequest (&quot;120&quot;,
	 * 		SFSVariable.TYPE_NUMBER);
	 * rVars.put (&quot;bestLap&quot;, bestLap);
	 * smartFox.setRoomVariables (rVars, activeRoomId, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to delete a Room Variable called
	 * "bestTime" by setting its value to {@code null}.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest variable = new RoomVariableRequest (null,
	 * 		SFSVariable.TYPE_NULL);
	 * rVars.put (&quot;bestTime&quot;, variable);
	 * smartFox.setRoomVariables (rVars, activeRoomId, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to send an array-like set of data without
	 * consuming too much bandwidth.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * String [] names = { &quot;john&quot;, &quot;dave&quot;, &quot;sam&quot; };
	 * for (int i = 0; i &lt; names.lenght; i++ ) {
	 * 	RoomVariableRequest variable = new RoomVariableRequest (names [i],
	 * 			SFSVariable.TYPE_STRING);
	 * 	rVars.put (&quot;name&quot; + i, variable);
	 * }
	 * smartFox.setRoomVariables (rVars, activeRoomId, true);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to handle the data sent in the previous
	 * example when the {@link SFSEvent#onRoomVariablesUpdate} event is
	 * received.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onRoomVariablesUpdate,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				Set &lt;String&gt; changedVars = (Set &lt;String&gt;) evt
	 * 						.getParams ().get (&quot;changedVars&quot;);
	 * 				Room room = (Room) evt.getParams ().get (&quot;room&quot;);
	 * 				for (String v : changedVars) {
	 * 					System.out
	 * 							.println (v
	 * 									+ &quot; room variable was updated; new value is: &quot;
	 * 									+ room.getVariable (v).getValue ());
	 * 				}
	 * 			}
	 * 		});
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to update a Room Variable without
	 * affecting the variable's ownership. By default, when a user updates a
	 * Room Variable, he becomes the "owner" of that variable. In some cases it
	 * could be needed to disable this behavoir by setting the
	 * <i>setOwnership</i> property to {@code false}.
	 * 
	 * <pre>
	 * Map &lt;String, RoomVariableRequest&gt; rVars = new HashMap &lt;String, RoomVariableRequest&gt; ();
	 * RoomVariableRequest shipPosX = new RoomVariableRequest (&quot;100&quot;,
	 * 		SFSVariable.TYPE_NUMBER);
	 * rVars.put (&quot;shipPosX&quot;, shipPosX);
	 * RoomVariableRequest shipPosY = new RoomVariableRequest (&quot;200&quot;,
	 * 		SFSVariable.TYPE_NUMBER);
	 * rVars.put (&quot;shipPosY&quot;, shipPosY);
	 * smartFox.setRoomVariables (rVars, activeRoomId, false);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param vars {@code Map} containing the room variables name as key and the
	 *        room variables as value.
	 * @param roomId the id of the room where the variables should be set, in
	 *        case of molti-room join.
	 * @param setOwnership {@code false} to prevent the Room Variable change
	 *        ownership when its value is modified by another user.
	 * 
	 * @see Room#getVariable
	 * @see Room#getVariables
	 * @see SFSEvent#onRoomVariablesUpdate
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void setRoomVariables (
			final Map <String, RoomVariableRequest> vars,
			final int roomId, final boolean setOwnership) {
		if (!checkRoomList () || !checkJoin ())
			return;

		String xmlMsg;

		if (setOwnership) {
			xmlMsg = "<vars>";
		} else {
			xmlMsg = "<vars so='0'>";
		}

		for (final String varName : vars.keySet ()) {
			xmlMsg += getXmlRoomVariable (varName, vars.get (varName));
		}

		xmlMsg += "</vars>";

		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "setRvars", roomId,
				xmlMsg);
	}

	private void setupMessageHandlers () {
		sysHandler = new SysHandler (this);
		extHandler = new ExtHandler (this);

		addMessageHandler ("sys", sysHandler);
		addMessageHandler ("xt", extHandler);
	}

	// -------------------------------------------------------
	// Internal Http Event Handlers
	// -------------------------------------------------------

	/**
	 * Set on or more User Variables.
	 * <p>
	 * User Variables are a useful tool to store user data that has to be shared
	 * with other users. When a user sets/updates/deletes one or more User
	 * Variables, all the other users in the same room are notified. Allowed
	 * data types for User Variables are Numbers, Strings and Booleans; Arrays
	 * and Objects are not supported in order save bandwidth. If a User Variable
	 * is set to {@code null}, it is deleted from the server. Also, User
	 * Variables are destroyed when their owner logs out or gets disconnected.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save the user data (avatar name and
	 * position) in an avatar chat application.
	 * 
	 * <pre>
	 * Map &lt;String, SFSVariable&gt; uVars = new HashMap &lt;String, SFSVariable&gt; ();
	 * uVars.put (&quot;myAvatar&quot;, new SFSVariable (&quot;Homer&quot;,
	 * 		SFSVariable.TYPE_STRING));
	 * uVars.put (&quot;posx&quot;, new SFSVariable (&quot;100&quot;, SFSVariable.TYPE_NUMBER));
	 * uVars.put (&quot;posy&quot;, new SFSVariable (&quot;200&quot;, SFSVariable.TYPE_NUMBER));
	 * smartFox.setUserVariables (uVars);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param vars {@code Map} containing the user variables name as key and the
	 *        room variables as value.
	 * 
	 * @see User#getVariable
	 * @see User#getVariables
	 * @see SFSEvent#onUserVariablesUpdate
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void setUserVariables (final Map <String, SFSVariable> vars) {
		setUserVariables (vars, activeRoomId);
	}

	/**
	 * Set on or more User Variables.
	 * <p>
	 * User Variables are a useful tool to store user data that has to be shared
	 * with other users. When a user sets/updates/deletes one or more User
	 * Variables, all the other users in the same room are notified. Allowed
	 * data types for User Variables are Numbers, Strings and Booleans; Arrays
	 * and Objects are not supported in order save bandwidth. If a User Variable
	 * is set to {@code null}, it is deleted from the server. Also, User
	 * Variables are destroyed when their owner logs out or gets disconnected.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to save the user data (avatar name and
	 * position) in an avatar chat application.
	 * 
	 * <pre>
	 * Map &lt;String, SFSVariable&gt; uVars = new HashMap &lt;String, SFSVariable&gt; ();
	 * uVars.put (&quot;myAvatar&quot;, new SFSVariable (&quot;Homer&quot;,
	 * 		SFSVariable.TYPE_STRING));
	 * uVars.put (&quot;posx&quot;, new SFSVariable (&quot;100&quot;, SFSVariable.TYPE_NUMBER));
	 * uVars.put (&quot;posy&quot;, new SFSVariable (&quot;200&quot;, SFSVariable.TYPE_NUMBER));
	 * smartFox.setUserVariables (uVars, 25);
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param vars {@code Map} containing the user variables name as key and the
	 *        room variables as value.
	 * @param roomId the room id where the request was originated, in case of
	 *        molti-room join.
	 * 
	 * @see User#getVariable
	 * @see User#getVariables
	 * @see SFSEvent#onUserVariablesUpdate
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void setUserVariables (final Map <String, SFSVariable> vars,
			final int roomId) {
		if (!checkRoomList () || !checkJoin ())
			return;

		final Room currRoom = getActiveRoom ();
		final User user = currRoom.getUser (myUserId);

		// Update local client
		user.setVariables (vars);

		// Prepare and send message
		final String xmlMsg = getXmlUserVariable (vars);
		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "setUvars", roomId,
				xmlMsg);
	}

	// -------------------------------------------------------
	// Internal Socket Event Handlers
	// -------------------------------------------------------

	private void strReceived (final String msg) {
		// Got String response
		final String [] params = msg.substring (1).split (
				String.valueOf (SmartFoxClient.MSG_STR));

		final String handlerId = params [0];
		final IMessageHandler handler = messageHandlers.get (handlerId);

		if (handler != null) {
			final String [] spliceParams = new String [params.length - 1];
			System.arraycopy (params, 1, spliceParams, 0,
					spliceParams.length);
			handler.handleMessage (spliceParams,
					SmartFoxClient.XTMSG_TYPE_STR);
		}
	}

	/**
	 * Turn a player inside a game room into a spectator.
	 * <p>
	 * All players have their <b>player id</b> property set to a value > 0; when
	 * a spectator becomes a player, his playerId is set to -1.
	 * </p>
	 * <p>
	 * If the user joined more than one room use {@link #switchPlayer(int)}
	 * instead.
	 * </p>
	 * <p>
	 * The switch operation is successful only if at least one spectator slot is
	 * available in the room.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to turn a spectator into a player.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onPlayerSwitched,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				if (evt.getParams ().getBool (&quot;success&quot;)) {
	 * 					System.out
	 * 							.println (&quot;You have been turned into a player; your id is &quot;
	 * 									+ evt.getParams ().get (&quot;newId&quot;));
	 * 				} else {
	 * 					System.out
	 * 							.println (&quot;The attempt to switch from spectator to player failed&quot;);
	 * 				}
	 * 				smartFox.switchPlayer ();
	 * 			}
	 * 		});
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #switchPlayer(int)
	 * @see User#isSpectator()
	 * @see SFSEvent#onPlayerSwitched
	 * 
	 * @since SmartFoxServer Pro v1.6.6
	 */
	public void switchPlayer () {
		switchPlayer (activeRoomId);
	}

	/**
	 * Turn a player inside a game room into a spectator.
	 * <p>
	 * All players have their <b>player id</b> property set to a value > 0; when
	 * a spectator becomes a player, his playerId is set to -1.
	 * </p>
	 * <p>
	 * The switch operation is successful only if at least one spectator slot is
	 * available in the room.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to turn a spectator into a player.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onPlayerSwitched,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				if (evt.getParams ().getBool (&quot;success&quot;)) {
	 * 					System.out
	 * 							.println (&quot;You have been turned into a player; your id is &quot;
	 * 									+ evt.getParams ().get (&quot;newId&quot;));
	 * 				} else {
	 * 					System.out
	 * 							.println (&quot;The attempt to switch from spectator to player failed&quot;);
	 * 				}
	 * 				smartFox.switchPlayer (35);
	 * 			}
	 * 		});
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param roomId the id of the room where the player should be switched to
	 *        spectator.
	 * 
	 * @see User#isSpectator()
	 * @see SFSEvent#onPlayerSwitched
	 * 
	 * @since SmartFoxServer Pro v1.6.6
	 */
	public void switchPlayer (final int roomId) {
		if (!checkRoomList () || !checkJoin ())
			return;

		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "swPl", roomId, "");
	}

	/**
	 * Turn a spectator inside a game room into a player.
	 * <p>
	 * All spectators have their <b>player id</b> property set to -1; when a
	 * spectator becomes a player, his player id gets a number > 0, representing
	 * the player number. The player id values are assigned by the server, based
	 * on the order in which the players joined the room. If the user joined
	 * more than one room, the id of the room where the switch should occurr
	 * must be passed to this method. The switch operation is successful only if
	 * at least one player slot is available in the room.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to turn a spectator into a player.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onSpectatorSwitched,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				if (evt.getParams ().getBool (&quot;success&quot;)) {
	 * 					System.out
	 * 							.println (&quot;You have been turned into a player; your player id is &quot;
	 * 									+ evt.getParams ().get (&quot;newId&quot;));
	 * 				} else {
	 * 					System.out
	 * 							.println (&quot;The attempt to switch from spectator to player failed&quot;);
	 * 				}
	 * 			}
	 * 		});
	 * smartFox.switchSpectator ();
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see User#isSpectator
	 * @see SFSEvent#onSpectatorSwitched
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void switchSpectator () {
		switchSpectator (activeRoomId);
	}

	/**
	 * Turn a spectator inside a game room into a player.
	 * <p>
	 * All spectators have their <b>player id</b> property set to -1; when a
	 * spectator becomes a player, his player id gets a number > 0, representing
	 * the player number. The player id values are assigned by the server, based
	 * on the order in which the players joined the room. If the user joined
	 * more than one room, the id of the room where the switch should occurr
	 * must be passed to this method. The switch operation is successful only if
	 * at least one player slot is available in the room.
	 * </p>
	 * 
	 * <p>
	 * The following example shows how to turn a spectator into a player.
	 * 
	 * <pre>
	 * smartFox.addEventListener (SFSEvent.onSpectatorSwitched,
	 * 		new ISFSEventHandler () {
	 * 			public void handleEvent (SFSEvent evt) {
	 * 				if (evt.getParams ().getBool (&quot;success&quot;)) {
	 * 					System.out
	 * 							.println (&quot;You have been turned into a player; your player id is &quot;
	 * 									+ evt.getParams ().get (&quot;newId&quot;));
	 * 				} else {
	 * 					System.out
	 * 							.println (&quot;The attempt to switch from spectator to player failed&quot;);
	 * 				}
	 * 
	 * 				smartFox.switchSpectator (25);
	 * 			}
	 * 		});
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param roomId the id of the room where the spectator should be switched,
	 *        in case of molti-room join.
	 * 
	 * @see User#isSpectator
	 * @see SFSEvent#onSpectatorSwitched
	 * 
	 * @since SmartFoxServer Basic / Pro v1.x.x
	 */
	public void switchSpectator (final int roomId) {
		if (!checkRoomList () || !checkJoin ())
			return;

		send (SmartFoxClient.MESSAGE_HEADER_SYSTEM, "swSpec", roomId,
				"");
	}

	/*
	 * New in 1.5.4
	 */
	private void tryBlueBoxConnection () {
		if (!connected) {
			if (smartConnect) {
				debugMessage (
						"Socket connection failed. Trying BlueBox",
						Level.INFO);

				poolTimer = new Timer ();
				poolTask = new TimerTask () {
					@Override
					public void run () {
						handleDelayedPoll ();
					}
				};
				isHttpMode = true;
				final String __ip = blueBoxIpAddress != null ? blueBoxIpAddress
						: ipAddress;
				final int __port = blueBoxPort != 0 ? blueBoxPort
						: httpPort;

				httpConnection.connect (__ip, __port);
			} else {
				dispatchConnectionError ();
			}
		} else {
			debugMessage ("[WARN] Connection error.", Level.WARNING);
			dispatchConnectionError ();
		}
	}

	private void xmlReceived (final String msg) {
		try {
			// Got XML response
			final IXMLParser parser = XMLParserFactory
					.createDefaultXMLParser ();
			final IXMLReader reader = StdXMLReader.stringReader (msg);
			parser.setReader (reader);
			final IXMLElement xmlData = (IXMLElement) parser.parse ();

			final String handlerId = xmlData.getAttribute ("t", null);
			if (handlerId != null) {
				final IMessageHandler handler = messageHandlers
						.get (handlerId);

				if (handler != null) {
					handler.handleMessage (xmlData,
							SmartFoxClient.XTMSG_TYPE_XML);
				}
			}
		} catch (final XMLException ex) {
			debugMessage ("Error parsing XML string: "
					+ ex.getMessage (), ex, Level.SEVERE);
		} catch (final ClassNotFoundException ex) {
			debugMessage ("Error parsing XML string: "
					+ ex.getMessage (), ex, Level.SEVERE);
		} catch (final InstantiationException ex) {
			debugMessage ("Error parsing XML string: "
					+ ex.getMessage (), ex, Level.SEVERE);
		} catch (final IllegalAccessException ex) {
			debugMessage ("Error parsing XML string: "
					+ ex.getMessage (), ex, Level.SEVERE);
		}
	}
}
