/*! @license
 * Shaka Player
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.polyfill.Orientation');

goog.require('shaka.log');
goog.require('shaka.util.FakeEvent');
goog.require('shaka.util.FakeEventTarget');
goog.require('shaka.polyfill');


/**
 * @summary A polyfill for systems that do not implement screen.orientation.
 * For now, this only handles systems that implement the deprecated
 * window.orientation feature... e.g. iPad.
 * @export
 */
shaka.polyfill.Orientation = class {
  /**
   * Install the polyfill if needed.
   * @export
   */
  static install() {
    if (screen.orientation && screen.orientation.unlock) {
      // Not needed.
      shaka.log.info('Using native screen.orientation');
      return;
    }

    if (screen.orientation != undefined) {
      // There are some platforms where screen.orientation is defined but
      // incomplete (e.g. Safari).
      // Install a very simple polyfill in that case.
      shaka.polyfill.Orientation.installBasedOnScreenMethods_();
    } else if (window.orientation != undefined) {
      // There is no way to check to see if the 'orientationchange' event exists
      // on window, which could theoretically lead to this making a
      // screen.orientation object that doesn't actually work.
      // However, it looks like all platforms that support the deprecated
      // window.orientation feature also support that event.
      shaka.polyfill.Orientation.installBasedOnWindowMethods_();
    }
  }

  /**
   * Modifies screen.orientation to add no-ops for missing methods.
   * Meant for cases where screen.orientation is defined, but has missing
   * methods that cannot be properly polyfilled.
   * @private
   */
  static installBasedOnScreenMethods_() {
    if (screen.orientation.lock === undefined) {
      screen.orientation.lock = (orientation) => {
        shaka.log.info('screen.orientation.lock is a no-op');
        return Promise.resolve();
      };
    }
    if (screen.orientation.unlock === undefined) {
      screen.orientation.unlock = () => {
        shaka.log.info('screen.orientation.unlock is a no-op');
      };
    }
  }

  /**
   * Makes a polyfill for orientation, based on window methods.
   * Note that some of the features this is based on are deprecated, so this
   * will not necessarily work on all platforms.
   * @private
   */
  static installBasedOnWindowMethods_() {
    const orientation = new shaka.polyfill.Orientation.FakeOrientation();
    screen.orientation = /** @type {!ScreenOrientation} */ (orientation);
    const setValues = () => {
      switch (window.orientation) {
        case -90:
          orientation.type = 'landscape-secondary';
          orientation.angle = 270;
          break;
        case 0:
          orientation.type = 'portrait-primary';
          orientation.angle = 0;
          break;
        case 90:
          orientation.type = 'landscape-primary';
          orientation.angle = 90;
          break;
        case 180:
          orientation.type = 'portrait-secondary';
          orientation.angle = 180;
          break;
      }
    };

    setValues();
    window.addEventListener('orientationchange', () => {
      setValues();
      orientation.dispatchChangeEvent();
    });
  }
};


shaka.polyfill.Orientation.FakeOrientation =
class extends shaka.util.FakeEventTarget {
  constructor() {
    super();

    /** @type {string} */
    this.type = '';

    /** @type {number} */
    this.angle = 0;
  }

  /** Dispatch a 'change' event. */
  dispatchChangeEvent() {
    const event = new shaka.util.FakeEvent('change');
    this.dispatchEvent(event);
  }

  /**
   * @param {string} orientation
   * @return {!Promise}
   */
  lock(orientation) {
    /**
     * @param {string} orientation
     * @return {boolean}
     */
    const lockOrientation = (orientation) => {
      if (screen.lockOrientation) {
        return screen.lockOrientation(orientation);
      }
      if (screen.mozLockOrientation) {
        return screen.mozLockOrientation(orientation);
      }
      if (screen.msLockOrientation) {
        return screen.msLockOrientation(orientation);
      }
      return false;
    };

    let success = false;
    // The set of input strings for screen.orientation.lock and for
    // screen.lockOrientation are almost, but not entirely, the same.
    switch (orientation) {
      case 'natural':
        success = lockOrientation('default');
        break;
      case 'any':
        // It's not quite clear what locking the screen orientation to 'any'
        // is supposed to mean... presumably, that's equivalent to not being
        // locked?
        success = true;
        this.unlock();
        break;
      default:
        success = lockOrientation(orientation);
        break;
    }
    // According to the docs, there "may be a delay" between the
    // lockOrientation method being called and the screen actually being
    // locked.  Unfortunately, without any idea as to how long that delay is,
    // and with no events to listen for, we cannot account for it here.
    if (success) {
      return Promise.resolve();
    }
    // Either locking was not available, or the process failed... either way,
    // reject this with a mock error.
    // This should be a DOMException, but there is not a public constructor for
    // that.  So we make this look-alike instead.
    const unsupportedKeySystemError =
        new Error('screen.orientation.lock() is not available on this device');
    unsupportedKeySystemError.name = 'NotSupportedError';
    unsupportedKeySystemError['code'] = DOMException.NOT_SUPPORTED_ERR;
    return Promise.reject(unsupportedKeySystemError);
  }

  /** Unlock the screen orientation. */
  unlock() {
    // screen.unlockOrientation has a return value, but
    // screen.orientation.unlock does not. So ignore the return value.
    if (screen.unlockOrientation) {
      screen.unlockOrientation();
    } else if (screen.mozUnlockOrientation) {
      screen.mozUnlockOrientation();
    } else if (screen.msUnlockOrientation) {
      screen.msUnlockOrientation();
    }
  }
};


shaka.polyfill.register(shaka.polyfill.Orientation.install);
