function getElFromArrByProp(arr,prop,str) {
  for (var i=0; i<arr.length; i++) {
    if (arr[i][prop] == str)
      return arr[i];
  }
  return null;
}

function RosterSort(a,b) {
  return (a.name.toLowerCase()<b.name.toLowerCase())?-1:1;
}

var RosterGroup = Class.create();

RosterGroup.prototype = {
    initialize: function(name) {
      this.name = name;
      this.users = new Array();
      this.onlUserCount = 0;
      this.messagesPending = 0;
    }
}

var RosterUser = Class.create();

RosterUser.prototype = {
    initialize: function(jid,subscription,groups,name,jabberServer) {
    
    	this.fulljid = jid;
      this.jid = cutResource(jid) || 'unknown';
      this.jid = this.jid.toLowerCase(); // jids are case insensitive
    
      this.subscription = subscription || 'none';
      this.groups = groups || [''];
    
      if (name)
        this.name = name;
      else if (this.jid == jabberServer)
        this.name = "System";
      else if ((this.jid.indexOf('@') != -1) && this.jid.substring(this.jid.indexOf('@')+1) == jabberServer) // we found a local user
        this.name = this.jid.substring(0,jid.indexOf('@'));
      else
        this.name = this.jid;
    
      this.name = htmlEnc(this.name);
    
      // initialise defaults
      this.status = (this.subscription == 'from' || this.subscription == 'none') ? 'stalker' : 'unavailable';
      this.statusMsg = null;
      this.lastsrc = null;
      this.messages = new Array();
      this.chatmsgs = new Array();
      this.chatW = null; // chat window
    },
    
    add2Group: function(group) {
      this.groups = this.groups.concat(group);
    }    
}

var Rosters = new Object();

var Roster = Class.create();

