(function() {
var RE_WHITESPACE = /[\t\r\n\f]/g

window.ajax = {
    get: function(url, data) {
        if (typeof data == 'object') {
            var index = 0;
            for (var key in data) {
                var val = data[key];
                url += index === 0 && url.indexOf('?') === -1 ? '?' : '&';
                url += encodeURIComponent(key) + '=' + encodeURIComponent(val);
            }
        }
        return this.raw(url);
    },

    post: function(url, body) {
        var opts = {
            method: 'POST'
        };
        if (body)
            opts.body = body;
        return this.raw(url, opts);
    },

    raw: function(url, options) {
        if (!options)
            options = {}

        return fetch(url, Object.assign({
            headers: {
                'X-Requested-With': 'XMLHttpRequest',
            }
        }, options))
        .then(resp => {
            return resp.json()
        })
    }
};

window.extend = function(a, b) {
    return Object.assign(a, b);
}

window.ge = function(id) {
    return document.getElementById(id);
}

var ua = navigator.userAgent.toLowerCase();
window.browserInfo = {
    version: (ua.match(/.+(?:me|ox|on|rv|it|ra|ie)[\/: ]([\d.]+)/) || [0,'0'])[1],
    //opera: /opera/i.test(ua),
    msie: (/msie/i.test(ua) && !/opera/i.test(ua)) || /trident/i.test(ua),
    mozilla: /firefox/i.test(ua),
    android: /android/i.test(ua),
    mac: /mac/i.test(ua),
    samsungBrowser: /samsungbrowser/i.test(ua),
    chrome: /chrome/i.test(ua),
    safari: /safari/i.test(ua),
    mobile: /iphone|ipod|ipad|opera mini|opera mobi|iemobile|android/i.test(ua),
    operaMini: /opera mini/i.test(ua),
    ios: /iphone|ipod|ipad|watchos/i.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1),
};

window.isTouchDevice = function() {
    return 'ontouchstart' in window || navigator.msMaxTouchPoints;
}

window.hasClass = function(el, name) {
    if (!el)
        throw new Error('hasClass: invalid element')

    if (el.nodeType !== 1)
        throw new Error('hasClass: expected nodeType is 1, got' + el.nodeType)

    if (window.DOMTokenList && el.classList instanceof DOMTokenList) {
        return el.classList.contains(name)
    } else {
        return (" " + el.className + " ").replace(RE_WHITESPACE, " ").indexOf(" " + name + " ") >= 0
    }
}

window.addClass = function(el, name) {
    if (!hasClass(el, name)) {
        el.className = (el.className ? el.className + ' ' : '') + name;
        return true
    }
    return false
}

window.Cameras = {
    hlsOptions: null,
    h265webjsOptions: null,
    host: null,
    proto: null,
    hlsDebugVideoEvents: false,

    getUrl: function(name) {
        return this.proto + '://' + this.host + '/ipcam/' + name + '/live.m3u8';
    },

    setupHls: function(video, name, useHls) {
        var src = this.getUrl(name);

        // hls.js is not supported on platforms that do not have Media Source Extensions (MSE) enabled.

        // When the browser has built-in HLS support (check using `canPlayType`), we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video element through the `src` property.
        // This is using the built-in support of the plain video element, without using hls.js.

        if (useHls) {
            var config = this.hlsOptions;
            config.xhrSetup = function (xhr,url) {
                xhr.withCredentials = true;
            };

            var hls = new Hls(config);
            hls.loadSource(src);
            hls.attachMedia(video);
            hls.on(Hls.Events.MEDIA_ATTACHED, function () {
                video.muted = true;
                video.play();
            });
        } else {
            console.warn('hls.js is not supported, trying the native way...')

            video.autoplay = true;
            video.muted = true;
            video.playsInline = true;
            video.autoplay = true;
            if (window.browserInfo.ios)
                video.setAttribute('controls', 'controls');

            video.src = src;

            var events = ['canplay'];
            if (this.hlsDebugVideoEvents)
                events.push('canplay', 'canplaythrough', 'durationchange', 'ended', 'loadeddata', 'loadedmetadata', 'pause', 'play', 'playing', 'progress', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'waiting');

            for (var i = 0; i < events.length; i++) {
                var evt = events[i];
                (function(evt, video, name) {
                    video.addEventListener(evt, function(e) {
                        if (this.debugVideoEvents)
                            console.log(name + ': ' + evt, e);

                        if (!window.browserInfo.ios && ['canplay', 'loadedmetadata'].includes(evt))
                            video.play();
                    })
                })(evt, video, name);
            }
        }
    },

    setupH265WebJS: function(videoContainer, name) {
        var containerHeightFixed = false;
        var config = {
            player: 'video-'+name,
            width: videoContainer.offsetWidth,
            height: parseInt(videoContainer.offsetWidth * 9 / 16, 10),
            accurateSeek: true,
            token: this.h265webjsOptions.token,
            extInfo: {
                moovStartFlag: true,
                readyShow: true,
                autoPlay: true,
                rawFps: 15,
            }
        };

        var mediaInfo;
        var player = window.new265webjs(this.getUrl(name), config);

        player.onSeekStart = (pts) => {
            console.log(name + ": onSeekStart:" + pts);
        };

        player.onSeekFinish = () => {
            console.log(name + ": onSeekFinish");
        };

        player.onPlayFinish = () => {
            console.log(name + ": onPlayFinish");
        };

        player.onRender = (width, height, imageBufferY, imageBufferB, imageBufferR) => {
            // console.log(name + ": onRender");
            if (!containerHeightFixed) {
                var ratio = height / width;
                videoContainer.style.width = parseInt(videoContainer.offsetWidth * ratio, 10)+'px';
                containerHeightFixed = true;
            }
        };

        player.onOpenFullScreen = () => {
            console.log(name + ": onOpenFullScreen");
        };

        player.onCloseFullScreen = () => {
            console.log(name + ": onCloseFullScreen");
        };

        player.onSeekFinish = () => {
            console.log(name + ": onSeekFinish");
        };

        player.onLoadCache = () => {
            console.log(name + ": onLoadCache");
        };

        player.onLoadCacheFinshed = () => {
            console.log(name + ": onLoadCacheFinshed");
        };

        player.onReadyShowDone = () => {
            // console.log(name + ": onReadyShowDone:【You can play now】");
            player.play()
        };

        player.onLoadFinish = () => {
            console.log(name + ": onLoadFinish");

            player.setVoice(1.0);

            mediaInfo = player.mediaInfo();
            console.log("onLoadFinish mediaInfo===========>", mediaInfo);

            var codecName = "h265";
            if (mediaInfo.meta.isHEVC === false) {
                console.log(name + ": onLoadFinish is Not HEVC/H.265");
                codecName = "h264";
            } else {
                console.log(name + ": onLoadFinish is HEVC/H.265");
            }

            console.log(name + ": onLoadFinish media Codec:" + codecName);
            console.log(name + ": onLoadFinish media FPS:" + mediaInfo.meta.fps);
            console.log(name + ": onLoadFinish media size:" + mediaInfo.meta.size.width + "x" + mediaInfo.meta.size.height);

            if (mediaInfo.meta.audioNone) {
                console.log(name + ": onLoadFinish media no Audio");
            } else {
                console.log(name + ": onLoadFinish media sampleRate:" + mediaInfo.meta.sampleRate);
            }

            if (mediaInfo.videoType == "vod") {
                console.log(name + ": onLoadFinish media is VOD");
                console.log(name + ": onLoadFinish media dur:" + Math.ceil(mediaInfo.meta.durationMs) / 1000.0);
            } else {
                console.log(name + ": onLoadFinish media is LIVE");
            }
        };

        player.onCacheProcess = (cPts) => {
            console.log(name + ": onCacheProcess:" + cPts);
        };

        player.onPlayTime = (videoPTS) => {
            if (mediaInfo.videoType == "vod") {
                console.log(name + ": onPlayTime:" + videoPTS);
            } else {
                // LIVE
            }
        };

        player.do();
        // console.log('setupH265WebJS: video: ', video.offsetWidth, video.offsetHeight)
    },

    init: function(opts) {
        this.proto = opts.proto;
        this.host = opts.host;
        this.hlsOptions = opts.hlsConfig;
        this.h265webjsOptions = opts.h265webjsConfig;

        var useHls;
        if (opts.hlsConfig !== undefined) {
            useHls = Hls.isSupported();
            if (!useHls && !this.hasFallbackSupport()) {
                alert('Neither HLS nor vnd.apple.mpegurl is not supported by your browser.');
                return;
            }
        }

        for (var camId in opts.camsByType) {
            var name = camId + '';
            if (opts.isLow)
                name += '-low';
            var type = opts.camsByType[camId];

            switch (type) {
                case 'h265':
                    var videoContainer = document.createElement('div');
                    videoContainer.setAttribute('id', 'video-'+name);
                    videoContainer.setAttribute('style', 'position: relative'); // a hack to fix an error in h265webjs lib
                    videoContainer.className = 'video-container';
                    document.getElementById('videos').appendChild(videoContainer);
                    try {
                        this.setupH265WebJS(videoContainer, name);
                    } catch (e) {
                        console.error('cam'+camId+': error', e)
                    }
                    break;

                case 'h264':
                    var video = document.createElement('video');
                    video.setAttribute('id', 'video-'+name);
                    document.getElementById('videos').appendChild(video);
                    this.setupHls(video, name, useHls);
                    break;
            }
        }
    },

    hasFallbackSupport: function() {
        var video = document.createElement('video');
        return video.canPlayType('application/vnd.apple.mpegurl');
    },
};
})();