// TextHighlight plugin
(function ($, window) {

	$.fn.textHighlight = function (options) {
		return this.each(function () {
			if ($(this).data(pluginName)) {
				$(this).data(pluginName, null);
			}

			$(this).data(pluginName, new TextHighlight(this, options));
		});
	};

	var pluginName = 'textHighlight';
	var highlighter;

	function TextHighlight(element, options) {
		this.element = element;
		this._setOptions(options);
		this.init();
	}

	TextHighlight._currentTargetToRemoveHighlight = null;

	TextHighlight.prototype.ICON_OFFSET_X = -20;
	TextHighlight.prototype.ICON_OFFSET_Y = 11;
	TextHighlight.prototype.HIGHLIGHT_CLASSNAME = "mark";

	TextHighlight.prototype.init = function () {
		var that = this;

		if (!$("#highlighter-pen").length || !$("#remove-highlight").length) {
			var localization = typeof(window.parent.getHighlighterLocalization) === 'function' && window.parent.getHighlighterLocalization() || {
				highlight: "Highlight selected text",
				remove: "Remove highlight text"
			};

            var $highlightbtn = $('<button class="highlight-btn highlight-mark-btn no-select" id="highlighter-pen"  aria-hidden="false" role="alert" style="top: 185px; left: 942px; display:none; opacity: 1; z-index: 10;"><img id="markerImg"  src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAQAAACROWYpAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfhBwsKOwEyz2PuAAABtUlEQVQ4y5XVwUtUURjG4ffqLcemEmwIhTaCu6QiEBeBCLqTIAIhpEUgSjtXgqhRISKu7B+QcTUtWikkrVuIq4GE2QRS5CbGoMbbzDhOzq/F3OqOnXPuzLu7cB6+e77vHI6nJoMkDeiRPuqNyl6zrE4R98gCP1mkg5aox30OqKfEUtMc4fOEr/xLsxyRYI4CjSmxSCJ+p1dZo8z/KbISR6+zQRVzAjftY4satmRd9BbvsWefITsdZt9BPzOChXo84JODHvEQYaQ+U+QdNGAGz0wTzHPsoCcs4NuqLlNx0F+s0/mH+lGqpNp1qLIuWieY0UvDnUJc4hUbpJjmh6XuW3oNjUK0sUCFGmmuWfgu/bYep/gAwJmF57hrpGGrMuEyE//CqIWGZ2rp79Lz/BsTVhri5UilKA94ajwUEZzkXcMe63yGPM+44KbiMcVzLTpjkx6GuBJHxzk0DKfEpGOvIb5BLkJO+U5AjSIvuOzC9eN5oqPwu6IdvdaBOnVHeW2r4sXU7WKMQfaAEs9JxvxoA+5inYB5bpNmlkQLb4HECgFVVvFpc07TEI+bGlC3Miq09HhJkn4DzjjsmiUEFWoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTctMDctMTFUMTA6NTk6MDErMDI6MDBgYxXXAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTA3LTExVDEwOjU5OjAxKzAyOjAwET6tawAAAABJRU5ErkJggg==            " alt=""><span class="sr-only" style="opacity: 0;">' + localization.highlight + '</span></button>');
			$(that.element).append($highlightbtn);

            var $rmvhighlightbtn = $('<button class="highlight-btn highlight-mark-btn no-select" id="remove-highlight" aria-hidden="true" role="alert" style="top: 178px; left: 959px; display: none; z-index: 10; outline: none;"><img id="rmvmarkerImg" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAQAAACROWYpAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfhBwsKNhdztajyAAAB7UlEQVQ4y43VP2gTYRjH8d+1hyaNTQKWouAiuCkqgnQQwcFN7OAiiIhLC07iIGpaUalFnCo0jqUujbMVxVEcxKnQQjcLQhdpB7VnLjlr8nXImbsk7/umv/WeD8/d8/45ARWKqF8Q4gRPuUG2Xc0SIWXy9KfnWAF+M8X+uJoi80S8dHGEx2U2aCVkOuaIPGUXR/jc5DtJ9soRGe7yi86ETJGRi9N68pwavakym5T1cIQYZYFdzAnSXTo4QhzlDU1sWel8yRRHnOQT9qwx1j2chJ9nzUG/cQHDbPOUCXnLVwfd5goyLCxihI+4EjCJZ6ZZ7vHTQeuU8M3UZ4bIQf8ylxwMP02V06A2VdM+6zav6IlqnqHrEC9YYIQJ62u/47BhUIgBSkQ0WeSghX/mmH3GqwA0LHydM0Yaj6oSl5l4wLiFxjt5ul3azevUHbcNQsykOqV5wB3nbYPI8aHjG1t8ki0e4jNsvS4Q4jrVrhE1eMUhxhjGftsgxCU2DYsTcu3/mCwccYT1FPnDDwKaVHnMgWTGvby1PevajisivddrbSir09rSsqJkK3piRyVJE5IesONJiAIXOcsXIOQROet69nanwBwB9znFIrfJ9PvxtHmZopglYJdn+AwYj7iZzxNSEce5yi0Ke2EpXmSJxj89S5fVyAFB5wAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNy0xMVQxMDo1NDoyMyswMjowMEAniDMAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDctMTFUMTA6NTQ6MjMrMDI6MDAxejCPAAAAAElFTkSuQmCC" alt=""><span class="sr-only" style="opacity: 0;">' + localization.remove + '</span></button>');
			$(that.element).append($rmvhighlightbtn);

			$(that.element).on('click keydown', "#highlighter-pen", $.proxy(this.onHighlightButtonClick, this));

			$(that.element).on('click keydown', "#remove-highlight", $.proxy(function () {
					this.onRemoveHighlightButtonClick(TextHighlight._currentTargetToRemoveHighlight);
				}, this));
		}

		var debounce = function (fn, timeout, ctx) {
			var timer;
			return function () {
				var args = arguments;
				ctx = ctx || this;
				!timer && fn.apply(ctx, args);

				clearTimeout(timer);

				timer = setTimeout(function () {
						fn.apply(ctx, args);
						timer = null;
					}, timeout);
			};
		};

		var _mouseUpDebouncedHandler = debounce(this._mouseUp, 250, this);

		$(this.element).bind('mousemove keydown', $.proxy(this._mousemove, this));
		$(this.element).bind('keydown', _mouseUpDebouncedHandler);

		that.selectionInProgress = false;

		$(this.element).on('mousedown', function () {
			that.selectionInProgress = true;
		});

		$(document).bind('mouseup', _mouseUpDebouncedHandler);

		$('.introduction-text').addClass('allow-select');

		//to select highlighted text for highlight removal
		$(that.element).on('click keydown', '.' + this.HIGHLIGHT_CLASSNAME, $.proxy(this._mouseUpRemove, this));

		//to hide buttons
		window.parent.$('body').on('mousedown keydown', $.proxy(this.hideButtons, this));
		$(document).on('mousedown keydown', $.proxy(this.hideButtons, this));
	};

	/* Event handlers */

	TextHighlight.prototype._mousemove = function (e) {
		this.options.mouseX = e.pageX;
		this.options.mouseY = e.pageY;
		this.lastMoseMoveEvent = e;
	};

	TextHighlight.prototype._mouseUp = function (e) {
		var event = e;

		if (this.lastMoseMoveEvent && e.currentTarget != this.lastMoseMoveEvent.currentTarget)
			event = this.lastMoseMoveEvent;

		if (this.selectionInProgress) {
			var st = this.getSelected();

			//fix for ie10, otherwise the button and the text to the end of container get selected
			window.setTimeout((function () {
					if (st.toString().trim() != ''
						 && !this._isSelectonOnDisableElements(st)
						 && !this._isSelectonOutsideQuestionText(st)) {
						if (!this._isAllSelectonContainsElement(st, "." + this.HIGHLIGHT_CLASSNAME)) {
							var button = $(this.element).find('#highlighter-pen');
							var shadow = 6;
							var buttonWidth = button.width() + shadow;
							var buttonHeight = button.height() + shadow;

							var pos = this.getAdjustedButtonPosition(event.pageX, event.pageY, buttonWidth, buttonHeight);
							button.css({
								'top': pos.top,
								'left': pos.left
							}).fadeIn('fast').attr("aria-hidden", "false");

							//Make Marker pen Image color as per color settings
							SetMarkerImageColour('#markerImg', $(this.element));
						} else {
							this._clearSelection();
							this._mouseUpRemove(event);
						}
					}
				}).bind(this), 10);
		}

		this.selectionInProgress = false;
	};

	TextHighlight.prototype._mouseUpRemove = function (event) {
		var $removeHighligth = $(this.element).find('#remove-highlight');
		var shadow = 6;
		var buttonWidth = $removeHighligth.width() + shadow;
		var buttonHeight = $removeHighligth.height() + shadow;

		$removeHighligth.css("display", "none");
		$(this).on('mousemove keydown', $.proxy(this._mousemove, this));

		var pos = this.getAdjustedButtonPosition(event.pageX, event.pageY, buttonWidth, buttonHeight);

		$removeHighligth.css({
			'top': pos.top,
			'left': pos.left
		}).fadeIn('fast').attr("aria-hidden", "false").focus();

		//Make Marker pen Image color as per color settings
		SetMarkerImageColour('#rmvmarkerImg', $(this.element));

		TextHighlight._currentTargetToRemoveHighlight = event.target;
	};

	TextHighlight.prototype.getAdjustedButtonPosition = function (pageX, pageY, buttonWidth, buttonHeight) {
		var left = pageX + this.ICON_OFFSET_X;
		var top = pageY + this.ICON_OFFSET_Y - $(window).scrollTop();

		if ((left + buttonWidth) > $(document).width())
			left = $(document).width() - buttonWidth;
		if (left < 0)
			left = 0;

		if ((top + buttonHeight) > $(document).height())
			top = $(document).height() - buttonHeight;
		if (top < 0)
			top = 0;

		return {
			left: left,
			top: top
		};
	};

	/*Highlight buttons click handlers*/

	TextHighlight.prototype.onHighlightButtonClick = function () {
		highlighter.highlightSelection(this.HIGHLIGHT_CLASSNAME);

		var selection = rangy.getSelection();
		selection.removeAllRanges();

		if (this.options.autoSerializeEnabled && this.options.localStorageKey)
			localStorage.setItem(this.options.localStorageKey, highlighter.serialize());
	};

	TextHighlight.prototype.onRemoveHighlightButtonClick = function (currentTargetToRemoveHighlight) {
		if (currentTargetToRemoveHighlight) {
			var highlight = highlighter.getHighlightForElement(currentTargetToRemoveHighlight);
			highlighter.removeHighlights([highlight]);
			localStorage.setItem(this.options.localStorageKey, highlighter.serialize());
		}
	};

	/*Private methods*/

	TextHighlight.prototype._clearSelection = function () {
		var selection = null;
		if (window.getSelection) {
			selection = window.getSelection();
		} else if (document.selection) {
			selection = document.selection;
		}
		if (selection) {
			if (selection.empty) {
				selection.empty();
			}
			if (selection.removeAllRanges) {
				selection.removeAllRanges();
			}
		}
	}

	TextHighlight.prototype._setOptions = function (options) {
		var defaults = {
			highlighter: null,
			mouseX: 0,
			mouseY: 0,
			disableForElements: []
		};
		this.options = $.extend({}, defaults, options);
		if (options.highlighter) {
			highlighter = options.highlighter;
		}
	};

	TextHighlight.prototype._isSelectonOnDisableElements = function (selection) {
		var result = false,
		self = this;

		this.options.disableForElements.forEach(function (selector) {
			result = result || self._isSelectonContainsElement(selection, selector);
			if (result) {
				return;
			}
		});
		return !!result;
	};

	TextHighlight.prototype._isSelectonOutsideQuestionText = function (selection) {
		if (selection && selection.rangeCount) {
			var range = selection.getRangeAt(0);

			if ($(range.startContainer).closest(this.element).length === 0 || $(range.endContainer).closest(this.element).length === 0) {
				return true;
			}
		}

		return false;
	};

	TextHighlight.prototype._isSelectonContainsElement = function (selection, elementSelector) {
		if (selection && selection.rangeCount) {
			var range = selection.getRangeAt(0);

			//check boundaries and their parents
			if (searchWithParents(range.startContainer, elementSelector) ||
				searchWithParents(range.endContainer, elementSelector)) {
				return true;
			}

			//iterate all selected elements;
			var currentNode = range.startContainer;
			while (currentNode = getNextNode(currentNode, false, range.endContainer)) {
				if ($(currentNode).is(elementSelector)) {
					return true;
				}
			}
			return false;
		}
	};

	TextHighlight.prototype._isAllSelectonContainsElement = function (selection, elementSelector) {
		if (selection && selection.rangeCount) {
			var range = selection.getRangeAt(0);

			//check boundaries and their parents
			if (!searchWithParents(range.startContainer, elementSelector) ||
				!searchWithParents(range.endContainer, elementSelector)) {
				return false;
			}

			//iterate all selected elements;
			var currentNode = range.startContainer;
			while (currentNode = getNextNode(currentNode, false, range.endContainer)) {
				if (!($(currentNode).is(elementSelector)
						 || $(currentNode).closest(elementSelector).length > 0
						 || $(currentNode).find(elementSelector).length > 0
						 || $(currentNode).text().trim() === "")) {
					return false;
				}
			}
			return true;
		}
	};

	TextHighlight.prototype.getSelected = function () {
		if (window.getSelection) {
			return window.getSelection();
		} else if (document.getSelection) {
			return document.getSelection();
		} else if (document.selection) {
			return document.selection.createRange();
		}
	};

	TextHighlight.prototype.hideButtons = function (e) {
		$('#highlighter-pen:visible, #remove-highlight:visible').fadeOut('fast').attr("aria-hidden", "true");
	};

	function SetMarkerImageColour(imageId, currentQuestionDiv) {
		if (typeof window.parent.getColourPreferencesTxtColour == 'function') {
			var txtColor = window.parent.getColourPreferencesTxtColour();
			if (txtColor === null) {
				txtColor = window.parent.getStyleProfileBtnTextColour();
			}
			if (txtColor !== null) {
				var curentImageElement = currentQuestionDiv.find(imageId)[0];
				ColourHighlighterImage(curentImageElement, txtColor);
			}
		}
	}

	function HexToRgb(color) {
		var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
		color = color.replace(shorthandRegex, function (m, r, g, b) {
				return r + r + g + g + b + b;
			});

		var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
		return result ? {
			r: parseInt(result[1], 16),
			g: parseInt(result[2], 16),
			b: parseInt(result[3], 16)
		}
		 : {
			r: 0,
			g: 0,
			b: 0
		};
	}

	function ColourHighlighterImage(imgElement, hexaColor) {
		// create hidden canvas (using image dimensions)
		var canvas = document.createElement("canvas");
		canvas.width = imgElement.width;
		canvas.height = imgElement.height;

		var ctx = canvas.getContext('2d');
		ctx.drawImage(imgElement, 0, 0);

		var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
		var data = imageData.data;

		// convert image to grayscale
		var rgbColor = HexToRgb(hexaColor);

		for (var p = 0, len = data.length; p < len; p += 4) {
			data[p + 0] = rgbColor.r;
			data[p + 1] = rgbColor.g;
			data[p + 2] = rgbColor.b;
		}
		ctx.putImageData(imageData, 0, 0);

		// replace image source with canvas data
		imgElement.src = canvas.toDataURL();
	}

	function searchWithParents(container, elementSelector) {
		return $(container).is(elementSelector)
		 ? container
		 : $(container).parents(elementSelector)[0];
	}

	function getNextNode(node, skipChildren, endNode) {
		if (endNode == node) {
			return null;
		}
		if (node.firstChild && !skipChildren) {
			return node.firstChild;
		}
		if (!node.parentNode) {
			return null;
		}
		return node.nextSibling || getNextNode(node.parentNode, true, endNode);
	}

})($, window);