// ==========================================================================
// Event utils
// ==========================================================================

import is from './is';

// Check for passive event listener support
// https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
// https://www.youtube.com/watch?v=NPM6172J22g
const supportsPassiveListeners = (() => {
  // Test via a getter in the options object to see if the passive property is accessed
  let supported = false;
  try {
    const options = Object.defineProperty({}, 'passive', {
      get() {
        supported = true;
        return null;
      },
    });
    window.addEventListener('test', null, options);
    window.removeEventListener('test', null, options);
  }
  catch {}

  return supported;
})();

// Toggle event listener
export function toggleListener(element, event, callback, toggle = false, passive = true, capture = false) {
  // Bail if no element, event, or callback
  if (!element || !('addEventListener' in element) || is.empty(event) || !is.function(callback)) {
    return;
  }

  // Allow multiple events
  const events = event.split(' ');
  // Build options
  // Default to just the capture boolean for browsers with no passive listener support
  let options = capture;

  // If passive events listeners are supported
  if (supportsPassiveListeners) {
    options = {
      // Whether the listener can be passive (i.e. default never prevented)
      passive,
      // Whether the listener is a capturing listener or not
      capture,
    };
  }

  // If a single node is passed, bind the event listener
  events.forEach((type) => {
    if (this && this.eventListeners && toggle) {
      // Cache event listener
      this.eventListeners.push({ element, type, callback, options });
    }

    element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options);
  });
}

// Bind event handler
export function on(element, events = '', callback, passive = true, capture = false) {
  toggleListener.call(this, element, events, callback, true, passive, capture);
}

// Unbind event handler
export function off(element, events = '', callback, passive = true, capture = false) {
  toggleListener.call(this, element, events, callback, false, passive, capture);
}

// Bind once-only event handler
export function once(element, events = '', callback, passive = true, capture = false) {
  const onceCallback = (...args) => {
    off(element, events, onceCallback, passive, capture);
    callback.apply(this, args);
  };

  toggleListener.call(this, element, events, onceCallback, true, passive, capture);
}

// Trigger event
export function triggerEvent(element, type = '', bubbles = false, detail = {}) {
  // Bail if no element
  if (!is.element(element) || is.empty(type)) {
    return;
  }

  // Create and dispatch the event
  const event = new CustomEvent(type, {
    bubbles,
    detail: { ...detail, plyr: this },
  });

  // Dispatch the event
  element.dispatchEvent(event);
}

// Unbind all cached event listeners
export function unbindListeners() {
  if (this && this.eventListeners) {
    this.eventListeners.forEach((item) => {
      const { element, type, callback, options } = item;
      element.removeEventListener(type, callback, options);
    });

    this.eventListeners = [];
  }
}

// Run method when / if player is ready
export function ready() {
  return new Promise(resolve =>
    this.ready ? setTimeout(resolve, 0) : on.call(this, this.elements.container, 'ready', resolve),
  ).then(() => {});
}