Roster.prototype = {
    initialize: function(items,targetId,jabberServer,dbg) {
    	Rosters[targetId] = this;
    	this.id = targetId;
    	
		this.users = new Array();
		this.groups = new Array();
		this.disco = new Object();
		this.agents = new Object();
		this.rosterW = document;
		
		this.hiddenGroups = new Array();
		this.name = targetId;
		this.dbg = dbg;
    
		this.jabberServer = jabberServer;
		this.jabber = Jabber.cache[this.id];   	
		this.jabber.observe(this.jabber.addChatMsg, this.handleChat.bind(this));
		this.jabber.observe(this.jabber.addMsg, this.handleMessage.bind(this));
    	
		/* setup groups */
		this.addUsers(items);
    },

	handleChat: function(user, msg) {
	},
	
	handleMessage: function(user, msg) {
		user.messages = user.messages.concat(msg);
	},
	
    getGroupByName: function(groupName) {
      return getElFromArrByProp(this.groups,"name",groupName);
    },

    getUserByJID: function(jid) {
      return getElFromArrByProp(this.users,"jid",jid.toLowerCase());
    },

    updateStyleIE: function() {
    //  if(!is.ie)
    //    return;
    //  this.rosterW.getElementById("roster").style.width = this.rosterW.body.clientWidth;
    },
    
    getUserIcons: function(from) {
      var images = new Array();
      
      if (this.rosterW) {
    	  for (var i=0; i<this.groups.length; i++) {
    		var img = this.rosterW.images[from+"/"+this.groups[i].name];
    		if (img) {
    		  images = images.concat(img);
    		  continue; // skip this group
    		}
    	  }
      }
      return images;
    },
    
    toggleHide: function() {
      this.usersHidden = !this.usersHidden;
      this.print();
      return;
    },
    	
    setHide: function(flag) {
      this.usersHidden = flag;
      this.print();
      return;
    },

    setEmptyGroups: function(flag) {
      this.emptyGroups = flag;
      this.print();
      return;
    },
    	
    toggleGrp: function(name) {
      var el = $(name);
      if (el.className == 'hidden') {
        el.className = 'rosterGroup';
        this.hiddenGroups[name] = false;
        this.rosterW.images[name+"Img"].src = grp_open_img.src;
      } else {
        el.className = 'hidden';
        this.hiddenGroups[name] = true;
        this.rosterW.images[name+"Img"].src = grp_close_img.src;
      }
      this.updateStyleIE();
    },
    
    openMessage: function(jid) {
      var user = this.getUserByJID(jid);
      var wName = makeWindowName(user.jid); 
    
      if (user.messages.length > 0 && (!user.mW || user.mW.closed)) // display messages
        user.mW = open('/javascripts/jwchat/message.html?jid='+escape(jid)+"&rosterid="+this.id,"mw"+wName,'width=360,height=270,dependent=yes,resizable=yes');
      else if (!user.sW || user.sW.closed) // open send dialog
        user.sW = open("/javascripts/jwchat/send.html?jid="+escape(jid)+"&rosterid="+this.id,"sw"+wName,'width=320,height=200,dependent=yes,resizable=yes');
      return false;
    },
    
    openChat: function(jid) {   
      var user = this.getUserByJID(jid);
    
    	if (!user)
    		return;
    
    	if (user.messages.length > 0 && (!user.mW || user.mW.closed)) // display messages
    		this.openMessage(jid);
    		
      if (!user.chatW || user.chatW.closed)
        user.chatW = open("/javascripts/jwchat/chat.html?jid="+escape(jid)+"&rosterid="+this.id,"chatW"+makeWindowName(user.jid),"width=320,height=390,resizable=yes");
      else if (user.chatW.popMsgs)
        user.chatW.popMsgs();
    },
    
	openSubscriptionHandler: function(el) {
	   this.openSubscription(this.jabber.elToId(el));
	},
	
	/*
	function: openSubscription
	parameters:
		aJid - jabber id of user to subscribe to (optional)
	 */
	openSubscription: function(aJid) {
		this.subw = open(this.jabber.makeURL("subscription.html",{jid:aJid}),"sub","width=320,height=240,resizable=yes");
		this.subw.focus();
		return false;
	},

	openOptions: function() {
	  if (!this.ow || this.ow.closed)
		this.ow = open(this.jabber.makeURL("options.html"),"ow"+makeWindowName(this.jabber.jid),"width=380,height=380,resizable=yes");
	  this.ow.focus();
	  return false;
	},

	openRegistrations: function() {
	  if (!this.or || this.or.closed)
		this.or = open(this.jabber.makeURL("register.html"),"or"+makeWindowName(this.jabber.jid),"width=380,height=380,resizable=yes");
	  this.or.focus();
	  return false;
	},

	openUserPropsHandler: function(el) {
	   this.openUserProps(this.jabber.elToId(el));
	},
	
	openUserProps: function(aJid) {
	  open(this.jabber.makeURL("userprops.html",{jid:aJid}),"uProps"+makeWindowName(aJid),"width=480,height=360,resizable=yes");
	  return false;
	},
		
	openUserNoteHandler: function(el) {
	   this.openUserNote(this.jabber.elToId(el));
	},
	
	openUserNote: function(aJid) { /* store annotations to a user */
		var user = this.getUserByJID(aJid);
	
		if (user == null)
			return; // unbelievable
	
		if (!user.noteW || user.noteW.closed)
			user.noteW = open(this.jabber.makeURL("usernote.html",{jid:aJid}),"noteW"+makeWindowName(aJid),"width=300,height=200,resizable=yes,scrollbars=no");
		user.noteW.focus();
	},
	
	removeUserHandler: function(el) {
	   var userId = this.jabber.elToId(el);
	   if (confirm("Remove user " + userId + "?"))
		   this.removeUser(userId);
	},
	
	removeUser: function(aJid) {
		// get fulljid
		var fulljid = this.getUserByJID(aJid).fulljid;
		var iq = new JSJaCIQ();
		iq.setType('set');
		var query = iq.setQuery('jabber:iq:roster');
		var item = query.appendChild(iq.getDoc().createElement('item'));
		item.setAttribute('jid',fulljid);
		item.setAttribute('subscription','remove');
	
		this.jabber.con.send(iq);
	},
	
    cleanUp: function() {
		if (this.subw && !this.subw.closed)
			this.subw.close();
		
		if (typeof(this.ow) != 'undefined' && this.ow && !this.ow.closed)
			this.ow.close();
		
		if (typeof(this.or) != 'undefined' && this.or && !this.or.closed)
			this.or.close();
		
      for (var i=0; i<this.users.length; i++) {
        if (this.users[i].roster)
          this.users[i].roster.cleanUp();
        if (this.users[i].sW)
          this.users[i].sW.close();
        if (this.users[i].mW)
          this.users[i].mW.close();
        if (this.users[i].chatW)
          this.users[i].chatW.close();
        if (this.users[i].histW)
          this.users[i].histW.close();
      }
    },
    
    updateGroupsForUser: function(user) {
      for (var j=0; j<user.groups.length; j++) {
    	if (user.groups.length > 1 && user.groups[j] == '')
    	  continue;
    	var groupName = (user.groups[j] == '') ? "Unfiled" : user.groups[j];
    	var group = this.getGroupByName(groupName);
    	if(group == null) {
    	  group = new RosterGroup(groupName);
    	  this.groups = this.groups.concat(group);
    	}
    	group.users = group.users.concat(user);
      }
    },
    	
    updateGroups: function() {
      this.groups = new Array();
      for (var i=0; i<this.users.length; i++)
        this.updateGroupsForUser(this.users[i]);
    },
    
    addUser: function(user) {
      this.users = this.users.concat(user);
    	
      // add to groups
      this.updateGroupsForUser(user);
      return user;
    },
    
    removeUser: function(user) {
      var uLen = this.users.length;
      for (var i=0; i<uLen; i++) {
        if (user == this.users[i]) {
          this.users = this.users.slice(0,i).concat(this.users.slice(i+1,uLen));
          break;
        }
      }
      this.updateGroups();
    },
    
    getGroupchats: function() {
    	var groupchats = new Array();
        for (var i=0; i<this.users.length; i++)
    		if (this.users[i].roster)
    			groupchats[groupchats.length] = this.users[i].jid+'/'+this.users[i].roster.nick;
    	return groupchats;
    },
    
    getServices: function() {
        // Just use agents for now. Ignore disco.
        return this.agents;
    },
    
    isGateway: function(aJid) {
    	aJid = cutResource(aJid);
    	for (var i in this.agents)
    		if (i == aJid)
    			if (this.agents[i].transport)
    				return true;
    
    	for (var i in this.disco)
    		if (i == aJid)
    			if (this.disco[i].getNode().getElementsByTagName('identity').item(0)) {
    				if (this.disco[i].getNode().getElementsByTagName('identity').item(0).getAttribute('category') == 'gateway')
    					return true;
    			}
    
    	return false;
    },
    
    addUsers: function(items) {
    	if (!items)
    		return;
    
      for (var i=0;i<items.length;i++) {
        /* if (items[i].jid.indexOf("@") == -1) */ // no user - must be a transport
    	var jid = items.item(i).getAttribute('jid');
        if (typeof(jid) == 'undefined')
          continue;
        var name = items.item(i).getAttribute('name') || cutResource(items.item(i).getAttribute('jid'));
    		var groups = new Array('');
    		if (this.isGateway(jid)) {
    			groups = groups.concat('~gateways');
    		} else {
    			for (var j=0;j<items.item(i).childNodes.length;j++)
    				if (items.item(i).childNodes.item(j).nodeName == 'group')
    					groups = groups.concat(items.item(i).childNodes.item(j).firstChild.nodeValue);
    		}
        this.addUser(new RosterUser(jid,items.item(i).getAttribute('subscription'),groups,name,this.jabberServer));
      }
    },
    
	showMsg: function(msg) {
	   $('roster_'+this.id).innerHTML = msg;
	},
	
	hideMsg: function() {
	},
	
    print: function() {
      /* update user count for groups */
      for (var i=0; i<this.groups.length; i++) {
        this.groups[i].onlUserCount = 0;
        this.groups[i].messagesPending = 0;
        for (var j=0; j<this.groups[i].users.length; j++) {
          if (this.groups[i].users[j].status != 'unavailable' && this.groups[i].users[j].status != 'stalker')
            this.groups[i].onlUserCount++;
          if (this.groups[i].users[j].lastsrc)
            this.groups[i].messagesPending++;
        }
      }
    	
      var rosterHTML = '';
      
      this.groups = this.groups.sort(RosterSort);
      
    	/* ***
    	 * loop rostergroups 
    	 */
      for (var i=0; i<this.groups.length; i++) {
        var rosterGroupHeadClass = (this.emptyGroups && this.usersHidden && this.groups[i].onlUserCount == 0 && this.groups[i].messagesPending == 0) ? 'rosterGroupHeaderHidden':'rosterGroupHeader';
        rosterHTML += "<div id='"+this.groups[i].name+"Head' class='"+rosterGroupHeadClass+"' onClick='Rosters[\""+this.id+"\"].toggleGrp(\""+this.groups[i].name+"\");'><nobr>";
        var toggleImg = (this.hiddenGroups[this.groups[i].name])?'images/group_close.gif':'images/group_open.gif';
        rosterHTML += "<img src='/javascripts/jwchat/"+toggleImg+"' name='"+this.groups[i].name+"Img'> ";
        rosterHTML += this.groups[i].name+ " (<span id='"+this.groups[i].name+"On'>"+this.groups[i].onlUserCount+"</span>/" + this.groups[i].users.length + ")";
        rosterHTML += "</nobr></div>";
        var rosterGroupClass = ((this.usersHidden && this.groups[i].onlUserCount == 0 && this.groups[i].messagesPending == 0) || this.hiddenGroups[this.groups[i].name])?'hidden':'rosterGroup';
        rosterHTML += "<div id='"+this.groups[i].name+"' class='"+rosterGroupClass+"'>";
        
        this.groups[i].users = this.groups[i].users.sort(RosterSort);
    
    		/* ***
    		 * loop users in rostergroup 
    		 */
        for (var j=0; j<this.groups[i].users.length; j++) {
          var user = this.groups[i].users[j];
          var rosterUserClass = (this.usersHidden && (user.status == 'unavailable' || user.status == 'stalker') && !user.lastsrc) ? "hidden":"rosterUser";
          rosterHTML += "<div id='"+user.jid+"/"+this.groups[i].name+"Entry' class='"+rosterUserClass+"' onClick=\"return userClicked(this,'"+this.id+"','"+user.jid+"');\" title=\""+user.name;
    			if (user.realjid)
    				rosterHTML += "&#10;JID: "+ user.realjid;
    			else
    				rosterHTML += "&#10;JID: "+ user.jid;
    			rosterHTML += "&#10;"+"Status"+": "+user.status;
          if (user.statusMsg)
            rosterHTML += "&#10;"+"Message"+": " + user.statusMsg;
    			if ((user.messages.length + user.chatmsgs.length) > 0)
    				rosterHTML += "&#10;" + ""+(user.messages.length + user.chatmsgs.length)+" message(s) pending";
          rosterHTML += "\"><nobr>";
    
          var userImg = (user.lastsrc) ? messageImg : eval(user.status + "Led");
          rosterHTML += "<img src='"+userImg.src+"' name='"+user.jid+"/"+this.groups[i].name+"' width=16 height=16 border=0 align='left'>";
          rosterHTML += "<div><span id=\"nickName\" class=\"Jabber"+user.status+"\">";
          if (user.name.indexOf('@msn') != -1 && user.statusMsg)
            rosterHTML += user.statusMsg;
    	  else
    	  	rosterHTML += user.name;
          rosterHTML += "</span></div></nobr></div>";
        } /* END inner loop */
        rosterHTML += "</div>";
      }
      var rosterDiv = $('roster_'+this.id);
      if (rosterDiv)
    	rosterDiv.innerHTML = rosterHTML;
      this.updateStyleIE();
      Scroller.ids["body_"+this.id].resetScrollbar(true);
    }
}

