/**
 * Provides for repetitive polling of the server at distinct {@link #interval intervals}.
 * The initial request for data originates from the client, and then is responded to by the
 * server.
 * 
 * Configuration for the PollingProvider can be generated by the server-side
 * API portion of the Ext Direct stack.
 *
 * An instance of PollingProvider may be created directly via the new keyword or by simply
 * specifying `type = 'polling'`. For example:
 *
 *      var pollA = new Ext.direct.PollingProvider({
 *          type:'polling',
 *          url: 'php/pollA.php',
 *      });
 *      Ext.direct.Manager.addProvider(pollA);
 *      pollA.disconnect();
 *      
 *      Ext.direct.Manager.addProvider({
 *          type:'polling',
 *          url: 'php/pollB.php',
 *          id: 'pollB-provider'
 *      });
 *      var pollB = Ext.direct.Manager.getProvider('pollB-provider');
 *
 */
Ext.define('Ext.direct.PollingProvider', {
    extend: 'Ext.direct.JsonProvider',
    alias: 'direct.pollingprovider',
    
    requires: [
        'Ext.Ajax',
        'Ext.util.TaskRunner',
        'Ext.direct.ExceptionEvent'
    ],
    
    type: 'polling',
    
    /**
     * @cfg {Number} [interval=3000]
     * How often to poll the server-side in milliseconds. Defaults to every 3 seconds.
     */
    interval: 3000,
 
    /**
     * @cfg {Object} [baseParams]
     * An object containing properties which are to be sent as parameters on every
     * polling request. Note that if baseParams are set and {@link #url} parameter
     * is an URL string, poll requests will use POST method instead of default GET.
     */
    
    /**
     * @cfg {String/Function} url
     * The url which the PollingProvider should contact with each request. This can also be
     * an imported Ext Direct method which will be passed baseParams as named arguments.
     *
     * @deprecated 5.1.0 Using Function `url` is deprecated, please use {@link #pollFn} instead
     */
    
    /**
     * @cfg {String/Function} pollFn
     *
     * Ext Direct method to use for polling. If a method name is provided as a string,
     * the actual function will not be resolved until the first time this provider
     * is connected.
     *
     * The method should accept named arguments and will be passed {@link #baseParams}
     * if set.
     */
    
    /**
     * @cfg {Number} [timeout]
     *
     * The timeout to use for each request.
     */
    
    /**
     * @event beforepoll
     * @preventable
     * Fired immediately before a poll takes place.
     *
     * @param {Ext.direct.PollingProvider} this 
     */
 
    /**
     * @event poll
     * Fired immediately after a poll takes place.
     *
     * @param {Ext.direct.PollingProvider} this 
     */
    
    constructor: function(config) {
        var me = this;
        
        me.callParent([config]);
        
        me.pollTask = Ext.TaskManager.newTask({
            run: me.runPoll,
            interval: me.interval,
            scope: me
        });
    },
    
    destroy: function() {
        this.pollTask.stop(true);
        this.callParent();
    },
    
    doConnect: function() {
        var me = this,
            url = me.url,
            pollFn = me.pollFn;
        
        // It is important that pollFn resolution happens at the time when
        // Provider is first connected, and not at construction time. If
        // pollFn is configured as a string, the API stub may not exist yet
        // when PollingProvider is constructed.
        if (pollFn && Ext.isString(pollFn)) {
            //<debug>
            var fnName = pollFn; // eslint-disable-line vars-on-top, one-var
            //</debug>
            
            me.pollFn = pollFn = Ext.direct.Manager.parseMethod(pollFn);
            
            //<debug>
            if (!Ext.isFunction(pollFn)) {
                Ext.raise("Cannot resolve Ext Direct API method " + fnName +
                                " for PollingProvider");
            }
            //</debug>
        }
        else if (Ext.isFunction(url)) {
            //<debug>
            Ext.log.warn('Using a function for url is deprecated, use pollFn instead.');
            //</debug>
            
            me.pollFn = pollFn = url;
            me.url = url = null;
        }
        
        if (url || pollFn) {
            me.setInterval(me.interval);
            
            me.pollTask.start();
        }
    },
 
    doDisconnect: function() {
        if (this.pollTask) {
            this.pollTask.stop();
        }
    },
    
    getInterval: function() {
        return this.pollTask && this.pollTask.interval;
    },
    
    setInterval: function(interval) {
        var me = this,
            pollTask = me.pollTask;
        
        //<debug>
        if (interval < 100) {
            Ext.raise("Attempting to configure PollProvider " + me.id +
                            " with interval that is less than 100ms.");
                            
        }
        //</debug>
        
        me.interval = pollTask.interval = interval;
        
        if (me.isConnected()) {
            pollTask.restart(interval);
        }
    },
    
    /**
     * @private
     */
    runPoll: function() {
        var me = this,
            url = me.url,
            pollFn = me.pollFn,
            baseParams = me.baseParams,
            args, request;
        
        if (me.fireEvent('beforepoll', me) !== false) {
            if (pollFn) {
                args = pollFn.directCfg.method.getArgs({
                    params: baseParams !== undefined ? baseParams : {},
                    callback: me.onPollFn,
                    scope: me
                });
                
                pollFn.apply(window, args);
            }
            else {
                request = {
                    url: url,
                    callback: me.onData,
                    scope: me,
                    params: baseParams,
                    headers: me.getHeaders()
                };
                
                if (me.timeout != null) {
                    request.timeout = me.timeout;
                }
                
                me.sendAjaxRequest(request);
            }
            
            me.fireEvent('poll', me);
        }
    },
 
    /**
     * @private
     */
    onData: function(opt, success, response) {
        var me = this,
            i, len, events, event;
        
        if (success) {
            events = me.createEvents(response);
            
            for (= 0, len = events.length; i < len; ++i) {
                event = events[i];
                
                me.fireEvent('data', me, event);
                
                if (!event.status) {
                    me.fireEvent('exception', me, event);
                }
            }
        }
        else {
            event = new Ext.direct.ExceptionEvent({
                data: null,
                code: Ext.direct.Manager.exceptions.TRANSPORT,
                message: 'Unable to connect to the server.',
                xhr: response
            });
            
            me.fireEvent('data', me, event);
            me.fireEvent('exception', me, event);
        }
        
        me.callParent([opt, success, response]);
    },
    
    /**
     * @private
     */
    onPollFn: function(result, event, success, options) {
        this.onData(null, success, { responseText: result });
    },
    
    inheritableStatics: {
        /**
         * @private
         * @static
         * @inheritable
         */
        checkConfig: function(config) {
            // Polling provider needs either URI or pollFn
            return config && config.type === 'polling' &&
                   (config.url || config.pollFn);
        }
    }
});