(function () {
  "use strict";
  /**
   * @typedef {Function} SchedulePrerequisite
   * @return {boolean}
   */
  /**
   * @typedef {{func:Function,[priority]:number,[pre]:SchedulePrerequisite}} ScheduleTask
   */
  var lateMaxDelay = 4;
  var lateDelay = lateMaxDelay;
  /** @type {Array.<ScheduleTask>} */
  var eeArray = [];
  /** @type {Array.<ScheduleTask>} */
  var lateArray = [];
  /** @type {Array.<ScheduleTask|null>} */
  var prerequisiteArray = [];

  /** @param {ScheduleTask} a
   * @param {ScheduleTask} b
   */
  var sortFunction = function (a, b) {
    return b.priority - a.priority;
  };

  var tick = function () {
    var count1 = 0;
    if (eeArray.length > 0) {
      if (EASY.schedule.eePassed) {
        count1 = eeArray.length;
        eeArray.sort(sortFunction);
        for (var i = 0; i < eeArray.length; i++) {
          eeArray[i].func();
        }
        eeArray = [];
        lateDelay = lateMaxDelay;
      } else {
        return;
      }
    }
    var count2 = executePrerequisites();
    var count3 = 0;
    if (lateArray.length) {
      lateDelay--;
      if (lateDelay === 0) {
        count3 = lateArray.length;
        lateArray.sort(sortFunction);
        for (i = 0; i < lateArray.length; i++) {
          lateArray[i].func.call(window);
        }
        lateArray = [];
        lateDelay = lateMaxDelay;
      }
    }
    if (EASY.schedule.out&& (count1 || count2 || count3)) {
      console.log("EE: " + count1 + " REQ: " + count2 + " LATE: " + count3);
    }
  };
  var isNotNull = function (a) {
    return a !== null;
  };
  var executePrerequisites = function () {
    var count = 0;
    for (var i = 0; i < prerequisiteArray.length; i++) {
      if (prerequisiteArray[i].pre()) {
        count++;
        prerequisiteArray[i].func.call(window);
        prerequisiteArray[i] = null;
      }
    }
    if (count) {
      prerequisiteArray = prerequisiteArray.filter(isNotNull);
      count += executePrerequisites();
    }
    return count;
  };

  var cycle = function scheduleCycle() {
    tick();
    window.requestAnimationFrame(cycle);
  };
  document.addEventListener("DOMContentLoaded", cycle);

  /**
   *
   * @type {{eePassed: boolean, late: EASY.schedule.late, require: EASY.schedule.require, afterEE: EASY.schedule.afterEE}}
   */
  EASY.schedule = {
    eePassed: false,
    out: false,
    /**
     *
     * @param {Function} func
     * @param {number} [priority]
     */
    late: function (func, priority) {
      lateArray.push({func: func, priority: priority || 0})
    },
    /**
     *
     * @param {Function} func
     * @param {SchedulePrerequisite} prerequisite
     */
    require: function (func, prerequisite) {
      prerequisiteArray.push({func: func, pre: prerequisite})
    },
    /**
     *
     * @param {Function} func
     * @param {number} [priority]
     */
    afterEE: function (func, priority) {
      eeArray.push({func: func, priority: priority || 0})
    }
  };
})();