/***********************************************************************
 * GROUPCHAT ROSTER
 *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */
function GCRosterSort(a,b) {
  return (a.name.toLowerCase()<b.name.toLowerCase())?-1:1;
}

var GroupchatRosterUser = Class.create();

GroupchatRosterUser.prototype = {
    initialize: function(jid,name) {
      this.base = RosterUser;
      this.base(jid,'',[''],name);
      this.jid = this.fulljid; // always use fulljid
      this.affiliation = 'none';
      this.role = 'none';
    },
    
    add2Group: function(group) {
      this.groups = [group];
    }
}

var GroupChatRoster = Class.create();

GroupChatRoster.prototype = {
    initialize: function(targetW) {  
      this.base = Roster;
      this.base(null);
      this.usersHidden = true;
      this.emptyGroups = true;
    
      this.targetW = targetW.frames.groupchatRoster;
    
      this.rosterW = this.targetW.groupchatIRoster.document;
    
      this.name = 'GroupchatRoster';
    },
    
    getRealJIDByNick: function(nick) {
        for (var i=0; i<this.users.length; i++)
    		if (this.users[i].name == nick)
    			return this.users[i].realjid;
    	return null;
    },
    
    getFullJIDByNick: function(nick) {
        for (var i=0; i<this.users.length; i++)
    		if (this.users[i].name == nick)
    			return this.users[i].fulljid;
    	return null;
    },
    			
    getUserByJID: function(jid) {
    	// need to search fulljid here
      return getElFromArrByProp(this.users,"fulljid",jid);
    },

    openGroupchat: function(aJid,nick,pass) {
    	pass = pass || '';
    	nick = nick || '';
    
      var user = this.getUserByJID(aJid);
      if(!user) {
    	user = this.addUser(new RosterUser(aJid,'',["Chat Rooms"],aJid.substring(0,aJid.indexOf('@'))));
    		user.type = 'groupchat';
    	this.print();
      }
    	if (!user.chatW || user.chatW.closed)
    		user.chatW = open("/javascripts/jwchat/groupchat.html?jid="+escape(aJid)+"&nick="+escape(nick)+"&pass="+escape(pass),"gchatW"+makeWindowName(aJid+"/"+nick),"width=520,height=390,resizable=yes");
    	user.chatW.focus();
    },
    
    print: function() {
      var rosterHTML = '';
      
      this.groups = this.groups.sort(GCRosterSort);
    
    	/* ***
    	 * loop rostergroups 
    	 */
      for (var i=0; i<this.groups.length; i++) {
        var rosterGroupHeadClass = (this.groups[i].users.length == 0) ? 'rosterGroupHeaderHidden':'rosterGroupHeader';
        rosterHTML += "<div id='"+this.groups[i].name+"Head' class='"+rosterGroupHeadClass+"'><nobr> ";
        rosterHTML += this.groups[i].users.length + " " + this.groups[i].name;
        rosterHTML += "</nobr></div>";
        rosterHTML += "<div id='"+this.groups[i].name+"' class='rosterGroup'>";
        
        this.groups[i].users = this.groups[i].users.sort(RosterSort);
    
    		/* ***
    		 * loop users in rostergroup 
    		 */
        for (var j=0; j<this.groups[i].users.length; j++) {
          var user = this.groups[i].users[j];
          var rosterUserClass = (this.usersHidden && (user.status == 'unavailable' || user.status == 'stalker') && !user.lastsrc) ? "hidden":"rosterUser";
          rosterHTML += "<div id='"+user.jid+"/"+this.groups[i].name+"Entry' class='"+rosterUserClass+"' onClick=\"return userClicked(this,'"+this.id+"','"+user.jid+"');\" title=\""+user.name;
    			if (user.realjid)
    				rosterHTML += "&#10;JID: "+ user.realjid;
    			else
    				rosterHTML += "&#10;JID: "+ user.jid;
    			rosterHTML += "&#10;"+"Status"+": "+user.status;
          if (user.statusMsg)
            rosterHTML += "&#10;"+"Message"+": " + user.statusMsg;
    			if ((user.messages.length + user.chatmsgs.length) > 0)
    				rosterHTML += "&#10;" + ""+(user.messages.length + user.chatmsgs.length)+" message(s) pending";
          rosterHTML += "\"><nobr>";
          
          var userImg = (user.lastsrc) ? messageImg : eval(user.status + "Led");
          rosterHTML += "<img src='/javascripts/jwchat/"+userImg.src+"' name='"+user.jid+"/"+this.groups[i].name+"' width=16 height=16 border=0 align='left'>";
          rosterHTML += "<div><span class=\"nickName\">"+user.name+"</span>";
          if (user.statusMsg)
            rosterHTML += "<br clear=all><nobr><span class=\"statusMsg\">"+user.statusMsg+"</span></nobr>";
          rosterHTML += "</div></nobr></div>";
        } /* END inner loop */
        rosterHTML += "</div>";
      }
    
      var rosterDiv = $('roster_'+this.id);
      if (rosterDiv)
    	rosterDiv.innerHTML = rosterHTML;
      this.updateStyleIE();
    }


}

// some images - no idea why they are defined here

var messageImg = new Image();
messageImg.src = "/javascripts/jwchat/images/message.gif";
var grp_open_img = new Image();
grp_open_img.src = '/javascripts/jwchat/images/group_open.gif';
var grp_close_img = new Image();
grp_close_img.src = '/javascripts/jwchat/images/group_close.gif';
var arrow_right_blinking = new Image();
arrow_right_blinking.src = '/javascripts/jwchat/images/arrow_right_blinking.gif';

function userClicked(el,rosterId,jid) {
	var roster = Rosters[rosterId];
	
	//roster.selectUser(el);

	var user = roster.getUserByJID(cutResource(jid));

	if(user && typeof(user.type) != 'undefined' && user.type == 'groupchat')
		return roster.openGroupchat(jid);

	if (!roster.isGateway(jid))
		return roster.openChat(jid);
}
