/**
 * html5mmrPlayer.js
 */
/*
 * VideoSourceState
 *
 *   The state of the Video Source.
 */
const VideoSourceState = {
    NORMAL: 0,
    STARTED: 1,
    STOPPED: 2
};

const WebCommandId = {
    HTMLMMR_WEB_COMMAND_FATAL_ERROR: 0,
    HTML5MMR_WEB_COMMAND_LOG_MESSAGE: 1
};
var Logger = {
    error: function(a) {
        Logger.log(a, WebCommandId.HTMLMMR_WEB_COMMAND_FATAL_ERROR)
    },
	fatal_error: function(a) {
         try {
            if (typeof a == "string" || a.length > 0) {
                var c = {
					commandId:12,
                    command: 'fatal_error',
                    message: a
                };
                Logger.sendToConsole(JSON.stringify(c)),
                Logger.sendToWebsocket(JSON.stringify(c))
            }
        } catch(d) {
            Logger.sendToConsole("Exception occured : " + d.message)
        }
    },
    info: function(a) {
        Logger.log(a, WebCommandId.HTML5MMR_WEB_COMMAND_LOG_MESSAGE)
    },
    sendToConsole: function(a) { ! console || console.log(a)
    },
    sendToWebsocket: function(a) {
        typeof a == "string" && (!gWebSocket || gWebSocket.readyState != WebSocket.OPEN ? Logger.sendToConsole("Websocket is not open. Cannot send message : " + a) : gWebSocket.send(a))
    },
    log: function(a, b) {
        try {
            if (typeof a == "string" || a.length > 0) {
                var c = {
                    commandId: b,
                    message: a
                };
                Logger.sendToConsole(JSON.stringify(c)),
                Logger.sendToWebsocket(JSON.stringify(c))
            }
        } catch(d) {
            Logger.sendToConsole("Exception occured : " + d.message)
        }
    }
},

