var popups = new Array();
var hideTimeout;
var popupActive = false;

var users = new Object();
var chats = new Object();
var online = 0; // rename
var buddyonline = 0;

var connectedStatus = 0;
var reconnectTimeDefault = 4000;
var reconnectTime = reconnectTimeDefault;
var reconnectTimeout;
var tryReconnect = true;
var loadedVer = 2;
var loggedIn = true; // TODO not used
var pingInterval;
var pingPending = 0;
var lastPong = 0;
var url = 'index.php';

var readyFlash = false;
var readyDoc = false;
var readyOnload = false;
var readySound = false;

soundManager.debugMode = false;
soundManager.nullURL = '/ff/null.mp3';

var socket;

var status; // ?
var windowActive = true;
var msgPending = 0;
var reportFormActive = false;

/* const */ AV_SEEKING = 2;
/* const */ AV_ONLINE = 1;
/* const */ AV_INGAME = 3;
/* const */ AV_OFFLINE = 0;

var onlineAvail = new Array();
onlineAvail[AV_SEEKING] = 0;
onlineAvail[AV_ONLINE] = 0;
onlineAvail[AV_INGAME] = 0;
onlineAvail[AV_OFFLINE] = 0;

var outgoingChallenge = 0;

var validInstanceId = 0;

var prefsShow = false;
var prefList = new Object();
var defaultPrefOpts = new Array("On", "Off");

var availMsg = new Array();
availMsg[AV_SEEKING] = "Seeking Brawl";
availMsg[AV_ONLINE] = "Online";
availMsg[AV_INGAME] = "Brawling";
availMsg[AV_OFFLINE] = "Offline";

/* const */ CS_MYREPLY = 0;
/* const */ CS_WAITING = 1;
/* const */ CS_ACCEPTED = 2;
/* const */ CS_REJECTED = 3;
/* const */ CS_LEFT = 4;
/* const */ CS_CANCELED = 5;

var chalStatusMsg = new Array();
chalStatusMsg[CS_MYREPLY] = "Click here to accept";
chalStatusMsg[CS_WAITING] = "Waiting for response...";
chalStatusMsg[CS_ACCEPTED] = "Accepted";
chalStatusMsg[CS_REJECTED] = "Declined";
chalStatusMsg[CS_LEFT] = "Left";
chalStatusMsg[CS_CANCELED] = "Canceled";

/* const */ CSTR_SEND = 0;
/* const */ CSTR_ADD = 1;
/* const */ CSTR_NULL = 2;
var chalStr = new Array();

chalStr[CSTR_SEND] = "Send a challenge";
chalStr[CSTR_ADD] = "Add to challenge";
chalStr[CSTR_NULL] = "";

// browser only
window.onblur = function () {
	  status_activity("window blurred");
	windowActive = false;
}
// TODO: on focus, if chat window is active, set to not pending? (safari)
window.onfocus = function () {
	  status_activity("window focused");
	if (window.fluid)
	{
		msgPending = 0;
		window.fluid.dockBadge = "";
	}
	windowActive = true;
}

function updateOnlineAvail(avail, diff)
{
	// TODO remove printAvail when ready
	var printAvail = onlineAvail[avail] += diff;
	  status_activity("updating avail count");
	if (!printAvail)
	{
		printAvail = '0';
	}
	$('#userlist li.avail-'+avail+' div span.value').html('('+printAvail+')');
}

