dojo.provide('xg.chat.Base');

(function() {
	var doc = document,
		getDocHeight = function() {
			return doc.innerHeight || (doc.documentElement ? doc.documentElement.clientHeight : (doc.body ? doc.body.clientHeight : 0));
		},
		/**
		 *  Sets the cookie
		 *
		 *  @param name	string
		 *  @param value string
		 *  @param expires int		Expiration in seconds or 0 for the session TTL
		 *  @return     void
		 */
		setCookie = function(name, value, expires) {
			doc.cookie = name + '=' + encodeURIComponent(value) + (expires ? ';Expires='+(new Date((new Date).getTime()+expires*1000)).toGMTString() : '') + ';Path=/;Domain='+window.location.host;
			return true;
		},
		getCookie = function(name) {
			var cookies = doc.cookie.split('; ')
			for (var i = 0;i<cookies.length;i++) {
				var parts = cookies[i].split('=',2);
				if (parts[0] == name) return decodeURIComponent(parts[1])
			}
		},
		privateMessages = [],
		appId,
		appName,
		userId,
		chatDomain,
		randomizedChatDomain,
		token,
		roomId,
		chatContainer,

		chatLoadingState = 0, // 0 - not loaded, 1 - loading, 2 - loaded
		loadChat = function(expand) {
			var ac = x$('#appletContainer');
			/*
			 *	 The logic here is:
			 *	 	- if you started loading chat in collapsed mode, we load it collapsed, but if you
			 *	 	  expanded chat while it was loading, we expand it.
			 *	 	- if you started loading chat in expanded mode, we always load it expanded.
			 */
			if (expand) {
				chatContainer.style.display = 'none';
				xg.chat.Base.setChatExpandedMode(true);
				xg.chat.Base.expandChatDiv();
			}
			if (chatLoadingState > 0) return;
			chatLoadingState = 1;
			if (!expand) {
				ac.css('left','-10000px');
			}
			ac.css('display','').css('visibility','visible');
			ac[0].innerHTML = chatContainer.getAttribute('_chatEmbed');
		},
		/*
		 *	Workaround for a weird Firefox bug, when while 1 script is loading (polling script) no
		 *	other scripts can be executed and their execution is delayed until polling script finishes.
		 *	To work this around, we create a separate iframe, which we use for the polling only.
		 */
		pollIframe = undefined,
		getPollJSON = function(url, args, callback) {
			if (!x$.browser.mozilla) {
				return x$.getJSON(url, args, callback); // Thank you, all-other-browsers!
			}
			if ('undefined' == typeof pollIframe) {
				pollIframe = 'loading';
				var iframe = document.createElement('iframe');
				iframe.name = 'chatPoll';
				iframe.id = 'chatPoll';
				iframe.src = '/xn_resources/widgets/chat/poll.html'; // We need a document on the same domain to access the iframe's content
				iframe.onload = function() {
					pollIframe = iframe;
					getPollJSON(url, args, callback);
				}
				iframe.style.left = iframe.style.top = iframe.style.height = iframe.style.width = "1px";
				iframe.style.visibility = "hidden";
				xg.append(iframe);
			} else if ('string' == typeof pollIframe) {
				// iframe was created and still loading. just ignore this call.
			} else {
				// The default jQuery's getJSON won't work, as we need to work with the iframe document
				var now = (new Date).getTime(),
					jsonp = 'jsonp' + parseInt(now * Math.random()),
					w = pollIframe.contentWindow,
					head = w.document.getElementsByTagName("head")[0],
					script = w.document.createElement("script");

				w[jsonp] = function(data) {
					w[ jsonp ] = undefined;
					try{ delete w[ jsonp ]; } catch(e){}
					head.removeChild(script);
					callback(data);
				}
				args._ = now; // Prevent caching
				args = x$.param(args); // Serialize arguments into a string
				url += (url.match(/\?/) ? "&" : "?") + args;
				script.src = url.replace(/\bc=\?/,'c='+jsonp);
				head.appendChild(script);
			}
		},
		retrieveChatServer = function(callback) {
			if (chatDomain = xg.chat.Base.getChatServer(true)) {
				callback();
			} else {
				x$.getJSON('http://'+chatContainer.getAttribute('_chatServer')+'/xn/redirector/redirect?c=?', {a:appId}, function(data) {
					xg.chat.Base.setChatServer(chatDomain = data.domain);
					callback();
				});
			}
		},
		updateUserCount = function(count) {
			x$('.xj_info', chatContainer).html(appName+' Chat | '+count+' Online');
		},

		updatePresenceTimer,
		updatePresence = function() {
			x$.getJSON('http://'+chatDomain+'/xn/presence/count?c=?', {r:roomId, t:token, a:appId, i:userId}, function(data) {
				// Ok, we successfully got the # of users, update the count and schedule a next run
				updateUserCount(data.count);
				if (chatLoadingState == 0) {
					updatePresenceTimer = setTimeout(updatePresence, 30*1000);
				}
			});
		},
		stopUpdatingPresence = function() {
			if (updatePresenceTimer) {
				clearTimeout(updatePresenceTimer);
				updatePresenceTimer = undefined;
			}
		},

		listeningForMessages = 0,
		listenForMessages = function() {
			listeningForMessages = 1;
			getPollJSON('http://'+chatDomain+'/xn/groupchat/poll?c=?', {r:roomId, t:token, a:appId, i:userId}, function(data) {
				if (data.messages) {
					for (var i = 0;i<data.messages.length;i++) {
						if (data.messages[i].type == 'private') {
							privateMessages.push(data.messages[i]);
						}
					}
					if (data.messages.length) {
						loadChat(false);
					}
				}
				if (chatLoadingState != 2) {
					// Keep listening for messages even if chat is loading
					listenForMessages();
				}
			});
		},
		logIntoChat = function() {
			x$.getJSON('http://'+chatDomain+'/xn/presence/login?c=?', {a:appId, t:appId+userId, i:userId, user:chatContainer.getAttribute('_userInfo')}, function(data) {
				if (data.result != 'ok') {
					return;
				}
				roomId = data.roomId;
				token = data.token;
				updateUserCount(data.count);
				if (chatLoadingState == 0) {
					stopUpdatingPresence();
					updatePresenceTimer = setTimeout(updatePresence, 30*1000);
					if (!listeningForMessages) {
						setTimeout(listenForMessages, 50);
					}
				}
			});
		}

    xg.chat.Base = {
        /**
         *  Flash callback which is executed when the flash chat is loaded and ready for interaction.
         *  Gives a chance to JS part to cleanup things.
         *
         *  @return     void
         */
        chatIsLoaded: function() {
        	chatLoadingState = 2;
        	stopUpdatingPresence();
			chatContainer.style.display = 'none';
			x$('#appletContainer').css('visibility','visible').css('left','');
        },

        /**
         *  Returns the user online status (true - online, false - offline). Default is "true"
         *
         *  @return bool
         */
        getOnlineStatus: function() {
			var s = getCookie('xg_chatOnline');
			return typeof s == 'undefined' ? true : (s == '1' ? true : false);
        },

        /**
         *  Updates the user online status. Returns true if status was updated, false otherwise.
         *  Must be called when user changes his or her online status.
         *
		 *  @param    isOnline    bool    New online status: true - online, false - offline.
         *  @return    bool
         */
        setOnlineStatus: function(isOnline) {
			return setCookie('xg_chatOnline', isOnline ? '1' : '0');
        },

        /**
         *  Returns the preferred chat server domain. Returns undefined if there is no preferred domain.
         *  Redirector service must be contacted in this case to get the domain.
         *
		 *  @param noRandom	bool	Do not randomize chat server name
         *
         *  @return string
         */
        getChatServer: function(noRandom) {
        	var s = getCookie('xg_chatServer');
        	if (noRandom) {
        		return s;
			}
			// BAZ-17896: Randomize the domain name to avoid hitting the browser limits on number of connections to the same domain.
			// Flash chat doesn't close polling connections properly right now [Andrey 2009-08-05]
			if (!randomizedChatDomain) {
				randomizedChatDomain = s.replace(/^(\w+)((\.[a-z][\w-]+){3})$/i,''+((new Date).getTime()&0XFFFF)+'$2');
			}
			return randomizedChatDomain;
        },

        /**
         *  Updates the preferred chat server domain. Returns true if serverDomain was updated, false otherwise.
         *
         *    @param    serverDomain    string    Chat server domain name
         *  @return    bool
         */
        setChatServer: function(serverDomain) {
        	if (serverDomain != randomizedChatDomain) {
        		randomizedChatDomain = serverDomain;
				return setCookie('xg_chatServer', serverDomain, 3600);
			}
			return true;
        },

        /**
         *  Returns true if chat should is expanded or false otherwise. Default is "false"
         *
         *  @return bool
         */
        getChatExpandedMode: function() {
			var s = getCookie('xg_chatExp');
			return typeof s == 'undefined' ? false : (s == '1' ? true : false);
        },

        /**
         *  Updates the chat expanded status. Returns true if status was updated, false otherwise.
         *  Must be called when user expands or collapses the chat window.
         *
         *    @param    isExpanded    bool    New status: true - expanded, false - collapsed.
         *  @return    bool
         */
        setChatExpandedMode: function(isExpanded) {
			return setCookie('xg_chatExp', isExpanded ? '1' : '0');
        },

        /**
         *  Must be called by the flash widget once after chatIsLoaded().
         *  Returns the list of pending private messages that JS version accumulated (if any).
         *  Each message has the same structure as message from groupchat/poll endpoint.
         *
         *  @return array of messages
         */
        getPendingPrivateMessages: function() {
        	return privateMessages;
        },

        /**
         *  Expands the chat div. setChatExpandedMode() must be called after that.
         *
         *  @return     void
         */
        expandChatDiv: function() {
            //timeout required to avoid conflicts with Safari/IE
            setTimeout(function() {
                var elem = doc.getElementById("appletContainer");
                var height = getDocHeight();
                elem.style.height = (height && height<=425) ? (height-20)+"px" : "485px";
            }, 50);
        },

        /**
         *  Collapses the chat div. setChatExpandedMode() must be called after that.
         *
         *  @return     void
         */
        contractChatDiv: function() {
            //timeout required to avoid conflicts with Safari/IE
            setTimeout(function() {
                var elem = doc.getElementById("appletContainer");
                elem.style.height = "22px";
            }, 50);
		},
		start: function() {
			if (!(chatContainer = doc.getElementById('xj_chatContainer'))) {
				return;
			}

			appId = ning.CurrentApp.id;
			appName = ning.CurrentApp.name.length > 40 ? ning.CurrentApp.name.substr(0,37)+'...' : ning.CurrentApp.name;
			userId = ning.CurrentProfile ? ning.CurrentProfile.id : '';

			var load = function() { loadChat(true); return false };
			if (xg.chat.Base.getChatExpandedMode()) return load();
			x$('.xj_expand', chatContainer).click(load);
			x$('.xj_info', chatContainer).click(load);
			x$('.xj_window', chatContainer).click(function(){
				window.open('/chat/index/popOutWindow',"chat"+appId,"height=485,width=480,toolbar=no,scrollbars=no,resizable=yes");
				return false;
			});
			var online = xg.chat.Base.getOnlineStatus(), statusNode = x$('.xj_status', chatContainer);
			var goOffline = function() {
				statusNode.addClass('xg_status-offline');
				x$('.xj_info', chatContainer).html(appName+' Chat | Disconnected');
			}
			if (!online) goOffline();
			statusNode.click(function(){
				var newOnline = !xg.chat.Base.getOnlineStatus();
				xg.chat.Base.setOnlineStatus(newOnline);
				if (newOnline) {
					statusNode.removeClass('xg_status-offline');
					retrieveChatServer(logIntoChat);
				} else {
					stopUpdatingPresence();
					goOffline();
				}
				return false;
			});
			if (online) {
				retrieveChatServer(logIntoChat);
			}
		}
    };
})();
