'use strict';

(function ($, videojs, undefined) {
	// default setting
	var defaultSetting = {
		markerStyle: {
			'width': '8px',
			'border-radius': '30%',
			'background':'url(/ananas/modules/video/marker/point.png) no-repeat center center',
			'background-size':'100%'
		},
		markerStyle2: {
			'width': '8px',
			'border-radius': '30%',
			'background':'url(/ananas/modules/video/marker/point2.png) no-repeat center center',
			'background-size':'100%'
		},
		markerTip: {
			display: true,
			text: function text(marker) {
				return "Break: " + marker.text;
			},
			time: function time(marker) {
				return marker.time;
			},
			timeFormat: function timeFormate(marker) {
				var a = marker.time;
				if (a < 0) {
					return "NaN";
				}
				var hh = parseInt(a / 3600);
				if (hh == 0) {
					hh = "";
				} else {
					hh = hh < 10 ? "0" + hh : hh;
				}
				var mm = parseInt((a - hh * 3600) / 60);
				if (mm < 10) {
					mm = "0" + mm;
				}
				var ss = parseInt((a - hh * 3600) % 60);
				if (ss < 10) {
					ss = "0" + ss;
				}
				return hh > 0 ? hh + ":" + mm + ":" + ss : mm + ":" + ss;

			},
			timeFormat1: function timeFormat1(a) {
				if (a < 0) {
					return "NaN";
				}
				var hh = parseInt(a / 3600);
				if (hh == 0) {
					hh = "";
				} else {
					hh = hh < 10 ? "0" + hh : hh;
				}
				var mm = parseInt((a - hh * 3600) / 60);
				if (mm < 10) {
					mm = "0" + mm;
				}
				var ss = parseInt((a - hh * 3600) % 60);
				if (ss < 10) {
					ss = "0" + ss;
				}
				return hh > 0 ? hh + ":" + mm + ":" + ss : mm + ":" + ss;

			}
		},
		breakOverlay: {
			display: false,
			displayTime: 3,
			text: function text(marker) {
				return "Break overlay: " + marker.overlayText;
			},
			style: {
				'width': '100%',
				'height': '20%',
				'background-color': 'rgba(0,0,0,0.7)',
				'color': 'white',
				'font-size': '17px'
			}
		},
		onMarkerClick: function onMarkerClick(marker) {},
		onMarkerReached: function onMarkerReached(marker, index) {},
		markers: []
	};

	// create a non-colliding random number
	function generateUUID() {
		var d = new Date().getTime();
		var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
			var r = (d + Math.random() * 16) % 16 | 0;
			d = Math.floor(d / 16);
			return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);
		});
		return uuid;
	};

	var NULL_INDEX = -1;

	function registerVideoJsMarkersPlugin(options) {
		/**
		 * register the markers plugin (dependent on jquery)
		 */

		var setting = $.extend(true, {}, defaultSetting, options),
			markersMap = {},
			markersList = [],
			// list of markers sorted by time
			videoWrapper = $(this.el()),
			currentMarkerIndex = NULL_INDEX,
			player = this,
			markerTip = null,
			breakOverlay = null,
			overlayIndex = NULL_INDEX;

		function sortMarkersList() {
			// sort the list by time in asc order
			markersList.sort(function (a, b) {
				return setting.markerTip.time(a) - setting.markerTip.time(b);
			});
		}

		function addMarkers(newMarkers) {
			newMarkers.forEach(function (marker) {
				marker.key = generateUUID();

				if(marker.type == "KnowledgePoint" && marker.endTime > 0) {
					videoWrapper.find('.vjs-progress-holder').append(createMarkerDiv(marker)).append(createPeriodDiv(marker));
				}else {
					videoWrapper.find('.vjs-progress-holder').append(createMarkerDiv(marker));
				}
				// store marker in an internal hash map
				markersMap[marker.key] = marker;
				markersList.push(marker);
			});

			sortMarkersList();
		}

		function getPosition(marker) {
			return setting.markerTip.time(marker) / player.duration() * 100;
		}
		function getTipPosition(marker) {
			var percent = setting.markerTip.time(marker) / player.duration();
			if (percent * setting.vjsProgressWidth < parseFloat(markerTip.width()) / 2) {
				return "-20px";
			} else if (percent * setting.vjsProgressWidth + parseFloat(markerTip.width()) / 2 > setting.vjsProgressWidth){
				return setting.vjsProgressWidth - parseFloat(markerTip.width()) / 2 + "px";
			} else {
				return setting.markerTip.time(marker) / player.duration() * 100 + "%";
			}
		}
		function getTipMarginLeft(marker) {
			var percent = setting.markerTip.time(marker) / player.duration();
			if (percent * setting.vjsProgressWidth < parseFloat(markerTip.width()) / 2) {
				return "0px";
			} else {
				return -parseFloat(markerTip.outerWidth()) / 2 + 'px';
			}
		}

		function createMarkerDiv(marker) {
			var markerDiv = $("<div class='vjs-marker'></div>");
			var styleStr = setting.markerStyle;
			if(marker.text == "片头" || marker.text == "片尾") {
				styleStr = setting.markerStyle2;
			}

			markerDiv.css(styleStr).css({
				"margin-left": -parseFloat(markerDiv.css("width")) / 2 + 'px',
				"left": getPosition(marker) + '%'
			}).attr("data-marker-key", marker.key).attr("data-marker-time", setting.markerTip.time(marker));

			// add user-defined class to marker
			if (marker.class) {
				markerDiv.addClass(marker.class);
			}

			// bind click event to seek to marker time
			markerDiv.on('click', function (e) {
				var preventDefault = false;
				if (typeof setting.onMarkerClick === "function") {
					// if return false, prevent default behavior
					preventDefault = setting.onMarkerClick(marker) === false;
				}

				if (!preventDefault) {
					var key = $(this).data('marker-key');
					player.currentTime(setting.markerTip.time(markersMap[key]));
				}
			});

			if (setting.markerTip.display) {
				registerMarkerTipHandler(markerDiv);
			}

			return markerDiv;
		}

		function createPeriodDiv(marker) {
			var periodDiv = $('<div class="marker-period"></div>');
			var left = getPosition(marker);
			var right = marker.endTime / player.duration() * 100;
			var width = right - left;
			periodDiv.css({
				"margin-left": '-4px',
				"left": left + '%',
				"width": width + "%"
			}).attr("data-marker-key", marker.key);


			if (setting.markerTip.display) {
				registerPeriodTipHandler(periodDiv);
			}

			return periodDiv;
		}

		function updateMarkers() {
			// update UI for markers whose time changed
			markersList.forEach(function (marker) {
				var markerDiv = videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key + "']");
				var markerTime = setting.markerTip.time(marker);

				if (markerDiv.data('marker-time') !== markerTime) {
					markerDiv.css({ "left": getPosition(marker) + '%' }).attr("data-marker-time", markerTime);
				}
			});
			sortMarkersList();
		}

		function removeMarkers(indexArray) {
			// reset overlay
			if (!!breakOverlay) {
				overlayIndex = NULL_INDEX;
				breakOverlay.css("visibility", "hidden");
			}
			currentMarkerIndex = NULL_INDEX;

			var deleteIndexList = [];
			indexArray.forEach(function (index) {
				var marker = markersList[index];
				if (marker) {
					// delete from memory
					delete markersMap[marker.key];
					deleteIndexList.push(index);

					// delete from dom
					videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key + "']").remove();
				}
			});

			// clean up markers array
			deleteIndexList.reverse();
			deleteIndexList.forEach(function (deleteIndex) {
				markersList.splice(deleteIndex, 1);
			});

			// sort again
			sortMarkersList();
		}

		// attach hover event handler
		function registerMarkerTipHandler(markerDiv) {
			markerDiv.on('mouseover', function () {
				var marker = markersMap[$(markerDiv).data('marker-key')];

				if (!!markerTip) {
					var sTime = setting.markerTip.timeFormat(marker);
					var left = getTipPosition(marker);

					var endText = '';
					if (marker.type == "KnowledgePoint" && marker.endTime > 0) {
						endText = '-' + setting.markerTip.timeFormat1(marker.endTime);
					}
					var textDiv = '<div class="vjs-tip-title">' + setting.markerTip.text(marker) + '</div><div class="vjs-tip-time">' + sTime + endText + '</div>';
					markerTip.find('.vjs-tip-inner').html(textDiv);
					if (marker.type == "KnowledgePoint") {
						markerTip.find('.vjs-tip-inner').addClass("vjs-tip-knowledge");
					}

					// margin-left needs to minus the padding length to align correctly with the marker
					markerTip.css({
						"left": left,
						"margin-left": getTipMarginLeft(marker),
						"visibility": "visible"
					});
				}
			});

			markerDiv.on('mouseout', function () {
				!!markerTip && markerTip.css("visibility", "hidden");
			});
		}

		function registerPeriodTipHandler(periodDiv) {
			periodDiv.on('mouseover', function () {
				if (!!markerTip) {
					var marker = markersMap[$(periodDiv).data('marker-key')];
					var left = $('.vjs-mouse-display')[0].style.left;
					var sTime = $('.vjs-mouse-display .vjs-time-tooltip').text()
					var textDiv = '<div class="vjs-tip-title">' + setting.markerTip.text(marker) + '</div><div class="vjs-tip-time">' + sTime + '</div>';
					markerTip.find('.vjs-tip-inner').html(textDiv);
					if (marker.type == "KnowledgePoint") {
						markerTip.find('.vjs-tip-inner').addClass("vjs-tip-knowledge");
					}

					// margin-left needs to minus the padding length to align correctly with the marker
					markerTip.css({
						"left": left,
						"margin-left": getTipMarginLeft(marker),
						"visibility": "visible"
					});
				}
			});

			periodDiv.on('mouseout', function () {
				!!markerTip && markerTip.css("visibility", "hidden");
			});
		}

		function initializeMarkerTip() {
			markerTip = $("<div class='vjs-tip'><div class='vjs-tip-arrow'></div><div class='vjs-tip-inner'></div></div>");
			videoWrapper.find('.vjs-progress-holder').append(markerTip);

			var markerTip2 = $("<div class='vjs-tip2'><div class='vjs-tip-arrow2'></div><div class='vjs-tip-inner2'></div></div>");
			videoWrapper.find('.vjs-progress-holder').append(markerTip2);
		}

		// show or hide break overlays
		function updateBreakOverlay() {
			if (!setting.breakOverlay.display || currentMarkerIndex < 0) {
				return;
			}

			var currentTime = player.currentTime();
			var marker = markersList[currentMarkerIndex];
			var markerTime = setting.markerTip.time(marker);

			if (currentTime >= markerTime && currentTime <= markerTime + setting.breakOverlay.displayTime) {
				if (overlayIndex !== currentMarkerIndex) {
					overlayIndex = currentMarkerIndex;
					breakOverlay && breakOverlay.find('.vjs-break-overlay-text').html(setting.breakOverlay.text(marker));
				}

				breakOverlay && breakOverlay.css('visibility', "visible");
			} else {
				overlayIndex = NULL_INDEX;
				breakOverlay && breakOverlay.css("visibility", "hidden");
			}
		}

		// problem when the next marker is within the overlay display time from the previous marker
		function initializeOverlay() {
			breakOverlay = $("<div class='vjs-break-overlay'><div class='vjs-break-overlay-text'></div></div>").css(setting.breakOverlay.style);
			videoWrapper.append(breakOverlay);
			overlayIndex = NULL_INDEX;
		}

		function onTimeUpdate() {
			onUpdateMarker();
			updateBreakOverlay();
			options.onTimeUpdateAfterMarkerUpdate && options.onTimeUpdateAfterMarkerUpdate();
		}

		function onUpdateMarker() {
			/*
              check marker reached in between markers
              the logic here is that it triggers a new marker reached event only if the player
              enters a new marker range (e.g. from marker 1 to marker 2). Thus, if player is on marker 1 and user clicked on marker 1 again, no new reached event is triggered)
            */
			if (!markersList.length) {
				return;
			}

			var getNextMarkerTime = function getNextMarkerTime(index) {
				if (index < markersList.length - 1) {
					return setting.markerTip.time(markersList[index + 1]);
				}
				// next marker time of last marker would be end of video time
				return player.duration();
			};
			var currentTime = player.currentTime();
			var newMarkerIndex = NULL_INDEX;

			if (currentMarkerIndex !== NULL_INDEX) {
				// check if staying at same marker
				var nextMarkerTime = getNextMarkerTime(currentMarkerIndex);
				if (currentTime >= setting.markerTip.time(markersList[currentMarkerIndex]) && currentTime < nextMarkerTime) {
					return;
				}

				// check for ending (at the end current time equals player duration)
				if (currentMarkerIndex === markersList.length - 1 && currentTime === player.duration()) {
					return;
				}
			}

			// check first marker, no marker is selected
			if (currentTime < setting.markerTip.time(markersList[0])) {
				newMarkerIndex = NULL_INDEX;
			} else {
				// look for new index
				for (var i = 0; i < markersList.length; i++) {
					nextMarkerTime = getNextMarkerTime(i);
					if (currentTime >= setting.markerTip.time(markersList[i]) && currentTime < nextMarkerTime) {
						newMarkerIndex = i;
						break;
					}
				}
			}

			// set new marker index
			if (newMarkerIndex !== currentMarkerIndex) {
				// trigger event if index is not null
				if (newMarkerIndex !== NULL_INDEX && options.onMarkerReached) {
					options.onMarkerReached(markersList[newMarkerIndex], newMarkerIndex);
				}
				currentMarkerIndex = newMarkerIndex;
			}
		}

		// setup the whole thing
		function initialize() {
			if (setting.markerTip.display) {
				initializeMarkerTip();
			}

			// remove existing markers if already initialized
			player.markers.removeAll();
			addMarkers(options.markers);

			if (setting.breakOverlay.display) {
				initializeOverlay();
			}
			onTimeUpdate();
			player.on("timeupdate", onTimeUpdate);
		}

		// setup the plugin after we loaded video's meta data
		player.on("loadedmetadata", function () {
			initialize();
		});

		// exposed plugin API
		player.markers = {
			getMarkers: function getMarkers() {
				return markersList;
			},
			next: function next() {
				// go to the next marker from current timestamp
				var currentTime = player.currentTime();
				for (var i = 0; i < markersList.length; i++) {
					var markerTime = setting.markerTip.time(markersList[i]);
					if (markerTime > currentTime) {
						player.currentTime(markerTime);
						break;
					}
				}
			},
			prev: function prev() {
				// go to previous marker
				var currentTime = player.currentTime();
				for (var i = markersList.length - 1; i >= 0; i--) {
					var markerTime = setting.markerTip.time(markersList[i]);
					// add a threshold
					if (markerTime + 0.5 < currentTime) {
						player.currentTime(markerTime);
						return;
					}
				}
			},
			add: function add(newMarkers) {
				// add new markers given an array of index
				addMarkers(newMarkers);
			},
			remove: function remove(indexArray) {
				// remove markers given an array of index
				removeMarkers(indexArray);
			},
			removeAll: function removeAll() {
				var indexArray = [];
				for (var i = 0; i < markersList.length; i++) {
					indexArray.push(i);
				}
				removeMarkers(indexArray);
			},
			updateTime: function updateTime() {
				// notify the plugin to update the UI for changes in marker times
				updateMarkers();
			},
			reset: function reset(newMarkers) {
				// remove all the existing markers and add new ones
				player.markers.removeAll();
				addMarkers(newMarkers);
			},
			destroy: function destroy() {
				// unregister the plugins and clean up even handlers
				player.markers.removeAll();
				breakOverlay && breakOverlay.remove();
				markerTip && markerTip.remove();
				player.off("timeupdate", updateBreakOverlay);
				delete player.markers;
			}
		};
	}

	videojs.registerPlugin('markers', registerVideoJsMarkersPlugin);
})(jQuery, window.videojs);
