/**
* MangoSpring SiteGroups Presence Plugin Module
* @author : Arnab Chakraborty(arnabc@mangospring.com), Amitesh Kumar(amiteshk@mangospring.com)
* @version: 2.1 ( sitegroups )
* Copyright (c) 2009, MangoSpring Technologies. All rights reserved.
*/

/**
  For the plugin to work properly the following little HTML snippet needs to be on the page, 
    
    <div class="mango-add-presence-plugin mango-presence-plugin-container" buddy_external_id="4411127"></div>
  
  buddy_external_id is the member id of the user.  
 */

( function ( App ) {
    
    var isIE = YAHOO.env.ua.ie;
    var mangospringURL = HOST_PREF; // global var available to use
    
    /**
    * Flag to determine whether there is any presence callback attached or not
    * if no callback is attached then we'll take care of the presence UI
    * otherwise we'll hide the <div>s and will call the callback function
    * whenever there's a change in presence
    */
    var hasPresenceCallback = false;

    /* Global hash to keep reference of markup istances, that can be used later to repaint the marup section. */
    var usrObjRefHashOpt = [];

    //Keep all type of plugins core logic and UI classes in plugin namespace
    
    //Create two namespaces Plugin UI View and Plugin Utility functions
    App.namespace( 'MangoSpring.plugin.ui', 'MangoSpring.plugin.util' );


    // create a static presenceRouting event property
    App.event.SGPresence = {
        /**
        * Presence routing event used to route presence to external callbacks
        */
        routePresence : new App.U.CustomEvent( 'routePresence', this, true, App.U.CustomEvent.FLAT )
    };


    /**
    * @class SGPresence
    * utility class provides Presence plugin implementation for social bar
    * @constructor
    * @module App.plugin
    */
    App.plugin.SGPresence = function ( state ) {
        this.currentState = state;

        this.memorizerTimer  = null;
        this.batchInterval   = 200; //in ms
        this.batchItemCount  = 200;
        this.pollUserIds     = [];
        this.type            = 'E';
        
        this.broadcastUIState  = new App.U.CustomEvent( 'broadcastUIState', this, true, App.U.CustomEvent.FLAT );
        this.onPresenceChange  = new App.U.CustomEvent( 'presenceChange', this, true );
        this.destroyAll        = new App.U.CustomEvent( 'destroyAll', this, true, App.U.CustomEvent.FLAT );

        // clone config object to prevent reference overwriting
        this.config = Ulib.clone( App.plugin.SGPresence.config );

        this._init();
    }
    
    /**
    * Static method helps to listen external callback functions
    * presence routing event, this also turns off UI handling by the
    * plugin itself
    * @param {Function} fn
    * @return {void}
    */
    App.plugin.SGPresence.setPresenceCallback = function ( fn ) {
        // subscribe to routePresence event here
        App.event.SGPresence.routePresence.subscribe( fn );

        // make hasPresenceCallback true
        // it means we'don't print the UI, will just pass
        // the response to callback method
        hasPresenceCallback = true;
    };
    

    App.plugin.SGPresence.prototype = { 
        
        /**
        * Initializes the SGPresence class and sets up memoizing operations
        * @private
        * @return {void}
        */
        _init : function () {
            //create UI for all the plugin item
            var containers = this.getContainers();

            this._createUIMemoizer( containers, 0, this.config );

            App.event.GlobalPresence.onSessionUserStateChange.subscribe( this.onStateChange, this, true );

            if(App.C.get( 'off_state' ) == 'N') {
                this.attachToUserModel();
            }
            
            /*try {
                YAHOO.util.Event.onDOMReady( function() {
                    YAHOO.lang.later( 2000, this, function () {
                        if(MangoSpring.model.users) {
                            //App.log('subscribing to user model update event...');
                            MangoSpring.model.users.onUpdate.subscribe( App.plugin.SGPresence.prototype.updatePresenceOnWeb, this, true );
                        }
                        
                    } );
                });
            } catch(e) {}*/
        },

        
        attachToUserModel : function() {

          try {
              var counter=0;
              var findModelAndAttach = App.L.later(1000, this, function() {
                  App.log('Trying to attach an event to user model update');
                  counter++;
                  if(MangoSpring.model.users) {
                      MangoSpring.model.users.onUpdate.subscribe( App.plugin.SGPresence.prototype.updatePresenceOnWeb, this, true );
                      findModelAndAttach.cancel();
                  } else if(counter > 7) {
                      App.log('Tried for '+ counter +' times. Thus exiting..');
                      findModelAndAttach.cancel();
                  } else {
                      App.log(':: Failed ::');
                  }
              }, null, true);              
          } catch(e) {}
          
        },

        /**
        * Method handles presence updation notifications on web UI
        * @param {Object} rec
        * @return {void}
        */
        updatePresenceOnWeb : function(rec) {
            try {                
                if(rec) {

                    var felixUserId = rec.getId();
                    
                    /*
                      Find all the elements with markup:
                        "<div class="mango-add-presence-plugin mango-presence-plugin-container" ...."
                      NOTE: From the available markup element/object(s), filter out one with
                      buddy_external_id equals felix_user_id in the "rec" object.
                      // Not Needed---
                      var containerClassName  = App.plugin.SGPresence.config.pluginContainerClassName;
                      var container = App.plugin.SGPresence.config.pluginMainContainer;
                      var plgContainers = App.S.query( "div." + containerClassName, container );
                      var reqPlgContainers = App.S.filter(plgContainers, "div[buddy_external_id="+felixUserId+"]");
                    */                    
                            
                    var reqObj = [{'id':felixUserId, 'state':rec.getPresenceMsg().toLowerCase(), 'presence_option_id':rec.getPresenceId()}];
                    
                    var tmuLen = usrObjRefHashOpt.length;
                    tmuLen = tmuLen-1;
                    while ( tmuLen >= 0 ) {
                        //App.log(usrObjRefHashOpt[tmuLen].id+'---'+usrObjRefHashOpt[tmuLen].obj.length);
                        if(usrObjRefHashOpt[tmuLen].id == felixUserId ) {
                            var objLen = usrObjRefHashOpt[tmuLen].obj.length;
                            for(var iObj=0; iObj<objLen; iObj++) {
                                if(usrObjRefHashOpt[tmuLen].obj[iObj]) {
                                    usrObjRefHashOpt[tmuLen].obj[iObj].changePresence('presenceChange', reqObj );
                                }                                
                            }
                            break;
                        }
                        tmuLen--;
                    }//while
                }
                
            } catch(e) {}
        },
    
        /**
         * Method to get the all elements where plugin will be added.
         * @param {String} containerClassName : a container element identifier
         * @param {Object} container          : The parent container which contains the all plugin container
         * @return {Array}                    : An array of elements.
         */
        getContainers : function ( containerClassName, container ) {
            var plgContainers = [];
            try {
                //if not supplied then use default
                if ( !containerClassName ){
                    containerClassName = this.config.pluginContainerClassName;
                }
                
                if ( !container ){
                     container = this.config.pluginMainContainer;
                }
                
                plgContainers = App.S.query( "div." + containerClassName, container );
                
            }catch(e) {                  
            }
            
            return plgContainers;
        },
        
        /**
        * Method creates the memoize batch and also initializes the ui components
        * @private
        * @param {Array} holders
        * @param {Number} startIndex
        * @param {Object} config
        * @return {void}
        */
        _createUIMemoizer : function ( holders, startIndex, config ) {
            try {
                startIndex = startIndex || 0;
                
                //buddy_external_id attribute name also configurable
                var attrName = config.externalIdAttrName,
                    elem     = null, key = null, count = 0, len = holders.length, i = startIndex;
                
                if ( len > 0 ) {
                    // set the type attribute which determines whether user is external or internal
                    // if there are really users to poll then only set this value
                    var elem = holders[0];
                    if ( elem && elem.getAttribute('type') ) {
                        this.type = elem.getAttribute('type');
                    }
                }

                for ( ; i < len; i++ ) {
                    elem = holders[i];
                    
                    //Getting the member id
                    key = elem.getAttribute( attrName );

                    //If member id is not there then don't show plugin. @TODO
                    if ( key)  {
                        config.isYou = ( key == mango_external_uid ) ? true : false;
                        
                        //Create the Plugin UI
                        if ( !hasPresenceCallback ) {
                            var plugin_ui = new App.plugin.ui.SGPresenceUI( elem, { externalId : key, presence : App.config.OFFLINE_STATE, type : this.type }, config, this.currentState );
                            this.broadcastUIState.subscribe( plugin_ui.changeState, plugin_ui, true );
                            this.onPresenceChange.subscribe( plugin_ui.changePresence, plugin_ui, true );
                            this.destroyAll.subscribe( plugin_ui.destroy, plugin_ui, true );
                            
                            /* Used to handle multiple occurance of same user markup on the web page.
                             * Hold on to all the markup instances of a user in a single hash entry.
                            */
                            var isFound = false;
                            var pollLen = this.pollUserIds.length;
                            var iter;
                            for(iter=0;iter<=pollLen;iter++) {
                                if(key == this.pollUserIds[iter]) {
                                    isFound = true;
                                    break;
                                }
                            }//for

                            if(isFound) {
                                var muLen = usrObjRefHashOpt.length;
                                muLen = muLen-1;
                                while ( muLen >= 0 ) {
                                    if(usrObjRefHashOpt[muLen].id == key) {
                                        usrObjRefHashOpt[muLen].obj.push(plugin_ui);
                                        break;
                                    }
                                    muLen--;
                                }

                            } else {
                                usrObjRefHashOpt.push({id : key, obj : [plugin_ui] });
                            }                            
                            
                        } else {
                            // callback is attached so hide our UI's
                            App.D.setStyle( elem, 'display', 'none' );
                        }

                        // store the external id for poll purpose
                        this.pollUserIds.push( key );
                        
                        //Increase item count for the current batch
                        count++;
                    }         
                    
                    //check for batch item count
                    if ( count == this.batchItemCount ) break;                    
                } //for
                 
                startIndex = i + 1;
                
                if ( this.memorizerTimer ){
                    this.memorizerTimer.cancel();
                }

                if ( startIndex < len ) {
                    this.memorizerTimer = App.L.later( this.batchInterval, this, arguments.callee, [ holders, len, startIndex, config ], true );
                }else{
                    this.memorizerTimer = null;
                    this.pollParamsStr = "userid[]=" + this.pollUserIds.join( "&userid[]=" );

                    // add the value to poll parameters
                    this.pollParamsStr += '&type=' + encodeURIComponent( this.type );
                  
                    // start polling for presence
                    this._startPresencePoll();
                    
                }
                
            }catch(e){}
        },
        
        /**
         * Method to start the polling for presence
         * @private
         * @return {void}
         */
        _startPresencePoll : function() {
            try{

                // return if nothing to poll
                if ( this.pollUserIds.length == 0 ) return;

                // if offline then dont need to start presence poll                
                if ( this.currentState == App.config.OFFLINE_STATE ) {
                    // send to all
                    this.routePresenceToExternal( this.pollUserIds, App.config.OFFLINE_STATE );

                    return this.broadcastUIState.fire( App.config.OFFLINE_STATE );
                }

                var callBack = {
                        success :  function( xhr ){
                                    try {
                                        if ( this.presencePoller.getIsCalled() ) {
                                            return;
                                        }
                                        else {
                                            this.presencePoller.setIsCalled( true );
                                        }
                                    }catch(e){}

                                    this.handleXMLResponse( xhr );
                                  } ,
                        
                        failure : function ( xhr ) {
                                    if( this.presencePoller.getIsCalled()){
                                        return;
                                    }else{
                                        this.presencePoller.setIsCalled( true );
                                    }
                                    
                                    this.onStateChange( this.config.OFFLINE_STATE );
                                  },
                        timeout : 2000,
                        scope : this
                    };
                    
                var options = {
                                  pollInterval : 10,
                                  URL          : this.config.presencePollURL,
                                  params       : this.pollParamsStr,
                                  callBack     : callBack,
                                  callTo       : this.config.RoR,
                                  oneTimeOnly  : true,
                                  isCalled     : false
                              };
                
				//Creating the polling instance
                this.presencePoller = new App.plugin.Poller( options );
                this.presencePoller.start();
                
            }catch(e){}
        },
        
        /**
        * Handles the poll presence XML response 
        * @param {XMLHTTPRequest} xhr
        * @param {void}
        */
        handleXMLResponse : function ( xhr ) {

            var modifier = function ( node ) {

                var v = node.nodeValue;
                var obj = { id : v };

                if ( node.parentNode.parentNode.tagName == 'on' ) {
                    obj.state = App.config.ONLINE_STATE;
                    obj.presence_option_id = App.config.ONLINE_ID;                    
                    
                } else if ( node.parentNode.parentNode.tagName == 'off' ) {
                    obj.state = App.config.OFFLINE_STATE;
                    obj.presence_option_id = App.config.OFFLINE_ID;                    
                }

                //Read id attribute in the desired tag of xml to get presence_option_id (if available)
                if(node.parentNode.parentNode.getAttribute('id')) {
                    obj.presence_option_id = node.parentNode.parentNode.getAttribute('id');
                }

                return obj;
            };

            var presence = App.plugin.util.PresenceXMlParser( xhr.responseText, modifier );

            //check for valid xml response, if not valid then set offline
            if ( !presence ) {
                // send to all
                this.routePresenceToExternal( this.pollUserIds, App.config.OFFLINE_STATE );

                this.broadcastUIState.fire( App.config.OFFLINE_STATE ); 
            }

            //broadcast online presence
            var list = presence.online.concat( presence.offline );

            var i = list.length - 1;

            while ( i >= 0 ) {
                this.onPresenceChange.fire( list[i] );

                this.routePresenceToExternal( [ list[i].id ], list[i].state  );

                i--;
            }
        },

        /**
        * Custom event handler listens to the global user state change event and broadcasts them to all
        * plugin ui components accordingly
        * @param {String} state
        * @return {void}
        */
        onStateChange : function ( state ) {
            //broadcast only if the previous state is different
            if ( state !== this.currentState ) {
                //set the new state
                this.currentState = state;

                App.info('should broadcast message....');

                this.broadcastUIState.fire( state );

                this.routePresenceToExternal( this.pollUserIds, state );
            }
        },
        
        /**
        * Method to route presence to external source (callback)
        * @param {Array} users
        * @param {String} state
        * @return {void}
        */
        routePresenceToExternal : function ( users, state ) {
            var i = users.length - 1, o = {};
            while ( i >= 0 ) {
                o = { userid : users[i], state : state };
                // fire presence to external source
                App.event.SGPresence.routePresence.fire( o );

                i--;
            }

        },

        /**
        * Destroys the class instance and unsubscribes all listeners
        * @return {void}
        */
        destroy : function () {
            //unsubscribe all from broadcast 
            this.broadcastUIState.unsubscribeAll();
            this.broadcastUIState = null;

            this.onPresenceChange.unsubscribeAll();
            this.onPresenceChange = null;

            //unsubscribe to all state change activity
            App.event.GlobalPresence.onSessionUserStateChange.unsubscribe( this.onStateChange, this );
                                   
            // tell the plugins to destroy themselves
            this.destroyAll.fire();
            
            // unsubscribe all
            this.destroyAll.unsubscribe();
        }

    }; // End presence plugin
         
    
    /**
     * Config contains all configurational properties. 
     * These properties can be also reset as per requirement in object.
     * @static
     */
    App.plugin.SGPresence.config = {
        //Boolean value to show/hide the presence icon in plugin or not
        showPresenceButton       : true,    
        
        //Boolean value to show/hide the chat now icon in plugin or not
        showChatNowButton        : true,    
        
        // whether to show Presence text ( Online, offline ) or not
        showPresenceText         : true,
        
        // Our default class name by which plugin identify the containers on the page. 
        // Note : Container must be 'div' for now. 
        pluginContainerClassName : 'mango-add-presence-plugin',
        
        //Main container which contains all plugin div
        pluginMainContainer      : document.body,    
        
        // An Attribute with the plugin container to hold the external id of member
        externalIdAttrName : 'buddy_external_id',                
        
        // Chat now and presence icon class name
        chatNowButtonCSS   : 'mango-plugin-chat-now',        
        presenceIconCSS    : 'mango-plugin-presence-icon',
        pluginSpinnerCSS   : 'mango-plugin-spinner',
        pluginStateCSS     : 'mango-plugin-state',
        pluginonlineCSS    : 'mango-plugin-online-state',
        pluginofflineCSS   : 'mango-plugin-offline-state',
        pluginsuspendCSS   : 'mango-plugin-suspend-state',
        
        
        presenceIconURL    : 'http://www.mangospring.com/ce/images/offline.gif',
        chatNowIconURL     : 'http://www.mangospring.com/ce/italki/mycontacts_files/icon_chat.gif',
        chatNowText        : 'Send IM',
        
        presencePollURL    : mangospringURL +'/ce/community/poll_external_presence?action=pollExternalPresence', //HOST_PREF
        pluginSpinnerURL   : 'http://www.mangospring.com/ce/images/spinner.gif',
        loginURL           : mangospringURL + '/ce/user/login.htm',
        
        callToRoR          : 'RoR'        
    };

    
   // ==================================================================
   //     Polling Class
   // ==================================================================

    /**
     * @class Poller
     * Provides Polling logic. It creates an instance to poll for values on the given time interval.
     * @module App.plugin
     * @constructor
     * @param {Hash} options
     */
    App.plugin.Poller = function ( options ) {
        options = options || {};
        
        this.pollTimer       = null ;                             //Reference to current poll
        this.pollInterval    = options.pollInterval || 10000;      //in ms
        this.URL             = options.URL || "";                 //HOST_PREF + '/PresenceServlet?' + params 
        this.callBack        = options.callBack || function(){};
        this.sendMethodType  = options.methodType || "GET";
        this.callTo          = options.callTo || "RoR";
        this.params          = options.params || '';
        this.oneTimeOnly     = options.oneTimeOnly ? (options.oneTimeOnly == true ? false : true) : true;       //false for one time only and true for call it after given time interval.
        this.isCalled        = options.isCalled || false;
    };
    
    App.plugin.Poller.prototype = {
        /**
         * Method to start the polling
         */
        start : function(){
            try{
                this.pollTimer = App.L.later(this.pollInterval, this, this._poll, [], this.oneTimeOnly);                
            }catch(e){}
        },
        
        /**
         * Privete method to send request to fetch the data
         */
        _poll : function(){
            try{

               this.setIsCalled( false );
               
               //for RoR FABridge call
               if ( this.callTo == 'RoR' ){
                   RoR.send( this.URL, this.params ,  this.callBack );
                   
                   //If any problem with the RoR.send(), then poller automatically timeout
                   App.L.later( this.callBack.timeout + 2000, this, function( ) { this.callBack.failure.apply( this.callBack.scope, ["Timeout from presence poll..."] ); } );
                   
               //for Janus FABridge call
               }else if( this.callTo == 'Janus' ){
                   //not needed yet
               //For simple Ajax call
               }else{
                   App.A.asyncRequest( this.sendMethodType, this.URL + this.params , this.callBack );    
               }
            }catch(e){}
        },
        
        /**
         * To Stop the polling
         */
        stop : function(){
            
            if( this.pollTimer ){
                this.pollTimer.cancel();
                this.pollTimer = null;                
            }
        },
        
        setIsCalled : function( flag ){
            this.isCalled = flag;
        },
        
        getIsCalled : function(){
            return this.isCalled;
        },
        
        /**
         * Method to change the poll time interval
         * @param {Number} nInterval : new time interval (in ms)
         * @param {boolean} isRestart : Is restart the polling now?
         */
        setPollInterval : function( nInterval, isRestart ){
            this.pollInterval = nInterval;
            if( isRestart ){
                this.stop();
                this.start();
            }
        },
        
        /**
         * Method to change the callback functions
         * @param {Hash} nCallBack : new call back functions like : {success : function(){}, failure : function(){}, scope : }
         * @param {boolean} isRestart : Is restart the polling now?
         */
        setPollCallBack : function( nCallBack, isRestart ){
            this.callBack = nCallBack;
            if( isRestart ) {
                this.stop();
                this.start();
            }
        },
        
        /**
         * Method to change the poll url
         * @param {String} url
         */
        setURL : function( url ){
            this.URL = url;
        },
        
        /**
         * Method to change the params
         * @param {String} nParams
         */
        setParams : function( nParams ){
            this.params = nParams;
        },
        
        /**
         * Method to get the current URL
         */
        getURL : function(){
            return this.URL;
        }
       
    };
    //End of Polling Class



   // ==================================================================
   //     Presence UI
   // ==================================================================

    /**
     * @class SGPresenceUI
     * Provides SGPresence plugin UI View, represent each atomic UI
     * @module App.plugin.ui
     * @constructor
     * 
     * @param {Object} el         : Container element
     * @param {Hash} keys         : This Hash contains all ids and parameters 
     * @param {Hash} options      : This contains the all configurational properties specific to this plugin object
     * @param {SGPresence} parent : Reference to the parent class
     */
    App.plugin.ui.SGPresenceUI = function ( el, keys, options, uiState ) {
        try {
            this.containerElem  = el;
            this.keys           = keys || {};
            this.config         = Ulib.clone( options );
            this.spinnerObj     = null;
            this.uiState        = uiState;

            this.userPresence   = keys.presence || App.config.OFFLINE_STATE;
            this.userPresenceId = keys.presenceId || App.config.OFFLINE_ID;

            //If container element given then create UI
            if ( el ) {
                //add Float fix class
                App.D.addClass( el, 'mango-clearfix' );

                //Create UI
                this._createUI( el, options );
            }
        }catch(e) {
        }
    };
    
    App.plugin.ui.SGPresenceUI.prototype = {
        /**
         * Method to create the Plugin UI
         * @return {void}
         */
        _createUI : function() {
            // paint the ui
            this._paint();
            //display the spinner object
            this._showSpinner();
        },
        
        /**
        * Low-level method to paint the UI 
        * @param {HTMLElement} el
        * @param {Object} options
        */
        _paint : function () {
            var el = this.containerElem;
            var options = this.config;

            // clear the container before writing
            this._removeAction();
            el.innerHTML = '';

            // whether to show chat now link
            //var canChatToUser = ( this.userPresence == App.config.ONLINE_STATE || this.userPresence == 'busy');
            var canChatToUser = ( this.userPresenceId == App.config.ONLINE_ID || this.userPresenceId == App.config.BUSY_ID || this.userPresenceId == App.config.IDLE_ID);            
            var showChatNow = ( this.uiState == App.config.ONLINE_STATE && canChatToUser && options.showChatNowButton === true );
            
            //App.log('In Paint :: showChatNow ::'+ canChatToUser+'--'+showChatNow + '\n isYou: '+options.isYou);

            if ( options.isYou ) {
                domBuilder( el )
                    .div().addClass( options.presenceIconCSS )
                        .img( { align : 'absmiddle', title : this.getPresenceText(), src : this.getPresenceIcon() } ).end()
                    .end()
                    .div().addClass( ' mango-its-me' )
                        .text( '[Me]' ).end( options.presenceIconCSS );
            } else {
                var node = domBuilder( el )
                            .div().addClass( options.presenceIconCSS )
                                .img( { align : 'top', title : this.getPresenceText(), src : this.getPresenceIcon() } ).end()
                            .end();

                if ( options.showChatNowButton ) {
                    
                    node = domBuilder( node.currentNode )
                            .div({ /*id : 'mango-chat-now-' + this.keys.externalId*/ }).addClass( options.chatNowButtonCSS + ' mango-clearfix' )
                                .on( 'click', this.chatNow, null, this )
                                /*.img( { align : 'absmiddle', src : options.chatNowIconURL, style : 'display:' + ( showChatNow ? '' : 'none' ) } ).end()*/
                                .a( { href : '#', style : 'display:' + ( showChatNow ? '' : 'none' ) } ).text( options.chatNowText ).end()
                            .end();
                }

                node = domBuilder( node.currentNode )
                            .div({ style : 'display:' + ( showChatNow || options.showPresenceText === false ? 'none' : '' ) })
                                .addClass( options.pluginStateCSS ).text( this.getPresenceText() );
                            
            }

            var spinnerParams = { id : this.keys.externalId };            
            this._createSpinner( spinnerParams );
        },
        
        /**
        * Creates the spinner object
        * @param {Object} spinnerParams
        * @return {void}
        */
        _createSpinner : function( spinnerParams ){
            this.spinnerObj = new App.plugin.ui.Spinner( this.containerElem, spinnerParams ); 
        },
        
        /**
        * Method opens the one-o-one chat with the person clicked
        * @param {Event} e 
        * @return {void}
        */
        chatNow : function ( e ) {
            App.E.stopEvent( e );            
            // Allow user to chat for "online,busy & idle" status.
            //if ( this.userPresence == App.config.ONLINE_STATE ) {
            if ( this.userPresenceId == App.config.ONLINE_ID || this.userPresenceId == App.config.BUSY_ID || this.userPresenceId == App.config.IDLE_ID) {
                // this method should exist otherwise code will fail
                up_launchWM( App.config.SessionConfig.get( 'felix_id' ) /* crap */ , this.keys.externalId, this.keys.type );
            }
        },

        /**
         * Method to remove action listener from the buttons
         */
        _removeAction : function () {
            //Removing Action from chat now button

            /*var elem = App.$( 'mango-chat-now-' + this.keys.externalId );
            if ( elem ) {
                App.E.removeListener( elem, 'click', this.chatNow );
            }*/
            
            var chatNowSect = App.S.query( 'div.mango-plugin-chat-now', this.containerElem, true);            
            if ( chatNowSect ) {
                App.E.removeListener( chatNowSect, 'click', this.chatNow );
            }
        },
        
        /**
         * Method to destroy the plugin
         * @return {void}
         */
        destroy : function () {
            this._removeAction();
            this.containerElem.innerHtml = "";

            if ( this.spinnerObj ) {
                this.spinnerObj.destroy();
            }
        },

        /**
        * Returns the presence icon based on the current state
        * @return {String}
        */
        getPresenceIcon : function () {
            return this.config.presenceIconURL.replace( 'offline', this._getPresenceString() );
        },
        
        /**
        * Returns the presence text according to the presence of the current user
        * @return {String}
        */
        getPresenceText : function () {
            return App.helper.capitalize( this._getPresenceString() );
        },

        /*
        *Returns state string based on presence option id
        * @return (string)
        */
       _getPresenceString : function() {

           switch(parseInt(this.userPresenceId)) {
               case 2:
                   return App.config.OFFLINE_STATE
               case 3:
                   return App.config.ONLINE_STATE                
               case 4:
                   return 'busy';
               case 5:
                   return 'offline';
               case 6:
                   return 'idle';
               default:
                   return 'offline';
           }
       },
        
        /**
        * Shows the spinner object
        * @return {void}
        */
        _showSpinner : function () {
            if ( this.spinnerObj ) {
                this.spinnerObj.show();
            }
        },
        
        /**
        * Listens to the UI State change event of the SGPresence class
        * @param {String} type
        * @param {Array} args
        * @return {void}
        */
        changeState : function ( state ) {
            //set the new state
            this.uiState = state;
            
            // if the UI state is offline then make everyone as offline
            if ( this.uiState == App.config.OFFLINE_STATE ) {
                this.userPresence = App.config.OFFLINE_STATE;
                this.userPresenceId = App.config.OFFLINE_ID;
            }
            
            //repaint the UI to reflect the changes
            this._paint();
        },
        
        /**
        * Listens to the Presence change event of the SGPresence class
        * @param {String} type
        * @param {Array} args
        * @return {void}
        */
        changePresence : function ( type, args ) {
            var obj = args[0];            

            // if not meant for this user then return
            if ( obj.id != this.keys.externalId ) return;

            // forcefully set the state online for session user
            // because initiallly server doesn't set the presence value for session user
            if ( this.config.isYou && App.config.SessionConfig.get( 'presence_text' ) == App.config.ONLINE_STATE ) {
                obj.state = App.config.ONLINE_STATE;
            }
            
            // change the user presence
            this.userPresence = obj.state;
            this.userPresenceId = obj.presence_option_id;

            //repaint the UI to reflect the changes
            this._paint();
        }
        
    }; //end of presence UI Class

    
    /**
     * @class Spinner
     * Provides new sppiner UI
     * @module App.ui
     * @constructor
     * 
     * @param {object} relativeElem : element ID|object
     * @param {Hash} options      : options
     */
    App.plugin.ui.Spinner = function ( relativeElem, options ) {
         try {
             options = options || {};
             
             this.relativeElem     = relativeElem;
             this.hAlign           = options.hAlign;
             this.vAlign           = options.vAlign;
             this.spinnerClass     = options.spinnerClass || this.spinnerClass;
             this.spinnerIdPrefix  = options.spinnerIdPrefix || this.spinnerIdPrefix;
             this.spinnerId        = options.id; 
             
             this._create();

        }catch(e) {
        }
    };
    
    App.plugin.ui.Spinner.prototype = {
        spinnerClass     : 'mango-plugin-spinner',
        spinnerIdPrefix  : 'mango_spinner_',
        spinnerUrl       : 'http://www.mangospring.com/ce/images/spinner.gif',
        
        /**
         * Method to create the spinner UI and set the position
         */
        _create : function(){
            var Dom     = YAHOO.util.Dom;
            var h_align = this.hAlign;
            var v_align = this.vAlign;
            
            var relativeElem = relativeElem = Dom.get(this.relativeElem);;
            
    		try{
    			if(relativeElem == null){
    				return;
    			}
                
    			var spinnerDiv               = document.createElement("div");
                    spinnerDiv.id            = this.spinnerIdPrefix + this.spinnerId;
                    spinnerDiv.className     = this.spinnerClass;
                    spinnerDiv.style.display = 'none';
                    spinnerDiv.innerHTML     = '<table border="0" cellpadding="0" cellspacing="0" height="100%" width="100%"> <tr> <td id="spinner_img_container' + this.spinnerId  + '" align="center" valign="middle"> <img id="spinner_image' + this.spinnerId + '" align="absmiddle" src="' + this.spinnerUrl + '" > <td> </tr> </table>';
    		        
                    relativeElem.appendChild(spinnerDiv);
                    
    			var wtImgContainer = Dom.get( 'spinner_img_container' + this.spinnerId );
                
    			if( !h_align ){
    				h_align = ''
    			}
    			if( !v_align ){
    				v_align = ''
    			}
    			if( h_align == '' && v_align == '' ){
    				h_align = "center";
    				v_align = "middle";
    			}
    			wtImgContainer.setAttribute( "align", h_align );
    			wtImgContainer.setAttribute( "valign", v_align );
    
                
    			var pos = Dom.getXY( relativeElem );
    			var region = Dom.getRegion( relativeElem );
    			
                Dom.setXY( spinnerDiv, pos, true );
                
    			var width  = region.right - region.left;
    			var height = region.bottom - region.top;
    			var left   =  region.left;
    			var top    =  region.top;
    
    			if( h_align.toLowerCase() == 'right' ){
    				width = width + 20; 						
    			}else if( h_align.toLowerCase() == 'left' ){
    				width = width + 20; 						
    				left = left - 20;
    				if ( left < 0 ){
    					left = 0;
    				}
    			}
    
    			if( v_align.toLowerCase() == 'top' ){
    				top     = top - 20;
    				height  = height + 20;
    				if ( top < 0 ){
    					top = 0;
    				}
    			}else if( v_align.toLowerCase() == 'bottom' ){
    				height = height + 20;
    			}
                
    			spinnerDiv.style.width    = width + "px";
    			spinnerDiv.style.height   = height + "px";
    			spinnerDiv.style.left     = left + "px";
    			spinnerDiv.style.top      = top + "px";
                spinnerDiv.style.position = 'absolute';
                
                Dom.setStyle(spinnerDiv, 'opacity', 0.8);
                
                this.spinner = spinnerDiv;
                
            }catch(e){}
        },
        
        /**
         * Method to show the spinner
         */
        show : function (){
           if ( this.spinner ){
                this.spinner.style.display = 'block';            
            } 
        },
        
        /**
         * Method to hide the spinner 
         */
        hide : function (){
            if ( this.spinner ) {
                this.spinner.style.display = 'none';            
            }
        },
        
        /**
         * Method to remove the spinner
         */
        destroy : function(){
            if( this.spinner ){
                var prt = this.spinner.parentNode;
                prt.removeChild(this.spinner);
            }
        }
    };
    //End of spinner class



   // ==================================================================
   //     Utility Methods
   // ==================================================================
    /**
    * Method parses the XML Presence response(using XPath) to an object with list of online/offline users
    * @param {String} response
    * @param {Function} modifier
    * @return {Object}
    * @dependency XMPP-Parser.js
    */
    App.plugin.util.PresenceXMlParser = function ( resposne, modifier ) {
        // parse and load the XML string
        try {
            var xDoc = new XMLDoc( resposne );
        }catch(ex) {
            return null;
        }
        
        /**
        * Inner function used to filter out the response for offline/online ids
        * @param {XPathResult} nodeSet
        * @param {Function} fn - Optional Test function if provided the it will be used 
        *                        to test the node value against
        * @return {Array}
        */
        var getNodeValues = function ( nodeSet, fn ) {
            // exit if not proper object
            if ( !nodeSet ) return [];

            var i = nodeSet.length - 1, result = [], v;
            while ( i >= 0 ) {
                v = nodeSet[i].nodeValue;

                // test against the specified fn if provided
                if ( App.L.isFunction( fn ) ) result.push( fn( nodeSet[i] ) );
                else
                    result.push( v );

                i--;
            }

            return result;
        };

        // get online users
        var online_users = getNodeValues( 
                                    xDoc.selectNodes( '/' + xDoc.root.tagName + '/on/u/text()' ), modifier
                                );
        // get offline users
        var offline_users = getNodeValues( 
                                    xDoc.selectNodes( '/' + xDoc.root.tagName + '/off/u/text()' ), modifier
                                );
        
        return { online : online_users, offline : offline_users };
    };

    /**
    * Subscribe to AppInit event for the initialization of the plugin
    * @dependency boot.js
    */
    App.event.AppInit.subscribe( function ( type, args ) { 
        var resp = args[0];
        
        // exit if not a valid response
        if ( !resp ) {
            return;
        }

        
        // check what type of plugin we're going to render
        if ( typeof mango_presence_type !== 'undefined' ) {
            
            if ( mango_presence_type == 0 ) {                
                App.plugin.SGPresence.config.showChatNowButton = false;
                App.plugin.SGPresence.config.showPresenceText  = false;
            }
            else if ( mango_presence_type == 1 ) {
                App.plugin.SGPresence.config.showChatNowButton = false;
            }
        }
            
        
        // create Presence class instance
        var presence = new App.plugin.SGPresence( resp.uiState );        
        // subscribe to the presenceChange event of the PresenceMgr which actually
        // broadcasts presence information from Socket
        if ( App.net.presenceMgr.presenceChange ) {
            // shortcut
            var PM = App.net.presenceMgr;

            PM.presenceChange.subscribe( 
                function ( type, args ) {
                    //create the desired object format which onPresenceChange listeners can understand
                    var obj = { id : args[0].record.external_id, state : PM.getPresenceText( args[0].presence ) };
                    
                    // now fire the Plugin's presenceChange event                   
                    presence.onPresenceChange.fire( obj );
                    
                    // route presence to external callbacks
                    presence.routePresenceToExternal( [ obj.id ], obj.state );

                }
            );
        }

        App.event.AppKill.subscribe( function () {
            presence.destroy();
            
            App.event.AppKill.unsubscribe( arguments.callee );
        } );
    } );


} )( MangoSpring );