// TODO remove from production versions
function var_dump(data,addwhitespace,safety,level) {
   var rtrn = '';
   var dt,it,spaces = '';
   if(!level) {level = 1;}
   for(var i=0; i<level; i++) {
      spaces += '   ';
   }//end for i<level
   if(typeof(data) != 'object') {
      dt = data;
      if(typeof(data) == 'string') {
         if(addwhitespace == 'html') {
            dt = dt.replace(/&/g,'&amp;');
            dt = dt.replace(/>/g,'&gt;');
            dt = dt.replace(/</g,'&lt;');
         }//end if addwhitespace == html
         dt = dt.replace(/\"/g,'\"');
         dt = '"' + dt + '"';
      }//end if typeof == string
      if(typeof(data) == 'function' && addwhitespace) {
         dt = new String(dt).replace(/\n/g,"\n"+spaces);
         if(addwhitespace == 'html') {
            dt = dt.replace(/&/g,'&amp;');
            dt = dt.replace(/>/g,'&gt;');
            dt = dt.replace(/</g,'&lt;');
         }//end if addwhitespace == html
      }//end if typeof == function
      if(typeof(data) == 'undefined') {
         dt = 'undefined';
      }//end if typeof == undefined
      if(addwhitespace == 'html') {
         if(typeof(dt) != 'string') {
            dt = new String(dt);
         }//end typeof != string
         dt = dt.replace(/ /g,"&nbsp;").replace(/\n/g,"<br>");
      }//end if addwhitespace == html
      return dt;
   }//end if typeof != object && != array
   for (var x in data) {
      if(safety && (level > safety)) {
         dt = '*RECURSION*';
      } else {
         try {
            dt = var_dump(data[x],addwhitespace,safety,level+1);
         } catch (e) {continue;}
      }//end if-else level > safety
      it = var_dump(x,addwhitespace,safety,level+1);
      rtrn += it + ':' + dt + ',';
      if(addwhitespace) {
         rtrn += '\n'+spaces;
      }//end if addwhitespace
   }//end for...in
   if(addwhitespace) {
      rtrn = '{\n' + spaces + rtrn.substr(0,rtrn.length-(2+(level*3))) + '\n' + spaces.substr(0,spaces.length-3) + '}';
   } else {
      rtrn = '{' + rtrn.substr(0,rtrn.length-1) + '}';
   }//end if-else addwhitespace
   if(addwhitespace == 'html') {
      rtrn = rtrn.replace(/ /g,"&nbsp;").replace(/\n/g,"<br>");
   }//end if addwhitespace == html
   return rtrn;
}//end function var_dump

function XMLSocket(options){
	var instanceId, connector;
	var self = this;
	var options = options || {};
	self.connect = function(host, port){
		connector.connect(instanceId, host, port);
	}
	self.send = function(str){
		connector.send(instanceId, JSON.stringify(str));
		  status_activity("-> ["+instanceId+"]"+JSON.stringify(str));
	}
	self.close = function(host, port){
		connector.close(instanceId);
	}
    self.addEventListener = function(type, listener){
        if(!XMLSocket.eventListeners[instanceId]){
            XMLSocket.eventListeners[instanceId] = [];
        }
        var eventListenerId = connector.addJSEventListener(instanceId, type, "XMLSocket.dispatcher");
        XMLSocket.eventListeners[instanceId][eventListenerId] = listener;
        return eventListenerId; 
    }
    self.removeEventListener = function(eventListenerId){
    	connector.removeJSEventListener(eventListenerId);
		XMLSocket.eventListeners[instanceId][eventListenerId] = function(){}
    }
	self.XMLSocket = function(){
		if (!readyFlash)
		{
			  status_activity("WTF");
		}
		connector = document["JSXMLSocket"];
		validInstanceId = instanceId = connector.create();
		connector.addJSEventListener(instanceId, 'close', 'ff_disconnect');
		connector.addJSEventListener(instanceId, 'Event', 'handle');
		connector.addJSEventListener(instanceId, 'connect', 'ff_connect');
		connector.addJSEventListener(instanceId, 'data', 'ff_data');
		connector.addJSEventListener(instanceId, 'IOErrorEvent.IO_ERROR', 'handle');
		connector.addJSEventListener(instanceId, 'ProgressEvent.PROGRESS', 'handle');
		connector.addJSEventListener(instanceId, 'SecurityErrorEvent.SECURITY_ERROR', 'handle');
		  status_activity("socket instanceId: " +instanceId);
	}();
}
XMLSocket.eventListeners = [];
XMLSocket.dispatcher = function(instanceId, eventListenerId, event){
	XMLSocket.eventListeners[instanceId][eventListenerId](event);
}

function User(options)
{
	var self = this;
	var options = options || {};
	var instances = 1; // reference count
	
	var data = new Object();
	var isbuddy;
	var userid;
	
	var row; // for display purposes
	var rowActive = false;
	var expanded = false;
	var noResize = false; // used to prevent bubbling of links/onclick
	
	var status; // TODO not used?

	//var chats = new Array();

	
	this.getData = function(k)
	{
		  status_activity('data: '+var_dump(data));
		return data;
	}
	
	this.getInstances = function()
	{
		return instances;
	}
	
	this.incInstances = function()
	{
		instances++;
	}
	
	this.decInstances = function()
	{
		instances--;
	}
	
	self.create = function(_data)
	{
		userid = _data.userid;
		isbuddy = _data.isbuddy;
		row = $("ul#userlist > li:last ul li:first").clone(); // .appendTo('ul#userlist');
		row.addClass('uid-'+userid);
		self.update(_data);
		  status_activity('isbuddy?');
		if (isbuddy)
		{
			row.addClass('buddy');
			if (window.fluid)
			{
				window.fluid.showGrowlNotification({
				    title: data.username, 
				    description: 'is now online', 
				    priority: 1, 
				    sticky: false,
				    //identifier: "swfff",
				   //onclick: ff_disconnect,
				    icon: data.avatar // or URL string
				})
			}
		}
		  status_activity('is me?');
		if (userid == myuserid)
		{
			row.addClass('me');
		}
		
		  status_activity("CREATE: "+var_dump(data));
		//users[data.userid] = this;
		  status_activity("creating user " + data.userid);
		
		// TODO check for challenges too; use data.chats instead of chats
		if (chats['u'+data.userid])
		{
			chats['u'+data.userid].print(data.username + ' came online.', 0);
		}
		$('.uid-'+userid+'.offline').removeClass('offline');
		
		self.show();
	}
	
	self.show = function()
	{
		if (!rowActive)
		{
			rowActive = true;
			  status_activity("showing row for user" + data.userid);
			
			row.find(".username").html(data.username);
			row.find(".avatar").html('<img src="' + data.avatarpath + '" width="' + data.avswidth + '" height="'+ data.avsheight +'" />');
			
			row.find('.chat').html('<a href="javascript: addChat(\'u\', \''+data.userid+'\');">(chat)</a>');
			
			if (data.blocked) // is currently blocked
			{
				row.find('.block').html('<a href="javascript: block(\''+data.userid+'\');">(unblock)</a>');
			}
			else // is not currently blocked
			{
				row.find('.block').html('<a href="javascript: block(\''+data.userid+'\');">(block)</a>');
			}
			
			row.find('.challenge').html('<a href="javascript: sendChallenge('+data.userid+');">Send a challenge</a>');
			//row.find('.team').html('<a href="javascript: socket.send({\'c\': \'TEAM\', \'u\': '+data.userid+'});">(team)</a>');
			row.find('.profile').html('<a href="/member.php?u='+data.userid+'" target="_blank">(profile)</a>');
			//row.find(".status").html(data.status);
			
			row.click(function() {
				self.resize();
			});
			
			row.find('a').click(function () {
				noResize = true;
				return true;
			});
			
			row.animate({opacity: 'show', height: 'show'}, {duration:'normal'});
		}
	}
	
	self.resize = function()
	{
		if (noResize)
		{
			noResize = false;
			return;
		}
		if (expanded)
		{
			self.contract();
		}
		else
		{
			self.expand();
		}
	}
	
	self.expand = function(_goto)
	{
		if (_goto)
		{
			//var parentDiv = row.parent().parent().find("> div");
			var parentDiv = $("#userlist > li.avail-" + data.avail + " > div");
			
			if (parentDiv.hasClass("contracted"))
			{
				  status_activity("contracted!");
				parentDiv.removeClass("contracted").parent().find("ul").slideDown();
			}
			else
			{
				  status_activity("not contracted");
			}
			
			$("html,body").animate({'scrollTop': row.offset().top}, 'normal');
		}
		expanded = true;
		row.find(".avatar").animate({width: '64px'}, {duration:'fast'});
		row.find(".avatar img").animate({width: data.avwidth, height: data.avheight}, {duration:'fast'});
		row.find(".username").animate({fontSize: '24px'}, {duration:'fast'});
		row.find(".expand").slideDown('fast');
		row.addClass('expanded');
	}
	
	self.contract = function()
	{
		expanded = false;
		row.find(".avatar").animate({width: '32px'}, {duration:'fast'});
		row.find(".avatar img").animate({width: data.avswidth, height: data.avsheight}, {duration:'fast'});
		row.find(".username").animate({fontSize: '14px'}, {duration:'fast'});
		row.removeClass('expanded');
		row.find(".expand").slideUp('fast');
	}
	
	self.quit = function(disconnect)
	{
		  status_activity("QUIT: "+var_dump(data));
		
		if (!disconnect && userid == myuserid)
		{
			ff_disconnect(validInstanceId, null, null);
			return;
		}
		
		users[userid] = null;
		delete users[userid];
		// TODO needed if user is filtered out? tbd
		
		// TODO check for challenges too; use data.chats instead of chats
		if (chats['u'+data.userid])
		{
			if (disconnect)
			{
				chats['u'+data.userid].print('Disconnected.', 0);
			}
			else
			{
				chats['u'+data.userid].print(data.username + ' went offline.', 0);
				$(".uid-"+data.userid+" .status").html('Offline');
				$(".uid-"+data.userid).addClass('offline');
			}
		}
		  status_activity("u"+data.userid);
		
		
		
		updateOnlineAvail(data.avail, -1);
		self.hide(function() {
			if (row) { row.remove();}
			delUser(data.userid);
		});
		//;
		//delUser(data.userid);
		//delete users[data.userid];
		//delete self;
	}
	
	self.hide = function (callback)
	{
		// hide row
		rowActive = false;
		if (row)
		{
			row.animate({opacity: 'hide', height: 'hide'}, {duration:'normal', complete: callback});
		}
	}
	
	self.update = function(_data)
	{
		$.each(_data, function(k,v){
			self.set(k,v);
		});
	}
	
	self.block = function(_blocked)
	{
		data.blocked = _blocked;
		var text;
		if (_blocked)
		{
			text = 'unblock';
		}
		else
		{
			text = 'block';
		}
		row.find('.block a').html('('+text+')');
	}
	
	// TODO generalize and decompose
	self.set = function(k,v)
	{
		
		// Special handlers
		switch (k)
		{
			// TODO generalize for all relevant areas
			case 'status':
				var status_clean = $('.uid-' + userid + ' .status').html(v);
				// TODO is this actually desirable behavior?
				if (userid == myuserid)
				{
					// workaround to prevent html entities in input ield
					$("#update_status").val(status_clean.text());
				}
				break;
			case 'avail':
				// move based on avail
				if (userid == myuserid)
				{
					$("#update_avail").val(v);
				}
				if (!data.avail || data.avail != v)
				{
					  status_activity('avail: ' + v);
					var placed = false;
					// place based on buddystatus, alphabetical order
					$('ul#userlist li.avail-'+v+' ul li').each(function(i) {
						var obj = $(this);
						  status_activity("iteration " + i + ": " + obj.find('.username').html());
						
		  status_activity("HERE " + k);
		  status_activity('isbuddy ' + isbuddy);
		  status_activity('objhasClass ' + obj.hasClass('buddy'));
		
		  status_activity('obj username ' + obj.find('.username').html().toLowerCase());
		//  status_activity('data username' + data.username.toLowerCase());
		
						if ((isbuddy && !obj.hasClass('buddy')) || (isbuddy == obj.hasClass('buddy') && (data.username && obj.find('.username').html().toLowerCase() > data.username.toLowerCase())))
						{
		  status_activity("HERE2 " + k);
							row.insertBefore(obj);
							placed = true;
							return false;
						}
						  status_activity(isbuddy +' ' +obj.hasClass('buddy'));
						//  status_activity(obj.find('.username').html().toLowerCase() +' '+ data.username.toLowerCase());
					});
					if (!placed)
					{
						// if this hasn't been placed yet, place last
						row.appendTo("ul#userlist li.avail-"+ v+" ul");
					}
					updateOnlineAvail(v, 1);
					if (data.avail)
					{
						updateOnlineAvail(data.avail, -1);
					}
					// row.appendTo("ul#userlist"); // li.Offline ul");
				}
				$('.uid-' + userid + ' .avail').html(availMsg[v]);
				break;
			case 'location':
				$('.uid-' + userid + ' .location').html(v);
				// Show/hide the location label based on whether the value is set
				if (v)
				{
					$('.uid-' + userid + ' .location_f').show();
				}
				else
				{
					$('.uid-' + userid + ' .location_f').hide();
				}
				break;
			case 'prefs':
				$('.uid-' + userid + ' .prefs').empty();
				if (userid == myuserid)
				{
					$('#prefsForm input').attr('checked', false);
					$('#prefsForm input[value="np"]').attr('checked', true);
					$('#prefsForm option[value="np"]').attr('selected', true);
					row.find('.region').text('');
				}
				var prefText = [];
				$.each(v, function(p,pv) {
					  status_activity('prefs: ' + var_dump(p) + ' ' + var_dump(pv));
					if (!pv || !pv.length || pv[0] == 'np' || prefList[p]['d'] == 'a' || p == 'sound')
					{
						return true;
					}
					
					if (p == 'region')
					{
						row.find('.region').html(prefList[p]['o'][pv[0]]);
					}
					
					var parr = [];
					$.each(pv, function (a,b) {
						  status_activity("parr.push: ");
						parr.push(prefList[p]['o'][b]);
						
						if (userid == myuserid)
						{
							  status_activity('p, pv: ' + var_dump(p) + ' : ' + var_dump(pv));
							if (prefList[p].t == 'd')
							{
								$('#pref-'+p+'-'+b).attr('selected', true);
							}
							else
							{
								$('#pref-'+p+'-'+b).attr('checked', true);
							}
						}
					});
					//var test = pv.join(', ');
					//  status_activity("test: " + test);
					//$('.uid-' + userid + ' .prefs').append('<div>'+prefList[p].n+': ' + pv.join(', ')+'</div>');
					prefText.push(prefList[p].n+': ' + parr.join(', '));
					
				});
				$('.uid-' + userid + ' .prefs').html('<div>'+prefText.join('; ')+'</div>');
				break;
		}
		data[k] = v;
		  status_activity("SET " + userid + ' ' + k + ", " + v);
	}
}

function block(userid)
{
	var user;
	if (user = getUser(userid))
	{
		if (user.getData().blocked) // unblock
		{
			socket.send({'c':'BLOCK', 'u': userid, 's': 0});
			user.block(0);
		}
		else // block
		{
			socket.send({'c':'BLOCK', 'u': userid, 's': 1});
			user.block(1);
			row.find('.block').html('<a href="javascript: block(\''+data.userid+'\');">(unblock)</a>');
		}
	}
}


function addChat(_chatType, _id, _users, _owner)
{
	if (chats[_chatType + _id])
	{
		chats[_chatType + _id].blink('alert');
		return;
	}
	var chat = new Chat();
	return chat.create(_chatType, _id, _users, _owner);
}

function Chat(options)
{
	var self = this;
	var options = options || {};
	
	var chatType; // user chat or challenge chat
	var id; // chat id TODO rename
	var members = new Object();
	var memberCount = 0;
	var owner = 0;	
	
	var box; // for display purposes
	var dragged = false;
	var dragLeft = 0;
	var dragRight = 0;
	var textbox;
	var boxUsed = 0; // whether textbox has been clicked/typed in
	var active = false; // whether or not chatbox is focused within the window
	
	// TODO used?
	var status;
	
	self.send = function(msg)
	{
		socket.send({'c': 'MSG', 't': chatType, 'i': id, 'm': msg});
	}
	
	self.pending = function(isPending, msg, username, avatar)
	{
		  status_activity("Pending function called");
		if (isPending && (!active || !windowActive))
		{
			  status_activity("now pending " + active + ' ' + windowActive);
			box.addClass("pending");	
			if (!windowActive && window.fluid)
			{
				msgPending++;
				window.fluid.dockBadge = msgPending;
				
				window.fluid.showGrowlNotification({
				    title: username, 
				    description: msg, 
				    priority: 1, 
				    sticky: false,
				    //identifier: "swfff",
				   //onclick: ff_disconnect,
				    icon: avatar // or URL string
				})
			}
		}
		else
		{
			  status_activity("NOT pending");
			box.removeClass("pending");
			// pending = 0;
		}
	}
	
	// Used when someone tries to open a new chat that's already open
	self.blink = function(tclass)
	{
		self.toggle(tclass);
		setTimeout(function(){self.toggle(tclass)}, 100);
		setTimeout(function(){self.toggle(tclass)}, 200);
		setTimeout(function(){self.toggle(tclass)}, 300);
	}
	
	self.toggle = function(tclass)
	{
		box.toggleClass(tclass);
	}
	
	self.create = function(_chatType, _id, _users, _owner, _codes)
	{
		chatType = _chatType;
		id = _id;
		chats[chatType + _id] = this;
		
		  status_activity("creating new chat window");
		// 'cancel': 'ul.chatarea *, :input'
		// 'snap': 'div.chatwindow, #updateform', 
		box = $("div#chatcontainer-fixed div.chatwindow-outer.hidden:first").clone().insertAfter("div#chatcontainer-fixed div.chatwindow-outer:last").addClass('chat-'+chatType+id).draggable({'containment': 'document', 'zindex': 12, 'distance': 5, 'handle': 'div.chatwindow-top', 'cancel': 'ul.chatarea, :input', 'stop': function(e, ui) {
			  status_activity("top: " + box.css('top'));
			  status_activity("winheight: " + $(window).height());
			  status_activity("boxheight: " + box.height());
			if (box.offset().top > $(window).height() - box.height() - 40)
			{
				box.css('top', $(window).height() - box.height() - 40);
			}
			dragLeft = box.css('left');
			dragTop = box.css('top');
		}}).draggable('disable').css({'top': 0, 'left': 0});
		/* .draggable({'containment': 'document', 'zindex': 12, 'distance': 5, 'handle': 'div.chatwindow-top', 'cancel': 'ul.chatarea, :input', 'snap': 'div.chatwindow, #updateform', 'start': function(e,ui) {
			if (!dragged)
			{
				dragged = true;
				$('.globalfixed:first').clone().insertAfter('.globalfixed:last');
				box.appendTo('.globalfixed:last');
				//box.css('position', 'fixed');
			}
		}
		});*/

		textbox = box.find("textarea");
		
		box.focus(function(){
			active = true;
			self.pending(false);
		});
		box.click(function(){
			active = true;
			self.pending(false);
		});
		textbox.blur(function(){
			active = false;
		});
		
		textbox.focus(function(){
			  status_activity("Chat textbox focused");
			active = true;
			self.pending(false);
			if (!boxUsed)
			{
				boxUsed = 1;
				textbox.val('');
			}
		});
		
		box.find("textarea").keypress(function(event){
			active = true;
			self.pending(false);
			key = event.charCode ? event.charCode : event.keyCode;
			//  status_activity('caught keypress ' + key);
			if (key == 13)
			{
				//  status_activity('match');
				var msg = box.find("textarea").val();
				self.send(msg);
				box.find("textarea").val('');
				return false;
			}
			else
			{
				return true;
			}
		});
		
		box.find(".window-buttons .close").click(function(){
			self.close();
			if (chatType == 'c')
			{
				socket.send({'c':'CHALSTS', 'i':id, 's': CS_REJECTED});
				if (outgoingChallenge == id)
				{
					outgoingChallenge = 0;
					update_chalStr(CSTR_SEND);
				}
			}
		});
		
		
		box.find(".window-buttons .minmax").click(function(){
			box.find(".chatarea, textarea").animate({minHeight: 'toggle', height: 'toggle'});
		});
		
		
		if ($.browser.msie)
		{
			box.find(".window-buttons .drag").hide();
		}
		else
		{			
			box.find(".window-buttons .drag").toggle(function(){
				dragged = true;
				var dragOffset = box.offset();
				dragTop = dragOffset.top;
				dragLeft = dragOffset.left;
				//$('.globalfixed:first').clone().insertAfter('.globalfixed:last');
				box.appendTo('body').addClass('globalfixed').draggable('enable').css({'top': dragTop, 'left': dragLeft}).find(".window-buttons .drag").html("&darr;").attr('title', 'Return chat to bottom');
				
				self.scroll(0);
			}, function() {
				dragged = false;
				//var parentContainer = box.parent();
			
			// extra show() prevents window from disappearing in safari
			box.appendTo('#chatcontainer-relative').removeClass('globalfixed').draggable('disable').css({'top': 0, 'left': 0}).show().find(".window-buttons .drag").html("&uarr;").attr('title', 'Enable dragging');
			self.scroll(0);
			//parentContainer.remove();
			});
		}
		
		box.find(".chatmembers").dblclick(function(){
			box.find(".chatarea, textarea").animate({minHeight: 'toggle', height: 'toggle'});
			return false;
		});
		
		if (chatType == 'u')
		{
			self.addUser(id);
		}
		else
		{
			playSound('msgNew');
			owner = _owner;
			if (_codes)
			{
					$.each(_users, function(k,v) {
					self.addUser(k,v,_codes[k]);
					// TODO if my challenge, hide challenge link
				});
			}
			else
			{
					$.each(_users, function(k,v) {
					self.addUser(k,v);
					// TODO if my challenge, hide challenge link
				});
			}
			
			if (owner == myuserid)
			{
				outgoingChallenge = id;
				// TODO check for max number of challengees, remove links if too high
				if (memberCount >= 4)
				{
					update_chalStr(CSTR_NULL);
				}
				else
				{
					update_chalStr(CSTR_ADD);
				}
			}
			
			box.find(".status").removeClass("status").addClass("chalstatus");
		}
		
		box.animate({opacity: 'toggle', height: 'toggle'}, 'normal');
		
		box.removeClass('hidden');
		
		//self.show();
	}
	
	self.addUser = function(userid, chalstatus, code)
	{
		  status_activity("adding userid " + userid + " to chat/challenge id " + id + " with fc " + code);
		
		var userobj = getUser(userid);
		if (!userobj)
		{
			  status_activity("userobj: getuser failed");
			return;
		}
		if (members[userid])
		{
			  status_activity('active? ' + members[userid].active);
			if (members[userid].active)
			{
				this.updateUser(userid, chalstatus, code);
				return;
			}
			else
			{
				clearTimeout(members[userid].removeTimeout);
			}
		}
		else
		{
			members[userid] = new Object();
			members[userid]['li'] = box.find(".chatmembers li:first").clone().insertAfter('.chat-'+chatType+id+' .chatmembers li:last').addClass('uid-'+userid);
		}
		
		memberCount++;
		// TODO add this chat to users' chat array
		
		members[userid].active = true;
		
		var user = userobj.getData();
		  status_activity("Adding user to chat");
		
		members[userid].li.find(".username").html(user.username);

		members[userid].li.find(".avatar").html('<img src="' + user.avatarpath + '" width="' + user.avswidth/2 + '" height="'+ user.avsheight/2 +'" />');

		if ($.browser.msie)
		{
			members[userid].li.find(".username, .avatar").wrap("<a href=\"javascript: userex = getUser("+ userid +"); if (userex) { userex.expand(true); } return false;\"></a>");
		}
		else
		{					
			members[userid].li.find(".username, .avatar").click(function() {
				userex = getUser(userid);
				if (userex)
				{
					userex.expand(true);
				}
				// TODO add profile link to actual member row
				//window.open('http://smashboards.com/member.php?u=' + userid, 'profile');
			});
		}
		
		//members[userid].li.find('.username, .avatar')		
		if (chatType == 'u')
		{
			members[userid].li.find(".status").html(user.status);
		}
		else
		{
			if (!chalstatus)
			{
				chalstatus = CS_WAITING;
			}
			  status_activity("about to update chalstatus...");
			members[userid]['chalstatus'] = chalstatus;
			members[userid].li.find("div").addClass("chalstatus-" + chalstatus);
			if (userid == myuserid && chalstatus == CS_WAITING)
			{
				chalstatus = CS_MYREPLY;
				if ($.browser.msie)
				{
					members[userid].li.find("div").html("<a href=\"javascript: socket.send({'c':'CHALSTS', 'i':"+id+", 's':"+CS_ACCEPTED+"})\">"+ chalStatusMsg[chalstatus]+ "</a>");
				}
				else
				{					
					members[userid].li.find("div").html(chalStatusMsg[chalstatus]).click(function() {
						socket.send({'c':'CHALSTS', 'i':id, 's':CS_ACCEPTED});
					});
				}
			}
			else
			{
				members[userid].li.find("div").html(chalStatusMsg[chalstatus]);
			}
			//  status_activity("ml");
			//  status_activity("members length: " + members.length);
			if (outgoingChallenge == id)
			{
//				$(".uid-"+userid+" .challenge a").html(chalStr[CSTR_NULL]);
				if (memberCount >= 4)
				{
					update_chalStr(CSTR_NULL);
				}
				else
				{
					update_chalStr(CSTR_ADD);
				}
			}
			
			if (userid != owner)
			{
				if (userid != myuserid)
				{
					this.print(getUser(userid).getData().username + ' has been challenged.');
				}
				else
				{
					ownerinfo = getUser(owner);
					if (ownerinfo)
					{
						this.print('You have been challenged by ' + getUser(owner).getData().username + '.');
					}
				}
			}
		}
		
		members[userid].li.show();
		
		
		// TODO here? rework this, perhaps
		this.updateUser(userid, chalstatus, code);
		
	}
	
	self.updateUser = function(userid, chalstatus, code)
	{
		  status_activity("updateUser called, code: " + code);
		if (chalstatus == CS_WAITING || chalstatus == CS_MYREPLY)
		{
			return;
		}
		
		if (!members[userid])
		{
			return;
		}
		
		var oldstatus = members[userid].chalstatus;
		
		var verb;
		if (chalstatus == CS_REJECTED)
		{
			memberCount--;
			members[userid].active = false;
			members[userid].removeTimeout = setTimeout(function() { self.removeUser(userid); }, 3000);
			if (oldstatus == CS_ACCEPTED)
			{
				chalstatus = CS_LEFT;
				verb = 'left';
			}
			else
			{
				verb = 'declined';
			}
		}
		else
		{
			verb = 'accepted';
		}
		
		if (chalstatus != CS_ACCEPTED || !code)
		{
			members[userid].li.find('.chalstatus').removeClass('chalstatus-'+members[userid].chalstatus).addClass('chalstatus-'+chalstatus).html(chalStatusMsg[chalstatus]);
		}
		else
		{
			  status_activity("CS_ACCEPTED in updateUser " + code);
			 members[userid].li.find('div').removeClass('chalstatus-'+members[userid].chalstatus).addClass('chalstatus-'+chalstatus).html(code);
			members[userid].code = code;
		}
		
		
		if (oldstatus == chalstatus)
		{
			return;
		}
		
		this.print(getUser(userid).getData().username + ' ' + verb + ' the challenge.');
		
		members[userid].chalstatus = chalstatus;
	}
	
	self.removeUser = function(userid)
	{
		if (members[userid] && !members[userid].active)
		{
			  status_activity("Removing user " + userid);
			
			if (outgoingChallenge == id)
			{
				if (memberCount >= 4)
				{
					update_chalStr(CSTR_NULL);
				}
				else
				{
					update_chalStr(CSTR_ADD);
				}
				
				// TODO use classes instead
				// $("#userlist .uid-"+userid+" .challenge").show();
			}
			
			members[userid].li.slideUp(function() {
				if (!members[userid].active)
				{
					members[userid].li.remove();
					members[userid] = null;
					delete members[userid];
				}
			});
		}
	}
	
	
	self.print = function(msg, userid, newWin)
	{
		  status_activity(msg + ' ' + userid);
		
		// TODO use vB format? other local format?
		var d = new Date();
		
		var year = d.getFullYear();
		
		var hour = d.getHours();
		var ampm = 'AM';
		if (hour > 12)
		{
			hour -= 12;
			ampm = 'PM';
		}
		else if (hour == 0)
		{
			hour = 12;
		}
		else if (hour == 12)
		{
			ampm = 'PM';
		}
		
		var min = d.getMinutes();
		if (min <= 9)
		{
			min = '0' + min;
		}
		if (min == '0')
		{
			min = '00';
		}
		
		var time = hour + ':' + min + ' ' + ampm;
		var month = d.getMonth() + 1;
		var timestamp = month + '/' + d.getDate() + '/' + year + ' ' + time;
		
		//  status_activity(scrollVal + ' ' + oldHeight);
		if (userid > 0)
		{
			var userSend = getUser(userid).getData();
			var name = userSend.username;
			box.find(".chatarea").append('<li title="'+ timestamp +'"><span class="username">'+name+':</span> '+msg+'</li>');
			if (userid != myuserid)
			{
				self.pending(true, msg, name, 'http://smashboards.com/' +userSend.avatarpath);
				if (newWin)
				{
					playSound('msgNew');
				}
				else
				{
					playSound('msgRcv');
				}
			}
			else
			{
				playSound('msgSend');	
			}
		}
		else
		{
			box.find(".chatarea").append('<li class="sysmsg" title="'+ timestamp + '">'+msg+'</li>');
			
			self.pending(true, msg, 'SWF Friend Finder', '');// 'http://smashboards.com/avatar.jpg');
			if (newWin)
			{
				playSound('msgNew');
			}
			else
			{
				playSound('msgSys');
			}
		}
		
		self.scroll();
		
		if ($.browser.msie && box.find('.chatarea').height() > 119)
		{
			box.find('.chatarea').css('height', 120);
		}
	}
	
	self.scroll = function(time)
	{
		if (!time)
		{
			time = 500;
		}
		
		var winHeight = box.find(".chatarea li:first").offset().top;
		var Height = box.find(".chatarea li:last").offset().top;
		var offset = (winHeight < 0) ? (Height - winHeight) : Height;
		  status_activity('SCROLL INFO: ' + Height + ' ' + winHeight + ' ' + offset);
		box.find(".chatarea").animate({scrollTop: offset}, {duration: time});
	}
	
	self.show = function()
	{
		// not used
	}
	
	self.close = function ()
	{
		
		//box.animate({opacity: 'hide', height: 'hide', width: 'hide'}, {duration:'normal'}).animate({width: 'hide'}, {duration: 'normal', complete: function() {box.remove()}});
		box.animate({opacity: 0, height: '1px'}, {duration:'normal'}).animate({width: 'hide'}, {duration:'normal', complete: function() {box.remove()}});
		delete chats[chatType + id];
		// hide row
	}
}

function ff_disconnect(i, j, event)
{
	if (i != validInstanceId)
	{
		return;
	}

	clearInterval(pingInterval);
	
	  status_activity("Disconnected");
	connectedStatus = 0;
	
	socket = null;
	$(".chatwindow textarea").attr('disabled','disabled');
	
	if (tryReconnect)
	{
		// TODO show a message
		$("div#loading").addClass("halfscreen").find("h2").html("Disconnected");
		$("div#loading div#loading_status").html('Trying to reconnect....');
		$("div#loading").slideDown();
		reconnectTimeout = setTimeout('ff_try_connect()', reconnectTimeDefault);
	}
	//   status_activity('users: '+var_dump(users));
	
	$.each(users, function(k,user){
		if (k > 0 && user != null)
		{
			  status_activity(var_dump(k+' -- ' +user));
			user.quit(true);
		}
	});
	//socket.close();

}

function ff_connect(i, j, event)
{
	if (i != validInstanceId)
	{
		return;
	}
	
	$("div#loading div#loading_status").html('Connected, now trying to authenticate...');
	
	  status_activity("Connected, now trying to authenticate");
	connectedStatus = 1;
	reconnectTime = reconnectTimeDefault;
	clearTimeout(reconnectTimeout);
	
	  status_activity("ping attempt?");
	resetPing();
	socket.send({'c':'USER', 'p':mypass, 'u':myuserid});
}

function resetPing()
{
	pingPending = 0;
	if (pingInterval)
	{
		clearInterval(pingInterval);
	}
	
	pingInterval = setInterval('sendPing()', 120000);
}

function sendPing()
{
	  status_activity("About to ping server...");
	if (socket)
	{
		if (pingPending > 0)
		{
			socket.close();
			  status_activity("Ping timeout");
			ff_disconnect(validInstanceId, null, null); // needed?
		}
		else
		{
			pingPending++;
			socket.send({'c':'PING'});	
		}
	}
}

function update_chalStr(strid)
{
	if (chalStr[strid])
	{
		$("#userlist .challenge a").html(chalStr[strid]);
	}
}

function ff_data(i, j, event)
{
	if (i != validInstanceId)
	{
		return;
	}
	  status_activity("<- ["+i+' ' + j+'] '+event.text);
	
	//debug(var_dump(event.text));
	// TODO JSON parser doesn't like double quotes?
	var data = JSON.parse(event.text);
	
	if (data.v && data.v > loadedVer)
	{
		newVersion(data.v);
	}
	//alert('here');
	
	switch(data.c)
	{
		case 'USER':
			if (data.i > 0)
			{
				$("div#loading").fadeOut();
				  status_activity("now logged in as " + data.u + " (" + data.i +")");
				$(".chatwindow textarea").attr('disabled','');
				//socket.send({'c':'NAMES'});
				// TODO send info about open challenges (if reconnecting)
			}
			break;
		case 'NAMES':
		case 'WHOIS':
		//debug('herefirst');
				$.each(data.d, function(j,user){
					//debug('here');
					// does user exist in users array?
					if (!(users[user.userid]))
					{
						//debug('some user');
						//debug(var_dump(user));
						// if not, create new user
						addUser(user);
						  status_activity(data.c + " added user " + user.username);
					}
					else
					{
						// if so, update with new data
						// TODO isn't this just like SET?
						//userm.update(user);
						  status_activity(data.c + " updated user " + user.username);
					}
				});
			break;
		case 'SET':
			getUser(data.i).update(data.s);
			break;
		case 'MSG':
			var newWin = false;
			if (chats[data.t + data.i])
			{
				  status_activity('found existing chat for MSG');
				thisChat = chats[data.t + data.i];
			}
			else if (data.t == 'u')
			{
				  status_activity('creating new chat based on MSG');
				thisChat = new Chat();
				newWin = true;
				thisChat.create(data.t, data.i);
			}
			else
			{
				break;
			}
			  status_activity('printing incoming message');
			thisChat.print(data.m, data.u, newWin);
			break;
		case 'CHAL':
			if (!chats['c' + data.i])
			{
				  status_activity('creating new challenge chat based on CHAL');
				thisChat = new Chat('c', data.i, data.u);
				thisChat.create('c', data.i, data.u, data.o, data.d);
			}
			else
			{
				  status_activity('found existing chat for CHAL');
				thisChat = chats['c' + data.i];
				if (data.d)
				{
					$.each(data.u, function(k,v){
						  status_activity("adding user " + k + " to challenge");
						thisChat.addUser(k,v,data.d[k]);
					});
				}
				else
				{					
					$.each(data.u, function(k,v){
						  status_activity("adding user " + k + " to challenge");
						thisChat.addUser(k,v);
					});
				}
			}
			break;
		case 'CHALSTS':
			chats['c'+data.i].updateUser(data.u, data.s, data.d);
			break;
		case 'QUIT':
			if (data.i)
			{
				users[data.i].quit();
			}
			else
			{
				ff_disconnect(validInstanceId, null, null);
			}
			break;
		case 'JOIN':
			addUser(data.d);
			break;
		case 'PREFLIST':
			setPreflist(data.p);
			break;
		case 'PONG':
			resetPing();
			break;
		case 'ERROR':
			switch (data.n)
			{
				case 2:
				case 7:
					$("div#loading h2").html("Login failed");
					$("div#loading div#loading_status").html(data.e);
					tryReconnect = false;
					break;
			}
			break;
		default:
			debug(event.text);
	}
}

function handle(i, j, event)
{
	if (i.type != 'data')
	{
	  status_activity('INCOMING EVENT: '+var_dump(i)+' ' + j + ' ' +var_dump(event));
	//debug("<p>GENERIC HANDLER START (" + event.type + ")</p>" + "<p>" + event.text + "</p>" + "<p>GENERIC HANDLER END</p>");
	}
}









soundManager.onload = function() {
  // soundManager should be ready to use/call at this point
  readySound = true;
  
  soundManager.defaultOptions = {'volume': 25};
  
  soundManager.createSound('msgSend','/ff/msgsnd.mp3');
  soundManager.createSound('msgNew','/ff/msgnew.mp3');
  soundManager.createSound('msgRcv','/ff/msgrcv.mp3');
  soundManager.createSound('msgSys','/ff/msgsys.mp3');
  soundManager.createSound('msg','/ff/msgsys.mp3');
}

$(document).ready(function(){
	  status_activity("JavaScript support loaded");
	$("div#loading div#loading_status").html("You don't seem to have Flash installed. The Friend Finder requires Flash Player version 8 or greater.");
	
	$('#update_submit').click(function() {
		  status_activity("update_submit clicked");
		socket.send({'c': 'SET', 's':{'status': $('#update_status').val()}});
	});

	$('#update_avail').change(function() {
		updateal();
	});
	
	$("#userlist > li > div").click(function() {
		$(this).toggleClass("contracted").parent().find("ul").slideToggle();
	});
	
	$('#update_status').keypress(function(event){
		var key = event.charCode ? event.charCode : event.keyCode;
		//  status_activity('caught keypress ' + key);
		if (key == 13)
		{
			$('#update_submit').click();
			return false;
		}
		else
		{
			return true;
		}
	});
	
	$('#prefsClose').click(function() {
		togglePrefs();
	});
	
	if (window.fluid)
	{
		//window.fluid.addDockMenuItem("Disconnect", ff_disconnect);
	}
	
	readyDoc = true;
	ff_onload_go();
});
 
function ff_onload()
{
	if (readyFlash)
	{
		return;
	}
	  status_activity("Flash support loaded");
	readyFlash = true;
	ff_onload_go();
}
function ff_onload_go()
{
	if (readyOnload || !readyFlash || !readyDoc)
	{
		return;
	}
	readyOnload = true;
	$("div#loading div#loading_status").html("Now connecting to the SWF Friend Finder service.");
	if (!myuserid || !mypass)
	{
		$("div#loading h2").html('Not logged in');
		// TODO ff_onload won't work because myuserid/mypass won't change
		$("div#loading div#loading_status").html('You are not currently logged in to Smash Boards. Make sure you are <a href="/index.php" target="_blank">logged in</a> and <a href="javascript:reload_all()">try again</a>.');
	}
	else
	{
		ff_try_connect();
	}
}

function ff_try_connect(reconnect)
{
	if (socket)
	{
		socket.close();
	}
	validInstanceId = 0;
	if (!connectedStatus && tryReconnect)
	{
		  status_activity("Trying to connect...");
		socket = new XMLSocket();
		socket.connect('smashboards.com',5852);	
		
		if (reconnect)
		{
			reconnectTime *= 2;
			if (reconnectTime > 60000)
			{
				reconnectTime = 60000;
			}
			
			if (reconnectTime > reconnectTimeDefault*4)
			{
				$("div#loading div#loading_status").html('<p>This is taking longer than it should. Make sure you have a working Internet connection, and that the Friend Finder service is <a href="status.php" target="_blank">currently running</a>. You can also try <a href="javascript:reload_all()">reloading</a>.</p>');
			}
		}
		
		  status_activity("Next connection attempt in " + reconnectTime/1000 + " seconds");
		
		reconnectTimeout = setTimeout('ff_try_connect(true)', reconnectTime);
	}
}

// called when the user closes the friend finder
function unload()
{
	// TODO if there are open chats, alert the user that they will be lost
	// but only if they haven't already confirmed through quit or something
	if (socket)
	{
		socket.send({'c': 'QUIT'});
	}
}


function sendChallenge(userid)
{
	if (outgoingChallenge)
	{
		// TODO add user to existing outgoingChallenge
		
		socket.send({'c': 'CHAL', 'u': [userid]});
	}
	else
	{
		socket.send({'c': 'CHAL', 'u': [userid]});
	}
}

function playSound(name)
{
	if (readySound) // && prefList.sound && prefList.sound[name])
	{
		soundManager.play(name);
	}
}

function firstLoad()
{
	show_popup('Welcome to the SWF Friend Finder', 'Welcome to the SWF Friend Finder! You\'ll be able to use this tool to find fellow SWF members to play online in Brawl. If you have any questions or concerns to report, you can either PM Kirby King or use the "Report an Issue" link at the bottom.', 'Dismiss');
}

function stopUpdate(header1, message1, header2, message2)
{
	if (!header2)
	{
		header2 = header1;
	}
	if (!message2)
	{
		message2 = message1;
	}
		// todo remove online list
   	//if (users)
  	//{
	   //	$.each(users, function(k,usersarr){
	   		// todo remove users en masse
	   			$(".userRow").remove();
	   			users.length = 0;
	
	   			// if isbuddy
	   			// buddyonline--;
	   			//userExists = true;
	   //	});
  	//}
  	
  	//disconnect();
  	
    if (loggedIn && !myuserid)
    {
    	show_popup(header1, message1, 'Try Again', 'forceUpdate()', 'Quit', 'quit_now()');
    }
    else if (loggedIn)
    {
    	show_popup(header2, message2, 'Try Again', 'forceUpdate()', 'Quit', 'quit_now()');
    }
    loggedIn = false;
}

function shutdown(msg)
{
	if (!msg)
	{
		msg = 'The Friend Finder has been temporarily disabled. Please try reloading it later.';
	}
	stopUpdate('Friend Finder Offline', msg);
}

function newVersion(loadVer)
{
	loadedVer = loadVer;
	show_popup('Update Available', 'The Friend Finder has been updated, but you must reload this window in order to obtain the update. Press "Reload" to reload this window automatically.', 'Reload', 'reload_all()', 'Cancel');
}

function reload_all()
{
	window.location = window.location;
}

function quit()
{
	if (popupActive)
	{
		if (confirm('You are about to disconnect from the Friend Finder. Press OK to close, or Cancel to stay connected.'))
		{
			quit_now();
		}
	}
	else
	{
		show_popup('Are you sure?', 'You are about to disconnect from the Friend Finder. Press Quit to close, or Cancel to stay connected.', 'Quit', 'quit_now();', 'Cancel');
	}
}

function quit_now()
{
	unload();
	window.close();
	// In case window.close doesn't work
	window.location = 'http://smashboards.com/';
}


function togglePrefs()
{
	if (prefsShow)
	{
		prefsShow = false;
		$("#prefsForm").animate({'opacity':'hide', 'height':'hide'});
		// Send prefs in to server

		var prefs = new Object;
		var prefsCount = 0;
		
		$('#prefsForm :input').each(function(k,v) {
			if (!prefs[v.name])
			{
				prefs[v.name] = [];
			}
			prefsCount++;
		});
		
		$('#prefsForm :input:checked, #prefsForm :input:selected, #prefsForm option:selected').each(function(k,v) {
			  status_activity('submitting: '+v.name+' | ' + v.value);
			
			if (v.name)
			{
				prefs[v.name].push(v.value);
			}
			else
			{
				prefs['region'].push(v.value);
			}
		});
		
		if (prefsCount)
		{
			socket.send({'c':'PREFS', 'p':prefs});
		}
		
	}
	else
	{
		prefsShow = true;
		$("#prefsForm").animate({'opacity':'show', 'height':'show'});
	}
}

function setPreflist(preflist)
{
	$("#prefsForm div.pref:gt(0)").remove();
	  status_activity("preflist: " + preflist);
	$.each(preflist, function(k,v)
	{
	  status_activity("Processing preflist " + k+" ("+var_dump(v)+")");
		//if (typeof(v) == 'object')
		//{
		//	setPreflist(v);
		//}
		//else
		//{
		setPreflistItem(v, k);
		//}
	});
}

function setPreflistItem(item, key, itemparent)
{
	// TODO if (itemparent)
	
	
	var pref;
	var checked = false;
	
	if (item.t == 'r' || item.t == 'o')
	{
		pref = $("#prefsForm div.pref:first").clone().insertAfter("#prefsForm div.pref:last");
	}
	else if (item.t == 'd')
	{
		pref = $("#prefsForm div.pref:first").clone().insertAfter("#prefsForm div.pref:last");
		pref.find('span.option_name').wrap('<select name="'+item.i+'"></select>');
		pref.find("select").append('<option value="np"></option>');
	}
	else
	{
		pref = $("#prefsForm div.pref:first").clone().insertAfter("#prefsForm div.pref:last");
	}

	  status_activity("Added " + var_dump(item) +" to preflist");
	pref.find("div.pref_name").html(item.n);
	
	if (!item.o)
	{
		item.o = defaultPrefOpts;
	}
	
	
	prefList[key] = item;
	
	// TODO add No Preference option
	$.each(item.o, function(k,v)
	{
		var vr=v.replace(' ', '-');
		//if (k == 0) return true;
		if (typeof(v) == 'object')
		{
			setPreflistItem(v, item);
		}
		  status_activity("Adding option " + v + " to pref");
		
		// TODO: IE sucks, must insert raw HTML for new input
		
		switch (item.t)
		{
			case 'r':
			case 'o':
			
				var option = pref.find("span.option:first").clone();
		
				option.find("span.option_name").html(v);
				  status_activity('attribute1: ' + option.find('input').attr('name'));
				//option.find("input").attr({'name':item.i, 'value':v, 'id':'pref-'+item.i+'-'+v});
				//option.find("label").attr({'for':'pref-'+item.i+'-'+v});
				if (item.c)
				{
					option.prepend('<input type="radio" name="'+ item.i +'" value="'+k+'" id="pref-'+item.i+'-'+k+'" checked="checked" />');
					checked = true;
				}
				else
				{
					option.prepend('<input type="radio" name="'+ item.i +'" value="'+k+'" id="pref-'+item.i+'-'+k+'" />');
				}
				
				option.find("label").attr({'for':'pref-'+item.i+'-'+k});
				  status_activity('attribute2: ' + option.find('input').attr('name'));
				break;
			case 'd':
				var option = pref.find("select");
				if (item.c)
				{
					option.append('<option value="'+k+'" id="pref-'+item.i+'-'+k+'" selected>'+v+'</option>');
					checked = true;
				}
				else
				{
					option.append('<option value="'+k+'" id="pref-'+item.i+'-'+k+'">'+v+'</option>');
				}
				break;
			default:
				var option = pref.find("span.option:first").clone();
				option.find("span.option_name").html(v);
				option.prepend('<input type="checkbox" name="'+ item.i +'" value="'+k+'" id="pref-'+item.i+'-'+k+'" />');
				//option.find("input").attr({'name':item.i+'-'+v, 'id':'pref-'+item.i+'-'+v});
				option.find("label").attr({'for':'pref-'+item.i+'-'+k});
				break;
		}
		
		
		pref.find("span.option:last").after(option);
		//option.find("input").attr('name', 'opt-'++'-'+k')
	});
	
	
	// Add the "no preference" option
	switch (item.t)
	{
		case 'r':
		case 'o':
			var option = pref.find("span.option:first").clone();
			option.find("span.option_name").html('No preference');
			if (!checked)
			{
				option.prepend('<input type="radio" name="'+ item.i +'" value="np" id="pref-'+item.i+'-np" checked="checked" />');
			}
			else
			{
				option.prepend('<input type="radio" name="'+ item.i +'" value="np" id="pref-'+item.i+'-np" />');	
			}
			option.find("label").attr({'for':'pref-'+item.i+'-np'});
			pref.find("span.option:last").after(option);
			break;
		case 'd':
			break;
	}
	
}

function getUser(userid)
{
	  status_activity("getUser "+userid);

	// does user with this id exist in our memory?
	if (users[userid])
	{
		  status_activity("got user " + userid);
		return users[userid];
	}
	else
	{
		  status_activity("didn't get user " + userid);
		socket.send({'c': 'NAMES'});
		//socket.send({'c': 'WHOIS', 'i': userid});
	}
	return false;
	// if not, use NAMES and/or WHOIS to update
	// if still doesn't exist, return null or something
	// if does exist, return that user (object)
}

// addUser
// user: user object as sent from server
function addUser(user)
{
	  status_activity("addUser1 " + user.userid);
	if (!users[user.userid])
	{
		users[user.userid] = new User();
		  status_activity('addUser vd: '+ var_dump(user));
		  status_activity("addUser2 " +	 user.userid);
		users[user.userid].create(user);
		  status_activity("addUser3 " + user.userid);
	}
	else
	{
		users[user.userid].update(user);
		users[user.userid].incInstances();
	}
	//users.push(user);
	//showUser(user);
}

function delUser(userid)
{
	  status_activity("delUser " + userid);
	if (users[userid])
	{
		users[userid].decInstances();
		if (users[userid].getInstances == 0)
		{
			users[userid] = null;
			delete users[userid];
		}
	}
}


function filterTable()
{
	if (users.length > 0 && room)
	{
		$.each(users, function(k,user){
			if (user && user.looking != room)
			{
				removeUser(user.userid, k);
			}	
		});
	}
}

function filterUser(user)
{
	if (user.looking != room)
	{
		removeUser(user.userid);
		return true;
	}
	return false;
}


/* Browser stuff */
function status_gen(id, message)
{
	if (message)
	{
		$("#status_" + id).html(message);
		$("#status_" + id).css('display', 'inline');
	}
	else
	{
		$("#status_" + id).css('display', 'none');
	}
}

function status_messages(pending)
{
	if (pending == 0)
	{
		status_gen('messages'); 
	}
	else if (pending == 1)
	{
		status_gen('messages', '1 message pending.');
	}
	else
	{
		status_gen('messages', pending + ' messages pending.');
	}
}

function status_activity(message)
{
	//return;
	if (myuserid == 5852)
	{
		//status_gen('activity', message);
		$("#doutput").append("<div>"+message+"</div>");
	}
}

function status_online()
{
	//show_popup('usersonline', online, 'ok');
	var users;
	total = online;
	buddies = buddyonline;
	if (total && (!buddies))
	{
		if (total == 1)
			users = ' user';
		else
			users = ' users';
		status_gen('online', total + users + ' online.'); 
	}
	else if (total && buddies)
	{
		if (total == 1)
			users = ' user';
		else
			users = ' users';
		if (buddies == 1)
			buddy = ' buddy';
		else
			buddy = ' buddies';
		if (buddies == 0)
		{
			buddies = 'no';
		}
		status_gen('online', total + users + ' online (' + buddies + buddy + ').');
	}
	else if (!total && buddies)
	{
		
		if (buddies == 1)
			buddy = ' buddy';
		else
			buddy = ' buddies';
		if (buddies == 0)
		{
			buddies = 'No';
		}
		status_gen('online', buddies + buddy +' online.');
	}
	else
	{
		status_gen('online', '');
	}
}

function updateal()
{
	var avail = $("#update_avail").val();
	socket.send({'c': 'SET', 's': {'avail': avail}});
}

function showReportForm()
{
	if (!reportFormActive)
	{
		reportFormActive = true;
		show_popup('Report an Issue', '<p>Use this form to report a problem with the Friend Finder or a user, or to make a suggestion.</p>' +
		'<form id="reportform" action="' + url + '" method="post">' +
		'<p><select name="reporttype"><option value="bug">Report a bug</option>' +
		'<option value="user">Report a user</option>' +
		'<option value="suggest">Send a suggestion</option></select></p>' +
		'<p>' +
		'<textarea name="message" rows="6"></textarea>' +
		'</p>' +
		'' +
		'<input type="hidden" name="action" value="reportsend" />' +
		'</form>', 'Send report', 'submitReportForm()', 'Cancel', 'reportFormActive = false; hide_popup()');
	}
}

function submitReportForm()
{
	$("#popup_yes").attr('value', 'Sending...');
	$("#popup_yes").attr("disabled","disabled");
	  status_activity('Submitting report form');
	submitForm('reportform', 'submitReportConfirm');
}

function submitReportConfirm()
{	
	show_popup_next('Report Sent', 'Thanks for your report.', 'Dismiss');
	hide_popup();
	hide_popup(2000);
	reportFormActive = false;
}

function submitForm(formid, callback)
{
	  status_activity('Submitting form');
	var inputs = [];
	$(':input', '#' + formid).each(function(k,v) {
	  inputs.push(v.name + '=' + escape(v.value));
	});
	  status_activity('Inputs assembled');
	//show_popup("ok", 'ok', 'dsaim')
	if (inputs.length > 0)
	{
		if (callback == 'forceUpdate')
		{
			  status_activity('Sending with callback');
			jQuery.ajax({
				'data': inputs.join('&'),
				'type': "POST",
				'url': $('#' + formid).attr('action'),
				'success': function() { forceUpdate(true); },
				'error': alert("There was an error sending your report. Please try again later.")
				//'error': function(msg, two, three) { 
				//		$.each(msg, function(k,v) {
				//		  alert(k + ": " + v);
				//		});
			 	//}
			});
			
			  status_activity('Waiting for callback');
		}
		else if (callback)
		{
			  status_activity('Sending with callback');
			jQuery.ajax({
				'data': inputs.join('&'),
				'type': "POST",
				'url': $('#' + formid).attr('action'),
				'success': function() { submitReportConfirm(); }
				//'error': function(msg, two, three) { 
				//		$.each(msg, function(k,v) {
				//		  alert(k + ": " + v);
				//		});
			 	//}
			});
		}
		else
		{
			jQuery.ajax({
				'data': inputs.join('&'),
				'type': "POST",
				'url': $('#' + formid).attr('action')
			});
		}
	}
}

function show_popup_next(header, message, yes, yes_function, no, no_function)
{
	popups.unshift(Array(header, message, yes, yes_function, no, no_function));
}

function debug(msg)
{
	show_popup('Debug message', msg, 'Dismiss');
}

function show_popup(header, message, yes, yes_function, no, no_function, third, third_function)
{
	
	playSound('msgSys');
	if (popupActive == false)
	{
		focus();
		
		popupActive = true;
		if (yes)
		{
			if (!no && !yes_function)
			{
				yes_function = "hide_popup()";	
			}
			message = message + "<br /><br /><input type=\"submit\" id=\"popup_yes\" value=\"" + yes + "\" \ onClick=\"javascript:" + yes_function + "\" />";
		}
		if (no)
		{
			if (!no_function)
			{
				no_function = "hide_popup()";
			}
			message = message + " <input type=\"submit\" id=\"popup_no\" value=\"" + no + "\" \ onClick=\"javascript:" + no_function + "\" />";
		}
		if (third)
		{
			if (!third_function)
			{
				third_function = "hide_popup()";
			}
			message = message + " <input type=\"submit\" id=\"popup_third\" value=\"" + third + "\" \ onClick=\"javascript:" + third_function + "\" />";
		}
		$("#popup_header").html(header);
		$("#popup_message").html(message);
		$("#popupall").show();
		
		if (windowActive == false)
		{
			alert('You have a new message.');	
		}
		
		windowActive = true;
		//$("#popupcontainer").css('display', '');
		// TODO play sound (use soundmanager (2)?)
	}
	else
	{
		popups.push(Array(header, message, yes, yes_function, no, no_function));
		status_messages(popups.length);
	}
}

function hide_popup(timeout)
{
	if (timeout)
	{
		hideTimeout = setTimeout(hide_popup, timeout);
		return;
	}

	$("#popupall").hide();
	clearTimeout(hideTimeout);
	popupActive = false;
	
	if (popups.length > 0)
	{
		var newPopup = popups.shift();
		show_popup(newPopup[0], newPopup[1], newPopup[2], newPopup[3], newPopup[4], newPopup[5]);
	}
	status_messages(popups.length);
}