import { MediaError } from "../../errors";
import assert from "../../utils/assert";
import isNullOrUndefined from "../../utils/is_null_or_undefined";
import { isIE11 } from "../browser_detection";
import { createCompatibleEventListener } from "../event_listeners";
import isNode from "../is_node";
import shouldFavourCustomSafariEME from "../should_favour_custom_safari_EME";
import CustomMediaKeySystemAccess from "./custom_key_system_access";
import getIE11MediaKeysCallbacks, { MSMediaKeysConstructor, } from "./custom_media_keys/ie11_media_keys";
import getMozMediaKeysCallbacks, { MozMediaKeysConstructor, } from "./custom_media_keys/moz_media_keys_constructor";
import getOldKitWebKitMediaKeyCallbacks, { isOldWebkitMediaElement, } from "./custom_media_keys/old_webkit_media_keys";
import getWebKitMediaKeysCallbacks from "./custom_media_keys/webkit_media_keys";
import { WebKitMediaKeysConstructor, } from "./custom_media_keys/webkit_media_keys_constructor";
/**
 * Automatically detect and set which EME implementation should be used in the
 * current platform.
 *
 * You can call `getEmeApiImplementation` for a different implementation.
 */
var defaultEmeImplementation = getEmeApiImplementation("auto");
export default defaultEmeImplementation;
/**
 * Returns the current EME implementation based on what's present on the device
 * and the given preference.
 * @param {string} preferredApiType - EME API preference
 * (@see IPreferredEmeApiType).
 * @returns {Object}
 */
