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(chatContainer.getAttribute('_loginUrl'), {chatServerDomain:chatDomain}, 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);
                    }
                }
                createRefreshTimer(data.sessionTTL);
            });
        },
        refreshSessionTimer,
        clearRefreshTimer = function(){
            if (refreshSessionTimer) {
                clearTimeout(refreshSessionTimer);
                refreshSessionTimer = undefined;
            }
        },
        createRefreshTimer = function (sessionTTL){
            clearRefreshTimer();
            //refresh session 5 min before TTL runs out to prevent interruption of service
            refreshSessionTimer = setTimeout(refreshSession, (sessionTTL - 300)*1000);
        },
        /**Gets a new session token from the chat server by calling the login end point again*/
        refreshSession = function() {
            x$.getJSON(chatContainer.getAttribute('_loginUrl'), {chatServerDomain:chatDomain}, function(data) {
                if (data.result != 'ok') {
                    return;
                }
                token = data.token;
                createRefreshTimer(data.sessionTTL);
            });
        };

    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();
            clearRefreshTimer();
            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();
                    clearRefreshTimer();
                    goOffline();
                }
                return false;
            });
            if (online) {
                retrieveChatServer(logIntoChat);
            }
        }
    };
})();