Html5MMRPlayer = function() {
    var a = this;
    this._videoSources = {},
    this._currentVideoSource = null,
	this.interval=null,
	this.seekinterval=null,
	this.spiceVolume = 1,
	this.videoVolume = 1,
	this.state = false;
	this.isIqiyi = false;
	this.isYouku = false;
    this.initialize = function() {
        return ! gWebSocket || !gVideo || !gVideoInfo ?
			(Logger.error("initialize(): Failed to find window variables."),!1)
			: ( Logger.info("initialize(): Initialize the Html5MMRPlayer object."),
				gWebSocket.onmessage = b,
				gVideo.addEventListener("canplay",
					function() {
						Logger.info("Is canplay ");
					},!1),
				gVideo.addEventListener("click",
					function(){
					Logger.info("click");
					Logger.info("video state " + a.state);
					if(a.state)
					{
						Logger.info("video state is playing ,so play the video");
						gVideo.play();
					}
					}),
				!0
			)
    };
    var b = function(a) {
        var b = a.data;
        typeof b == "string" ? d(b) : g(b)
    },
    //HandleMsg(a)
    c = function(a) { //GetTextTracks
        var b = 0;
        for (b = 0; b < gVideo.textTracks.length; b++) if (gVideo.textTracks[b].label === a) return gVideo.textTracks[b];
        return null
    },
    d = function(b) { //handleStrMsg
		if(!b) Logger.error("processJSONMessage(): b is null.");
		if(typeof b != "string") Logger.error("processJSONMessage(): b is not string.");
		if(b.length == 0) Logger.error("processJSONMessage(): b length is 0.");
        if (!b || typeof b != "string" || b.length == 0) return Logger.error("processJSONMessage(): Invalid parameter."),
        !1;
        //console.log(b);
        try {
            var d = null;
            d = JSON.parse(b);
            if (!d || d.command === undefined) return Logger.error("processJSONMessage(): Ill-formatted JSON data: " + b),
            !1;
            switch (d.command) {
            case "startUrl":
                if (d.videoSourceId === undefined) return Logger.error("processJSONMessage(): No videoSourceId present: " + b),
                !1;
                var g = e(d.videoSourceId);
                if (! (g instanceof VideoSource)) return Logger.error("processJSONMessage(): Could not find or create VideoSource: " + b),
                !1;
				if ((d.time - gVideo.currentTime) > 1 || (gVideo.currentTime - d.time) > 1)  gVideo.currentTime = d.time;
                //if( this.interval== null && d.id.substring(0, 5) != "blob:"){
					//this.interval = setInterval(function() {
						//var d = {
							//command: "updatetime",
							//commandId:18,
							//time: gVideo.currentTime
						//};
						//Logger.sendToConsole(JSON.stringify(d));
						//Logger.sendToWebsocket(JSON.stringify(d));
					//}, 2000);
				//}
                return a._currentVideoSource = g,
				//(d.id.substring(0, 5) == "blob:"? this.interval && (clearInterval(this.interval),this.interval=null):null),
				d.id.indexOf("iqiyi") != -1 && (this.isIqiyi = true),
				d.id.indexOf("youku") != -1 && (this.isYouku = true),
                a._currentVideoSource.start(d);
            case "stopUrl":
				if(this.interval) {clearInterval(this.interval),this.interval=null};
                if (d.videoSourceId === undefined) return Logger.error("processJSONMessage(): No videoSourceId present: " + b),
                !1;
                var g = e(d.videoSourceId);
                if (! (g instanceof VideoSource)) return Logger.error("processJSONMessage(): Could not find or create VideoSource: " + b),
                !1;
                if (! (a._currentVideoSource instanceof VideoSource)) return Logger.error("processJSONMessage(): There is no current video source to stop: " + b),
                !1;
                return a._currentVideoSource.stop(),
                a._currentVideoSource = null,
                !0;
            case "updatetime":
                if ((d.time - gVideo.currentTime) > 1 || (gVideo.currentTime - d.time) > 1)  gVideo.currentTime = d.time;
                if( this.interval== null){
					this.interval = setInterval(function() {
						var d = {
							command: "updatetime",
							commandId:18,
							time: gVideo.currentTime
						};
						Logger.sendToConsole(JSON.stringify(d));
						Logger.sendToWebsocket(JSON.stringify(d));
					}, 2000);
				}

				return !0;
            case "addSourceBuffer":
                if (! (a._currentVideoSource instanceof VideoSource)) return Logger.error("processJSONMessage(): There is no current video source: " + b),
                !1;
                return a._currentVideoSource.processAddSourceBufferMessage(d);
            case "abortSourceBuffer":
                if (! (a._currentVideoSource instanceof VideoSource)) return Logger.error("processJSONMessage(): There is no current video source: " + b),
                !1;
                return a._currentVideoSource.processAbortSourceBuffer(d);
            case "control":
                return f(d);
            case "addTextTrack":
                if (typeof d.kind != "string" || typeof d.label != "string" || typeof d.label != "string") return Logger.info("processJSONMessage(): WARNING, Invalid format: " + b),
                !1;
                var h = gVideo.addTextTrack(d.kind, d.label, d.language);
                if (! (h instanceof TextTrack)) return Logger.info("processJSONMessage(): WARNING, Failed to add text track: " + b),
                !1;
                return Logger.info("processJSONMessage(): Successfully added text track:" + d.label),
                !0;
            case "addCues":
                //tips
                if (typeof d.label != "string") return Logger.info("processJSONMessage(): WARNING, Invalid label format: " + b),
                !1;
                if (!d.cues || !d.cues.length) return Logger.info("processJSONMessage(): WARNING, Invalid cues format: " + b),
                !1;
                var h = c(d.label);
                if (! (h instanceof TextTrack)) return Logger.info("processJSONMessage(): WARNING, Could not find TextTrack: " + b),
                !1;
                for (var i = 0; i < d.cues.length; i++) {
                    var j = d.cues[i];
                    if (j) {
                        if (typeof j.startTime != "number" || typeof j.endTime != "number" || typeof j.text != "string") {
                            Logger.info("processJSONMessage(): WARNING, Cannot process cue, invalid format.");
                            continue
                        }
                        var k = new VTTCue(j.startTime, j.endTime, j.text);
                        h.addCue(k)
                    }
                }
                return ! 0;
            case "textTrackChange":
                if (typeof d.label != "string" || typeof d.mode != "string") return Logger.info("processJSONMessage(): WARNING, Invalid message format: " + b),
                !1;
                var h = c(d.label);
                if (! (h instanceof TextTrack)) return Logger.info("processJSONMessage(): WARNING, Could not find TextTrack: " + b),
                !1;
                return h.mode = d.mode,
                !0;
            default:
                return Logger.info("processJSONMessage(): WARNING, unknown command " + d),
                !1
            }
        } catch(l) {
            return Logger.error("processJSONMessage(): Exception " + l.message),
            !1
        }
    },
    e = function(b) { //GetVideoSource
        return typeof b != "number" ? (Logger.error("getVideoSourceInstance(): Video source Id is not valid format."), null) : (a._videoSources.hasOwnProperty(b) || (a._videoSources[b] = new VideoSource(b)), a._videoSources[b])
    },
    f = function(b) { //HandleControlMsg
        if (b === undefined || b.control === undefined) return ! 1;
        Logger.info("processControlMessage(): Control message: " + JSON.stringify(b));
        switch (b.control) {
        case "play":
			a.state = true;
            gVideo.play();
            break;
        case "pause":
			a.state = false;
            gVideo.pause(),
            Logger.info("[VERIFY] " + gVideoInfo.title + " " + gVideoInfo.url + " " + gVideo.currentTime);
            break;
        case "seeked":
            if (b.param === undefined || b.param.currentTime === undefined || typeof b.param.currentTime != "number") return ! 1;
            if(gVideo.currentTime-b.param.currentTime>1 || gVideo.currentTime-b.param.currentTime<-1)
				gVideo.currentTime = b.param.currentTime;
			//爱奇艺或者优酷网页时间在走,没有画面问题处理,原因:重定向视频seek后时间并没有改变
			//seek时,设置定时器,5秒后检测视频当前时间是否与seek时间+5相差范围在1秒以内,不在范围内就再次seek同步时间
			if(this.isIqiyi || this.isYouku)
			{
				var c=this;
				this.nowtime = b.param.currentTime;
				if(this.seekinterval != null)
				{
					clearTimeout(this.seekinterval);
					this.seekinterval = null;
				}
				if(this.seekinterval == null)
				{
					this.seekinterval = setTimeout(function(){
						if(gVideo.currentTime-(c.nowtime+5)>1 || gVideo.currentTime-(c.nowtime+5)<-1)
						{
							Logger.info("*** seek again");
							gVideo.currentTime = c.nowtime + 5;
						}
						clearTimeout(c.seekinterval);
						c.seekinterval = null;
					},5000);
				}
			}
            break;
        case "volumechange":
            if (b.param === undefined || b.param.volume === undefined || typeof b.param.volume != "number") return ! 1;
			var v = b.param.volume * a.spiceVolume;
            gVideo.volume = parseFloat(v).toFixed(1);
			a.videoVolume = b.param.volume;
            break;
		case "spicevolumechange":
            if (b.param === undefined || b.param.volume === undefined || typeof b.param.volume != "number") return ! 1;
            a.spiceVolume = b.param.volume;
			var v = b.param.volume * a.videoVolume;
			gVideo.volume = parseFloat(v).toFixed(1);
            break;
        case "muted":
            if (b.param === undefined || b.param.state === undefined || typeof b.param.state != "boolean") return ! 1;
            gVideo.muted = b.param.state;
            break;
        case "ratechange":
            if (b.param === undefined || b.param.playbackRate === undefined || typeof b.param.playbackRate != "number") return ! 1;
            gVideo.playbackRate = b.param.playbackRate;
            break;
        default:
            Logger.error("processControlMessage(): ERROR, Command not supported: " + b.control)
        }
    },
    g = function(a) { //HandleBlobMsg
        var b = new Uint8Array(a, 0, 32),
        c = new Uint8Array(a, 32); //b对应html5redir.js中sendBuffer函数的d，c对应a，a对应e
        if (!b || b.length != 32) return Logger.error("processBlobMessage(): Invalid header."),
        !1;
        var d = e(b[0]);
        return d instanceof VideoSource ? d._state === VideoSourceState.STOPPED ? (Logger.info("Discard blob, video source stopped, Id = " + b[0]), !1) : d.processBlobMessage(b[1], c) : (Logger.error("processBlobMessage():  Invalid video source instance."), !1)
    }

},

