/**
 * PeriodicalUpdater - jQuery plugin for timed, decaying ajax calls
 *
 * https://www.360innovate.co.uk/blog/2009/03/periodicalupdater-for-jquery/
 * https://nofluffjuststuff.com/blog/robert_fischer/2009/06/jquery_periodicalupdater.html
 *
 * Copyright (c) 2009 by the following:
 *  Frank White (https://customcode.info)
 *  Robert Fischer (https://blog.enfranchisedmind.com/posts/author/candide/?coming_from=smokejumperit.com)
 *  360innovate (https://www.360innovate.co.uk)
 *
 * Dual licensed under the MIT and GPL licenses:
 * https://www.opensource.org/licenses/mit-license.php
 * https://www.gnu.org/licenses/gpl.html
 *
 */

(function($) {
    var pu_log = function(msg) {
      try {
        console.log(msg);
      } catch(err) {}
    }

    // Now back to our regularly scheduled work
    $.PeriodicalUpdater = function(url, options, callback, autoStopCallback){
      var settings = jQuery.extend(true, {
          url: url,         // URL of ajax request
          cache: false,     // By default, don't allow caching
          method: 'GET',    // method; get or post
          data: '',         // array of values to be passed to the page - e.g. {name: "John", greeting: "hello"}
          minTimeout: 1000, // starting value for the timeout in milliseconds
          maxTimeout: 8000, // maximum length of time between requests
          multiplier: 2,    // if set to 2, timerInterval will double each time the response hasn't changed (up to maxTimeout)
          maxCalls: 0,      // maximum number of calls. 0 = no limit.
          autoStop: 0       // automatically stop requests after this many returns of the same data. 0 = disabled
        }, options);

        // set some initial values, then begin
        var timer         = null;
        var timerInterval = settings.minTimeout;
        var maxCalls      = settings.maxCalls;
        var autoStop      = settings.autoStop;
        var calls         = 0;
        var noChange      = 0;
        var originalMaxCalls = maxCalls;

        var reset_timer = function(interval) {
          if (timer != null) {
            clearTimeout(timer);
          }
          timerInterval = interval;
          pu_log('resetting timer to '+ timerInterval +'.');
          timer = setTimeout(getdata, timerInterval);
        }

        // Function to boost the timer
        var boostPeriod = function() {
          if(settings.multiplier >= 1) {
            before = timerInterval;
            timerInterval = timerInterval * settings.multiplier;

            if(timerInterval > settings.maxTimeout) {
              timerInterval = settings.maxTimeout;
            }
            after = timerInterval;
            pu_log('adjusting timer from '+ before +' to '+ after +'.');
            reset_timer(timerInterval);
          }
        };

        // Construct the settings for $.ajax based on settings
        var ajaxSettings = jQuery.extend(true, {}, settings);
        if(settings.type && !ajaxSettings.dataType) ajaxSettings.dataType = settings.type;
        if(settings.sendData) ajaxSettings.data = settings.sendData;
        ajaxSettings.type = settings.method; // 'type' is used internally for jQuery.  Who knew?
        ajaxSettings.ifModified = true;

        var handle = {
                    restart: function() {
                        maxCalls = originalMaxCalls;
                        calls = 0;
                        reset_timer(timerInterval);
                        return;
                    },
          stop: function() {
            maxCalls = -1;
            return;
          }
        };

        // Create the function to get data
        // TODO It'd be nice to do the options.data check once (a la boostPeriod)
        function getdata() {
          var toSend  = jQuery.extend(true, {}, ajaxSettings); // jQuery screws with what you pass in
          if(typeof(options.data) == 'function') {
            toSend.data = options.data();
            if(toSend.data) {
              // Handle transformations (only strings and objects are understood)
              if(typeof(toSend.data) == "number") {
                toSend.data = toSend.data.toString();
              }
            }
          }

          if(maxCalls == 0) {
            $.ajax(toSend);
          } else if(maxCalls > 0 && calls < maxCalls) {
            $.ajax(toSend);
            calls++;
          }
        }

        // Implement the tricky behind logic
        var remoteData  = null;
        var prevData    = null;

        ajaxSettings.success = function(data) {
          pu_log("Successful run! (In 'success')");
          remoteData      = data;
          // timerInterval   = settings.minTimeout;
        };

        ajaxSettings.complete = function(xhr, success) {
          //pu_log("Status of call: " + success + " (In 'complete')");
          if(maxCalls == -1) return;
          if(success == "success" || success == "notmodified") {
            var rawData = $.trim(xhr.responseText);
            if(rawData == 'STOP_AJAX_CALLS') {
                            handle.stop();
              return;
            }
            if(prevData == rawData) {
              if(autoStop > 0) {
                noChange++;
                if(noChange == autoStop) {
                                    handle.stop();
                  if(autoStopCallback) autoStopCallback(noChange);
                  return;
                }
              }
              boostPeriod();
            } else {
              noChange        = 0;
              reset_timer(settings.minTimeout);
              prevData        = rawData;
              if(remoteData == null) remoteData = rawData;
              // jQuery 1.4+ $.ajax() automatically converts "data" into a JS Object for "type:json" requests now
              // For compatibility with 1.4+ and pre1.4 jQuery only try to parse actual strings, skip when remoteData is already an Object
              if((ajaxSettings.dataType === 'json') && (typeof(remoteData) === 'string') && (success == "success")) {
                remoteData = JSON.parse(remoteData);
              }
              if(settings.success) { settings.success(remoteData, success, xhr, handle); }
              if(callback) callback(remoteData, success, xhr, handle);
            }
          }
          remoteData = null;
        }


        ajaxSettings.error = function (xhr, textStatus) {
          //pu_log("Error message: " + textStatus + " (In 'error')");
          if(textStatus != "notmodified") {
            prevData = null;
            reset_timer(settings.minTimeout);
          }
          if(settings.error) { settings.error(xhr, textStatus); }
        };

        // Make the first call
        $(function() { reset_timer(timerInterval); });

        return handle;
    };
})(jQuery);
