(function attachSafeSceneSkipController(root) {
  "use strict";

  var Common = root.SafeSceneCommon || require("./common.js");

  function noop() {}

  function buildRangesSignature(ranges) {
    if (!Array.isArray(ranges) || !ranges.length) {
      return "";
    }

    return ranges.map(function mapRange(range) {
      return [
        range.id || "",
        Number(range.startSeconds || 0),
        Number(range.endSeconds || 0),
        range.category || ""
      ].join("|");
    }).join("||");
  }

  function SkipController(video, options) {
    this.video = video;
    this.settings = Common.clone(Common.DEFAULT_SETTINGS);
    this.ranges = [];
    this.skippedRanges = new Set();
    this.notifiedBuckets = new Map();
    this.onNotify = options && options.onNotify ? options.onNotify : noop;
    this.onSkip = options && options.onSkip ? options.onSkip : noop;
    this.lastObservedTime = 0;
    this.rangesSignature = "";
    this.boundTick = this.tick.bind(this);

    if (options && options.settings) {
      this.setSettings(options.settings);
    }

    if (options && Array.isArray(options.ranges)) {
      this.setRanges(options.ranges);
    }

    if (this.video && typeof this.video.addEventListener === "function") {
      this.video.addEventListener("timeupdate", this.boundTick);
      this.video.addEventListener("play", this.boundTick);
      this.video.addEventListener("seeking", this.boundTick);
    }
  }

  SkipController.prototype.setSettings = function setSettings(settings) {
    this.settings = Object.assign({}, this.settings, settings || {});
  };

  SkipController.prototype.setRanges = function setRanges(ranges) {
    var nextRanges = Array.isArray(ranges) ? ranges.slice().sort(function sortRanges(left, right) {
      return left.startSeconds - right.startSeconds;
    }) : [];
    var nextSignature = buildRangesSignature(nextRanges);

    this.ranges = nextRanges;

    if (nextSignature !== this.rangesSignature) {
      this.rangesSignature = nextSignature;
      this.skippedRanges.clear();
      this.notifiedBuckets.clear();
    }
  };

  SkipController.prototype.destroy = function destroy() {
    if (this.video && typeof this.video.removeEventListener === "function") {
      this.video.removeEventListener("timeupdate", this.boundTick);
      this.video.removeEventListener("play", this.boundTick);
      this.video.removeEventListener("seeking", this.boundTick);
    }
  };

  SkipController.prototype.resetIfPlaybackRestarted = function resetIfPlaybackRestarted(currentTime) {
    if (currentTime + 15 < this.lastObservedTime) {
      this.skippedRanges.clear();
      this.notifiedBuckets.clear();
    }
    this.lastObservedTime = currentTime;
  };

  SkipController.prototype.tick = function tick() {
    if (!this.video || !this.ranges.length) {
      return null;
    }

    var currentTime = Number(this.video.currentTime || 0);
    this.resetIfPlaybackRestarted(currentTime);

    for (var index = 0; index < this.ranges.length; index += 1) {
      var range = this.ranges[index];
      var notifyLead = Number(this.settings.notifyBeforeSkipSeconds || 0);
      var leadIn = Number(this.settings.leadInSeconds || 0);
      var notifyWindowStart = Math.max(0, range.startSeconds - notifyLead);
      var skipWindowStart = Math.max(0, range.startSeconds - leadIn);

      if (currentTime >= notifyWindowStart && currentTime < range.startSeconds) {
        var secondsUntil = Math.ceil(range.startSeconds - currentTime);
        var previousBucket = this.notifiedBuckets.get(range.id);
        if (previousBucket !== secondsUntil) {
          this.notifiedBuckets.set(range.id, secondsUntil);
          this.onNotify({
            range: range,
            secondsUntil: secondsUntil
          });
        }
      }

      if (
        this.settings.autoSkipEnabled &&
        currentTime >= skipWindowStart &&
        currentTime < range.endSeconds &&
        !this.skippedRanges.has(range.id)
      ) {
        var seekTarget = range.endSeconds + Number(this.settings.resumePaddingSeconds || 0);
        this.video.currentTime = seekTarget;
        this.skippedRanges.add(range.id);
        this.onSkip({
          range: range,
          seekTarget: seekTarget
        });
        return {
          skipped: true,
          range: range,
          seekTarget: seekTarget
        };
      }
    }

    return null;
  };

  var api = {
    SkipController: SkipController
  };

  root.SafeSceneSkipController = api;

  if (typeof module !== "undefined" && module.exports) {
    module.exports = api;
  }
}(typeof globalThis !== "undefined" ? globalThis : this));