PlayerSourceBuffer = function(a) {
    this.rawBuffer = a,
    this.pendingBuffers = []
},

VideoSource = function(a) {
    this._mediaSource = null,
    this._playerSBList = {},
    this._state = VideoSourceState.NORMAL,
    this._id = a,
    this._bufferCount = 0,
    this._videoId = 0,
    this._Iqiyi = false;
    var b = function(a) { //ErrorHandle
        switch (a.target.error.code) {
        case a.target.error.MEDIA_ERR_ABORTED:
            Logger.error("The video playback was aborted.");
            break;
        case a.target.error.MEDIA_ERR_NETWORK:
            Logger.error("A network error caused the video download to fail part-way.");
            break;
        case a.target.error.MEDIA_ERR_DECODE:
            Logger.error("The video playback was aborted due to a corruption problem or because the video used features which browser did not support.");
            break;
        case a.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
            Logger.fatal_error("The video could not be loaded, either because the server or network failed or because the format is not supported.");
            break;
        default:
            Logger.error("An unknown error occurred.")
        }
    };
    this.start = function(a) {
        if (!a || a.constructor !== Object) 
			return Logger.error("start(): Invalid object."),!1;
        if (typeof a.id != "string" || a.id.length == 0) 
			return Logger.error("start(): Incorrect Id param" + JSON.stringify(a)),!1;
        gVideo.src != "" && (gVideo.pause());
        gVideo.onerror || (Logger.info("start(): Set the error handler of the video."), gVideo.onerror = b);
		gVideo.defaultPlaybackRate= a.defaultPlaybackRate;
        if (a.id.substring(0, 5) == "blob:") {
            a.id.indexOf("iqiyi") != -1 && (this._Iqiyi = true);
            window.MediaSource && (this._mediaSource = new MediaSource(),
			gVideo.src = window.URL.createObjectURL(this._mediaSource), 
			this._mediaSource.readyState == "open" ? Logger.info("start(): MediaSource open.") : this._mediaSource.readyState == "closed" ? Logger.info("start(): MediaSource closed.") : Logger.info("start(): MediaSource ended."))
        } else gVideo.src = a.id;
        //gVideo.play();
        return Logger.info("start(): Video set to source = " + gVideo.src),this._state = VideoSourceState.STARTED,!0
    },
    this.stop = function() {
        return Logger.info("stop(): Stopping video source = " + this._id),
        gVideo.onerror && (Logger.info("stop(): Reset the error handler of the video."), gVideo.onerror = null),
        gVideo.src.substring(0, 5) == "blob:" && URL.revokeObjectURL(gVideo.src),
        gVideo.src = "",
        this._playerSBList = null,
        this._mediaSource = null,
        this._state = VideoSourceState.STOPPED,
        !0
    },
    this.updatePlayerSourceBufferFromId = function(a, b) {
		// console.log('-------'+a);
		// console.log('-------'+b);
        return typeof a != "number" ? 
				(Logger.error("updatePlayerSourceBufferFromId(): Invalid id parameter."), null) 
				:   this._playerSBList.hasOwnProperty(a) ? 
					( b && (Logger.info("updatePlayerSourceBufferFromId(): Update PlayerSourceBuffer, Id =" + a),this._playerSBList[a].rawBuffer = b), this._playerSBList[a]) 
					: (Logger.info("updatePlayerSourceBufferFromId(): Create new PlayerSourceBuffer, Id =" + a), b instanceof SourceBuffer ? Logger.info("updatePlayerSourceBufferFromId(): b is SourceBuffer.") : Logger.info("updatePlayerSourceBufferFromId(): b is not SourceBuffer."), this._playerSBList[a] = new PlayerSourceBuffer(b), this._playerSBList[a])
    },
    this.performAppendBuffer = function(a) {
        try {
            var b = null;
            return typeof a != "number" ? (Logger.error("performAppendBuffer(): Invalid parameter."), !1) 
				: this._playerSBList.hasOwnProperty(a) ? 
					(	b = this._playerSBList[a],console.log(this._playerSBList), 
						!!b && b.rawBuffer instanceof SourceBuffer ? 
							(b.pendingBuffers.length > 0 && !b.rawBuffer.updating && (Logger.info("performAppendBuffer(): Append buffer " + a), this._Iqiyi && this._bufferCount == 2 && this._videoId == a && (this._bufferCount++), (!this._Iqiyi || this._bufferCount > 2) && b.rawBuffer.appendBuffer(b.pendingBuffers.shift())), !0)
							: ( !! b ? Logger.info("performAppendBuffer(): b is Not NULL.") : Logger.info("performAppendBuffer(): b Is NULL."),
								b.rawBuffer instanceof SourceBuffer ? Logger.info("performAppendBuffer(): b.rawBuffer is SourceBuffer.") : (Logger.info("performAppendBuffer(): b.rawBuffer is not SourceBuffer."),console.log(b.rawBuffer)),
								Logger.info("performAppendBuffer(): Raw source buffer not instantiated yet."), !1)) 
					: (Logger.error("performAppendBuffer(): No source buffer of particular id."), !1)
        } catch(c) {
            return Logger.error("performAppendBuffer: Exception:" + c.message),
            !1
        }
    },
    this.processAddSourceBufferMessage = function(a) {
        function d(d) {
            try {
                if (a.mimeType.substring(0, 5) == "video") {
                    console.log("save video id:" + a.id),
                    b._videoId = a.id
                }
                Logger.info("processAddSourceBufferMessage(): in d."),
				c = b._mediaSource.addSourceBuffer(a.mimeType),
                c instanceof SourceBuffer ? Logger.info("processAddSourceBufferMessage(): c is SourceBuffer.") : Logger.info("processAddSourceBufferMessage(): c is not SourceBuffer."),
                b.updatePlayerSourceBufferFromId(a.id, c),
                c.id = a.id,
                b._bufferCount++,
                Logger.info("sourceOpen(): Source buffer added, Id = " + a.id + ", mimeType = " + a.mimeType),
                c.addEventListener("update",
                function(c) {
                    b.performAppendBuffer(this.id)
                }),
                b.performAppendBuffer(a.id)
            } catch(d) {
                Logger.error("sourceOpen(): Exception thrown:" + d.message)
            }
        }
        var b = this,
        c = null;
        return ! a || typeof a.id != "number" 
			||  typeof a.mimeType != "string" ? (Logger.error("processAddSourceBufferMessage(): Invalid parameter."), !1) 
				: this._mediaSource == null ? 
				  (Logger.error("processAddSourceBufferMessage(): Media Source is NULL."), !1) 
				  : MediaSource.isTypeSupported(a.mimeType) ? (this._mediaSource.readyState == "open" ? d() : this._mediaSource.addEventListener("sourceopen", d, !1), !0) : (Logger.error("processAddSourceBufferMessage(): MimeType is not supported."), !1)
    },
    this.processAbortSourceBuffer = function(a) {
        if (!a || typeof a.id != "number") return Logger.error("processAbortSourceBuffer(): Invalid paramter."),
        !1;
        var b = this._playerSBList[a.id];
        return b instanceof PlayerSourceBuffer ? b.rawBuffer instanceof SourceBuffer ? (this._bufferCount--, b.rawBuffer.abort(), !0) : (Logger.error("processAbortSourceBuffer(): Invalid rawBuffer property."), !1) : (Logger.error("processAbortSourceBuffer(): SourceBuffer cannot be found."), !1)
    },
    this.processBlobMessage = function(a, b) {
		// console.log('--------processBlobMessage');
		// console.log('--------'+a);
		// console.log('--------'+b);
        if (typeof a == "number" && b instanceof Uint8Array) {
            var c = this.updatePlayerSourceBufferFromId(a);
            return c instanceof PlayerSourceBuffer ? (c.pendingBuffers.push(b), this.performAppendBuffer(a), !0) : (Logger.error("processBlobMessage(): Cannot instantiate PlayerSourceBuffer from Id = " + a), !1)
        }
        return Logger.error("processBlobMessage(): Invalid parameters."),
        !1
    }
}
