/**
 * Cesium - https://github.com/CesiumGS/cesium
 *
 * Copyright 2011-2020 Cesium Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Columbus View (Pat. Pend.)
 *
 * Portions licensed separately.
 * See https://github.com/CesiumGS/cesium/blob/master/LICENSE.md for full licensing details.
 */
define(['exports', './when-8d13db60'], function (exports, when) { 'use strict';

    var _supportsFullscreen;
        var _names = {
            requestFullscreen : undefined,
            exitFullscreen : undefined,
            fullscreenEnabled : undefined,
            fullscreenElement : undefined,
            fullscreenchange : undefined,
            fullscreenerror : undefined
        };

        /**
         * Browser-independent functions for working with the standard fullscreen API.
         *
         * @exports Fullscreen
         * @namespace
         *
         * @see {@link http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html|W3C Fullscreen Living Specification}
         */
        var Fullscreen = {};

        Object.defineProperties(Fullscreen, {
            /**
             * The element that is currently fullscreen, if any.  To simply check if the
             * browser is in fullscreen mode or not, use {@link Fullscreen#fullscreen}.
             * @memberof Fullscreen
             * @type {Object}
             * @readonly
             */
            element : {
                get : function() {
                    if (!Fullscreen.supportsFullscreen()) {
                        return undefined;
                    }

                    return document[_names.fullscreenElement];
                }
            },

            /**
             * The name of the event on the document that is fired when fullscreen is
             * entered or exited.  This event name is intended for use with addEventListener.
             * In your event handler, to determine if the browser is in fullscreen mode or not,
             * use {@link Fullscreen#fullscreen}.
             * @memberof Fullscreen
             * @type {String}
             * @readonly
             */
            changeEventName : {
                get : function() {
                    if (!Fullscreen.supportsFullscreen()) {
                        return undefined;
                    }

                    return _names.fullscreenchange;
                }
            },

            /**
             * The name of the event that is fired when a fullscreen error
             * occurs.  This event name is intended for use with addEventListener.
             * @memberof Fullscreen
             * @type {String}
             * @readonly
             */
            errorEventName : {
                get : function() {
                    if (!Fullscreen.supportsFullscreen()) {
                        return undefined;
                    }

                    return _names.fullscreenerror;
                }
            },

            /**
             * Determine whether the browser will allow an element to be made fullscreen, or not.
             * For example, by default, iframes cannot go fullscreen unless the containing page
             * adds an "allowfullscreen" attribute (or prefixed equivalent).
             * @memberof Fullscreen
             * @type {Boolean}
             * @readonly
             */
            enabled : {
                get : function() {
                    if (!Fullscreen.supportsFullscreen()) {
                        return undefined;
                    }

                    return document[_names.fullscreenEnabled];
                }
            },

            /**
             * Determines if the browser is currently in fullscreen mode.
             * @memberof Fullscreen
             * @type {Boolean}
             * @readonly
             */
            fullscreen : {
                get : function() {
                    if (!Fullscreen.supportsFullscreen()) {
                        return undefined;
                    }

                    return Fullscreen.element !== null;
                }
            }
        });

        /**
         * Detects whether the browser supports the standard fullscreen API.
         *
         * @returns {Boolean} <code>true</code> if the browser supports the standard fullscreen API,
         * <code>false</code> otherwise.
         */
        Fullscreen.supportsFullscreen = function() {
            if (when.defined(_supportsFullscreen)) {
                return _supportsFullscreen;
            }

            _supportsFullscreen = false;

            var body = document.body;
            if (typeof body.requestFullscreen === 'function') {
                // go with the unprefixed, standard set of names
                _names.requestFullscreen = 'requestFullscreen';
                _names.exitFullscreen = 'exitFullscreen';
                _names.fullscreenEnabled = 'fullscreenEnabled';
                _names.fullscreenElement = 'fullscreenElement';
                _names.fullscreenchange = 'fullscreenchange';
                _names.fullscreenerror = 'fullscreenerror';
                _supportsFullscreen = true;
                return _supportsFullscreen;
            }

            //check for the correct combination of prefix plus the various names that browsers use
            var prefixes = ['webkit', 'moz', 'o', 'ms', 'khtml'];
            var name;
            for (var i = 0, len = prefixes.length; i < len; ++i) {
                var prefix = prefixes[i];

                // casing of Fullscreen differs across browsers
                name = prefix + 'RequestFullscreen';
                if (typeof body[name] === 'function') {
                    _names.requestFullscreen = name;
                    _supportsFullscreen = true;
                } else {
                    name = prefix + 'RequestFullScreen';
                    if (typeof body[name] === 'function') {
                        _names.requestFullscreen = name;
                        _supportsFullscreen = true;
                    }
                }

                // disagreement about whether it's "exit" as per spec, or "cancel"
                name = prefix + 'ExitFullscreen';
                if (typeof document[name] === 'function') {
                    _names.exitFullscreen = name;
                } else {
                    name = prefix + 'CancelFullScreen';
                    if (typeof document[name] === 'function') {
                        _names.exitFullscreen = name;
                    }
                }

                // casing of Fullscreen differs across browsers
                name = prefix + 'FullscreenEnabled';
                if (document[name] !== undefined) {
                    _names.fullscreenEnabled = name;
                } else {
                    name = prefix + 'FullScreenEnabled';
                    if (document[name] !== undefined) {
                        _names.fullscreenEnabled = name;
                    }
                }

                // casing of Fullscreen differs across browsers
                name = prefix + 'FullscreenElement';
                if (document[name] !== undefined) {
                    _names.fullscreenElement = name;
                } else {
                    name = prefix + 'FullScreenElement';
                    if (document[name] !== undefined) {
                        _names.fullscreenElement = name;
                    }
                }

                // thankfully, event names are all lowercase per spec
                name = prefix + 'fullscreenchange';
                // event names do not have 'on' in the front, but the property on the document does
                if (document['on' + name] !== undefined) {
                    //except on IE
                    if (prefix === 'ms') {
                        name = 'MSFullscreenChange';
                    }
                    _names.fullscreenchange = name;
                }

                name = prefix + 'fullscreenerror';
                if (document['on' + name] !== undefined) {
                    //except on IE
                    if (prefix === 'ms') {
                        name = 'MSFullscreenError';
                    }
                    _names.fullscreenerror = name;
                }
            }

            return _supportsFullscreen;
        };

        /**
         * Asynchronously requests the browser to enter fullscreen mode on the given element.
         * If fullscreen mode is not supported by the browser, does nothing.
         *
         * @param {Object} element The HTML element which will be placed into fullscreen mode.
         * @param {HMDVRDevice} [vrDevice] The VR device.
         *
         * @example
         * // Put the entire page into fullscreen.
         * Cesium.Fullscreen.requestFullscreen(document.body)
         *
         * // Place only the Cesium canvas into fullscreen.
         * Cesium.Fullscreen.requestFullscreen(scene.canvas)
         */
        Fullscreen.requestFullscreen = function(element, vrDevice) {
            if (!Fullscreen.supportsFullscreen()) {
                return;
            }

            element[_names.requestFullscreen]({ vrDisplay: vrDevice });
        };

        /**
         * Asynchronously exits fullscreen mode.  If the browser is not currently
         * in fullscreen, or if fullscreen mode is not supported by the browser, does nothing.
         */
        Fullscreen.exitFullscreen = function() {
            if (!Fullscreen.supportsFullscreen()) {
                return;
            }

            document[_names.exitFullscreen]();
        };

        //For unit tests
        Fullscreen._names = _names;

    var theNavigator;
    if (typeof navigator !== 'undefined') {
        theNavigator = navigator;
    } else {
        theNavigator = {};
    }

    function extractVersion(versionString) {
        var parts = versionString.split('.');
        for (var i = 0, len = parts.length; i < len; ++i) {
            parts[i] = parseInt(parts[i], 10);
        }
        return parts;
    }

    var isChromeResult;
    var chromeVersionResult;
    function isChrome() {
        if (!when.defined(isChromeResult)) {
            isChromeResult = false;
            // Edge contains Chrome in the user agent too
            if (!isEdge()) {
                var fields = (/ Chrome\/([\.0-9]+)/).exec(theNavigator.userAgent);
                if (fields !== null) {
                    isChromeResult = true;
                    chromeVersionResult = extractVersion(fields[1]);
                }
            }
        }

        return isChromeResult;
    }

    function chromeVersion() {
        return isChrome() && chromeVersionResult;
    }

    var isSafariResult;
    var safariVersionResult;
    function isSafari() {
        if (!when.defined(isSafariResult)) {
            isSafariResult = false;

            // Chrome and Edge contain Safari in the user agent too
            if (!isChrome() && !isEdge() && (/ Safari\/[\.0-9]+/).test(theNavigator.userAgent)) {
                var fields = (/ Version\/([\.0-9]+)/).exec(theNavigator.userAgent);
                if (fields !== null) {
                    isSafariResult = true;
                    safariVersionResult = extractVersion(fields[1]);
                }
            }
        }

        return isSafariResult;
    }

    function safariVersion() {
        return isSafari() && safariVersionResult;
    }

    var isWebkitResult;
    var webkitVersionResult;
    function isWebkit() {
        if (!when.defined(isWebkitResult)) {
            isWebkitResult = false;

            var fields = (/ AppleWebKit\/([\.0-9]+)(\+?)/).exec(theNavigator.userAgent);
            if (fields !== null) {
                isWebkitResult = true;
                webkitVersionResult = extractVersion(fields[1]);
                webkitVersionResult.isNightly = !!fields[2];
            }
        }

        return isWebkitResult;
    }

    function webkitVersion() {
        return isWebkit() && webkitVersionResult;
    }

    var isInternetExplorerResult;
    var internetExplorerVersionResult;
    function isInternetExplorer() {
        if (!when.defined(isInternetExplorerResult)) {
            isInternetExplorerResult = false;

            var fields;
            if (theNavigator.appName === 'Microsoft Internet Explorer') {
                fields = /MSIE ([0-9]{1,}[\.0-9]{0,})/.exec(theNavigator.userAgent);
                if (fields !== null) {
                    isInternetExplorerResult = true;
                    internetExplorerVersionResult = extractVersion(fields[1]);
                }
            } else if (theNavigator.appName === 'Netscape') {
                fields = /Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/.exec(theNavigator.userAgent);
                if (fields !== null) {
                    isInternetExplorerResult = true;
                    internetExplorerVersionResult = extractVersion(fields[1]);
                }
            }
        }
        return isInternetExplorerResult;
    }

    function internetExplorerVersion() {
        return isInternetExplorer() && internetExplorerVersionResult;
    }

    var isEdgeResult;
    var edgeVersionResult;
    function isEdge() {
        if (!when.defined(isEdgeResult)) {
            isEdgeResult = false;
            var fields = (/ Edge\/([\.0-9]+)/).exec(theNavigator.userAgent);
            if (fields !== null) {
                isEdgeResult = true;
                edgeVersionResult = extractVersion(fields[1]);
            }
        }
        return isEdgeResult;
    }

    function edgeVersion() {
        return isEdge() && edgeVersionResult;
    }

    var isFirefoxResult;
    var firefoxVersionResult;
    function isFirefox() {
        if (!when.defined(isFirefoxResult)) {
            isFirefoxResult = false;

            var fields = /Firefox\/([\.0-9]+)/.exec(theNavigator.userAgent);
            if (fields !== null) {
                isFirefoxResult = true;
                firefoxVersionResult = extractVersion(fields[1]);
            }
        }
        return isFirefoxResult;
    }

    var isWindowsResult;
    function isWindows() {
        if (!when.defined(isWindowsResult)) {
            isWindowsResult = /Windows/i.test(theNavigator.appVersion);
        }
        return isWindowsResult;
    }

    function firefoxVersion() {
        return isFirefox() && firefoxVersionResult;
    }

    var isNodeJsResult;
    function isNodeJs() {
        if (!when.defined(isNodeJsResult)) {
            isNodeJsResult = typeof process === 'object' && Object.prototype.toString.call(process) === '[object process]'; // eslint-disable-line
        }
        return isNodeJsResult;
    }

    var hasPointerEvents;
    function supportsPointerEvents() {
        if (!when.defined(hasPointerEvents)) {
            //While navigator.pointerEnabled is deprecated in the W3C specification
            //we still need to use it if it exists in order to support browsers
            //that rely on it, such as the Windows WebBrowser control which defines
            //PointerEvent but sets navigator.pointerEnabled to false.

            //Firefox disabled because of https://github.com/AnalyticalGraphicsInc/cesium/issues/6372
            hasPointerEvents = !isFirefox() && typeof PointerEvent !== 'undefined' && (!when.defined(theNavigator.pointerEnabled) || theNavigator.pointerEnabled);
        }
        return hasPointerEvents;
    }

    var imageRenderingValueResult;
    var supportsImageRenderingPixelatedResult;
    function supportsImageRenderingPixelated() {
        if (!when.defined(supportsImageRenderingPixelatedResult)) {
            var canvas = document.createElement('canvas');
            canvas.setAttribute('style',
                'image-rendering: -moz-crisp-edges;' +
                'image-rendering: pixelated;');
            //canvas.style.imageRendering will be undefined, null or an empty string on unsupported browsers.
            var tmp = canvas.style.imageRendering;
            supportsImageRenderingPixelatedResult = when.defined(tmp) && tmp !== '';
            if (supportsImageRenderingPixelatedResult) {
                imageRenderingValueResult = tmp;
            }
        }
        return supportsImageRenderingPixelatedResult;
    }

    function imageRenderingValue() {
        return supportsImageRenderingPixelated() ? imageRenderingValueResult : undefined;
    }

    var supportsWebPResult;
    var supportsWebPPromise;
    function supportsWebP() {
        // From https://developers.google.com/speed/webp/faq#how_can_i_detect_browser_support_for_webp
        if (when.defined(supportsWebPPromise)) {
            return supportsWebPPromise.promise;
        }

        supportsWebPPromise = when.when.defer();
        if (isEdge()) {
            // Edge's WebP support with WebGL is incomplete.
            // See bug report: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/19221241/
            supportsWebPResult = false;
            supportsWebPPromise.resolve(supportsWebPResult);
        }

        var image = new Image();
        image.onload = function () {
            supportsWebPResult = (image.width > 0) && (image.height > 0);
            supportsWebPPromise.resolve(supportsWebPResult);
        };

        image.onerror = function () {
            supportsWebPResult = false;
            supportsWebPPromise.resolve(supportsWebPResult);
        };

        image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA';

        return supportsWebPPromise.promise;
    }

    function supportsWebPSync() {
        if (!when.defined(supportsWebPPromise)) {
            supportsWebP();
        }
        return supportsWebPResult;
    }

    var typedArrayTypes = [];
    if (typeof ArrayBuffer !== 'undefined') {
        typedArrayTypes.push(Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array);

        if (typeof Uint8ClampedArray !== 'undefined') {
            typedArrayTypes.push(Uint8ClampedArray);
        }

        if (typeof CanvasPixelArray !== 'undefined') {
            typedArrayTypes.push(CanvasPixelArray);
        }
    }

    /**
     * A set of functions to detect whether the current browser supports
     * various features.
     *
     * @exports FeatureDetection
     */
    var FeatureDetection = {
        isChrome : isChrome,
        chromeVersion : chromeVersion,
        isSafari : isSafari,
        safariVersion : safariVersion,
        isWebkit : isWebkit,
        webkitVersion : webkitVersion,
        isInternetExplorer : isInternetExplorer,
        internetExplorerVersion : internetExplorerVersion,
        isEdge : isEdge,
        edgeVersion : edgeVersion,
        isFirefox : isFirefox,
        firefoxVersion : firefoxVersion,
        isWindows : isWindows,
        isNodeJs : isNodeJs,
        hardwareConcurrency : when.defaultValue(theNavigator.hardwareConcurrency, 3),
        supportsPointerEvents : supportsPointerEvents,
        supportsImageRenderingPixelated: supportsImageRenderingPixelated,
        supportsWebP: supportsWebP,
        supportsWebPSync: supportsWebPSync,
        imageRenderingValue: imageRenderingValue,
        typedArrayTypes: typedArrayTypes,
        isPCBroswer : isPCBroswer
    };

    /**
     * Detects whether the current browser supports the full screen standard.
     *
     * @returns {Boolean} true if the browser supports the full screen standard, false if not.
     *
     * @see Fullscreen
     * @see {@link http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html|W3C Fullscreen Living Specification}
     */
    FeatureDetection.supportsFullscreen = function() {
        return Fullscreen.supportsFullscreen();
    };

    /**
     * Detects whether the current browser supports typed arrays.
     *
     * @returns {Boolean} true if the browser supports typed arrays, false if not.
     *
     * @see {@link http://www.khronos.org/registry/typedarray/specs/latest/|Typed Array Specification}
     */
    FeatureDetection.supportsTypedArrays = function() {
        return typeof ArrayBuffer !== 'undefined';
    };

    /**
     * Detects whether the current browser supports Web Workers.
     *
     * @returns {Boolean} true if the browsers supports Web Workers, false if not.
     *
     * @see {@link http://www.w3.org/TR/workers/}
     */
    FeatureDetection.supportsWebWorkers = function() {
        return typeof Worker !== 'undefined';
    };

    /**
     * Detects whether the current browser supports Web Assembly.
     *
     * @returns {Boolean} true if the browsers supports Web Assembly, false if not.
     *
     * @see {@link https://developer.mozilla.org/en-US/docs/WebAssembly}
     */
    FeatureDetection.supportsWebAssembly = function() {
        return typeof WebAssembly !== 'undefined' && !FeatureDetection.isEdge();
    };

    /**
     * Detects whether the current browser supports OffscreenCanvas.
     * @returns {Boolean} true if the browsers supports OffscreenCanvas, false if not.
     */
    FeatureDetection.supportsOffscreenCanvas = function() {
        return typeof OffscreenCanvas !== 'undefined' && !FeatureDetection.isEdge();
    };

    function isPCBroswer() {
        var sUserAgent = window.navigator.userAgent.toLowerCase();
        var bIsIpad = sUserAgent.match(/ipad/i) == "ipad";
        var bIsIphoneOs = sUserAgent.match(/iphone os/i) == "iphone os";
        var bIsMidp = sUserAgent.match(/midp/i) == "midp";
        var bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) == "rv:1.2.3.4";
        var bIsUc = sUserAgent.match(/ucweb/i) == "ucweb";
        var bIsAndroid = sUserAgent.match(/android/i) == "android";
        var bIsCE = sUserAgent.match(/windows ce/i) == "windows ce";
        var bIsWM = sUserAgent.match(/windows mobile/i) == "windows mobile";
        if (bIsIpad || bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM) {
            return false;
        } else {
            return true;
        }
    }

    exports.FeatureDetection = FeatureDetection;

});