function getEmeApiImplementation(preferredApiType) {
    var requestMediaKeySystemAccess;
    var onEncrypted;
    var setMediaKeys = defaultSetMediaKeys;
    var implementation;
    if ((preferredApiType === "standard" ||
        (preferredApiType === "auto" && !shouldFavourCustomSafariEME())) &&
        (
        // eslint-disable-next-line @typescript-eslint/unbound-method
        isNode || !isNullOrUndefined(navigator.requestMediaKeySystemAccess))) {
        requestMediaKeySystemAccess = function () {
            var args = [];
            for (var _i = 0; _i < arguments.length; _i++) {
                args[_i] = arguments[_i];
            }
            return navigator.requestMediaKeySystemAccess.apply(navigator, args);
        };
        onEncrypted = createCompatibleEventListener(["encrypted"]);
        implementation = "standard";
    }
    else {
        var isTypeSupported_1;
        var createCustomMediaKeys_1;
        if (preferredApiType === "webkit" && WebKitMediaKeysConstructor !== undefined) {
            onEncrypted = createCompatibleEventListener(["needkey"]);
            var callbacks = getWebKitMediaKeysCallbacks();
            isTypeSupported_1 = callbacks.isTypeSupported;
            createCustomMediaKeys_1 = callbacks.createCustomMediaKeys;
            setMediaKeys = callbacks.setMediaKeys;
            implementation = "webkit";
        }
        else {
            // This is for Chrome with unprefixed EME api
            if (isOldWebkitMediaElement(HTMLVideoElement.prototype)) {
                onEncrypted = createCompatibleEventListener(["needkey"]);
                var callbacks = getOldKitWebKitMediaKeyCallbacks();
                isTypeSupported_1 = callbacks.isTypeSupported;
                createCustomMediaKeys_1 = callbacks.createCustomMediaKeys;
                setMediaKeys = callbacks.setMediaKeys;
                implementation = "older-webkit";
                // This is for WebKit with prefixed EME api
            }
            else if (WebKitMediaKeysConstructor !== undefined) {
                onEncrypted = createCompatibleEventListener(["needkey"]);
                var callbacks = getWebKitMediaKeysCallbacks();
                isTypeSupported_1 = callbacks.isTypeSupported;
                createCustomMediaKeys_1 = callbacks.createCustomMediaKeys;
                setMediaKeys = callbacks.setMediaKeys;
                implementation = "webkit";
            }
            else if (isIE11 && MSMediaKeysConstructor !== undefined) {
                onEncrypted = createCompatibleEventListener(["encrypted", "needkey"]);
                var callbacks = getIE11MediaKeysCallbacks();
                isTypeSupported_1 = callbacks.isTypeSupported;
                createCustomMediaKeys_1 = callbacks.createCustomMediaKeys;
                setMediaKeys = callbacks.setMediaKeys;
                implementation = "ms";
            }
            else if (MozMediaKeysConstructor !== undefined) {
                onEncrypted = createCompatibleEventListener(["encrypted", "needkey"]);
                var callbacks = getMozMediaKeysCallbacks();
                isTypeSupported_1 = callbacks.isTypeSupported;
                createCustomMediaKeys_1 = callbacks.createCustomMediaKeys;
                setMediaKeys = callbacks.setMediaKeys;
                implementation = "moz";
            }
            else {
                onEncrypted = createCompatibleEventListener(["encrypted", "needkey"]);
                var MK_1 = window.MediaKeys;
                var checkForStandardMediaKeys_1 = function () {
                    if (MK_1 === undefined) {
                        throw new MediaError("MEDIA_KEYS_NOT_SUPPORTED", "No `MediaKeys` implementation found " +
                            "in the current browser.");
                    }
                    if (typeof MK_1.isTypeSupported === "undefined") {
                        var message = "This browser seems to be unable to play encrypted " +
                            "contents currently." +
                            "Note: Some browsers do not allow decryption " +
                            "in some situations, like when not using HTTPS.";
                        throw new Error(message);
                    }
                };
                isTypeSupported_1 = function (keyType) {
                    checkForStandardMediaKeys_1();
                    assert(typeof MK_1.isTypeSupported === "function");
                    return MK_1.isTypeSupported(keyType);
                };
                createCustomMediaKeys_1 = function (keyType) {
                    checkForStandardMediaKeys_1();
                    return new MK_1(keyType);
                };
                implementation = "unknown";
            }
        }
        requestMediaKeySystemAccess = function (keyType, keySystemConfigurations) {
            if (!isTypeSupported_1(keyType)) {
                return Promise.reject(new Error("Unsupported key type"));
            }
            for (var i = 0; i < keySystemConfigurations.length; i++) {
                var keySystemConfiguration = keySystemConfigurations[i];
                var videoCapabilities = keySystemConfiguration.videoCapabilities, audioCapabilities = keySystemConfiguration.audioCapabilities, initDataTypes = keySystemConfiguration.initDataTypes, distinctiveIdentifier = keySystemConfiguration.distinctiveIdentifier;
                var supported = true;
                supported = supported &&
                    (initDataTypes == null ||
                        initDataTypes.some(function (idt) { return idt === "cenc"; }));
                supported = supported && (distinctiveIdentifier !== "required");
                if (supported) {
                    var keySystemConfigurationResponse = {
                        initDataTypes: ["cenc"],
                        distinctiveIdentifier: "not-allowed",
                        persistentState: "required",
                        sessionTypes: ["temporary", "persistent-license"],
                    };
                    if (videoCapabilities !== undefined) {
                        keySystemConfigurationResponse.videoCapabilities = videoCapabilities;
                    }
                    if (audioCapabilities !== undefined) {
                        keySystemConfigurationResponse.audioCapabilities = audioCapabilities;
                    }
                    // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
                    var customMediaKeys = createCustomMediaKeys_1(keyType);
                    return Promise.resolve(new CustomMediaKeySystemAccess(keyType, customMediaKeys, keySystemConfigurationResponse));
                }
            }
            return Promise.reject(new Error("Unsupported configuration"));
        };
    }
    return { requestMediaKeySystemAccess: requestMediaKeySystemAccess, onEncrypted: onEncrypted, setMediaKeys: setMediaKeys, implementation: implementation };
}
/**
 * Set the given MediaKeys on the given HTMLMediaElement.
 * Emits null when done then complete.
 * @param {HTMLMediaElement} mediaElement
 * @param {Object} mediaKeys
 * @returns {Promise}
 */
function defaultSetMediaKeys(mediaElement, mediaKeys) {
    try {
        var ret = void 0;
        var elt = mediaElement;
        /* eslint-disable @typescript-eslint/unbound-method */
        if (typeof elt.setMediaKeys === "function") {
            ret = elt.setMediaKeys(mediaKeys);
        }
        /* eslint-enable @typescript-eslint/unbound-method */
        // If we get in the following code, it means that no compat case has been
        // found and no standard setMediaKeys API exists. This case is particulary
        // rare. We will try to call each API with native media keys.
        else if (typeof elt.webkitSetMediaKeys === "function") {
            ret = elt.webkitSetMediaKeys(mediaKeys);
        }
        else if (typeof elt.mozSetMediaKeys === "function") {
            ret = elt.mozSetMediaKeys(mediaKeys);
        }
        else if (typeof elt.msSetMediaKeys === "function" && mediaKeys !== null) {
            ret = elt.msSetMediaKeys(mediaKeys);
        }
        if (typeof ret === "object" &&
            ret !== null &&
            typeof ret.then === "function") {
            return ret;
        }
        else {
            return Promise.resolve(ret);
        }
    }
    catch (err) {
        return Promise.reject(err);
    }
}
