(function()
{
	var moduleName = 'commentsModule';
	var	module = angular.module(moduleName);

	var commentUpdateService = function()
	{
		var service = this;

		service.comments = [];

		service.addComment = function(commentId)
		{
			var found = false;

			for (var i = 0; i < service.comments.length; i++)
			{
				if (service.comments[i].id == commentId)
					found = true;
			}
			if (!found)
				service.comments.push({id: commentId, reloaded: true});
		};

		service.removeComment = function(commentId)
		{
			var index = -1;

			for (var i = 0; i < service.comments.length; i++)
			{
				if (service.comments[i].id == commentId)
					index = i;
			}
			if (index >= 0)
				service.comments.splice(index, 1);
		};

		service.updateComment = function(commentId)
		{
			for (var i = 0; i < service.comments.length; i++)
			{
				if (service.comments[i].id == commentId)
					service.comments[i].reloaded = true;
			}
		};
	};

	var spanElement = document.createElement('span');

	function escapeHtml(string) {
		spanElement.textContent = string;
		return spanElement.innerHTML;
	}

	function shorten(string, commentWidthService) {
		var length = commentWidthService.getWidth() / 10;

		if (string.length > length)
			string = string.slice(0, length-11) + '...' + string.slice(-8);

		return string;
	}

	var claComment = function ($compile, $timeout, service, commentWidthService)
	{
		controller.$inject = ['$scope'];
		return {
			restrict: 'E',
			controllerAs: 'comment',
			replace: false,
			scope: {
				text: '=',
				mentioned: '=',
				commentId: '=',
				isSummary: '='

			},
			controller: controller,
			link: function (scope, ele, attrs)
			{
				parseComment(scope, ele);
				scope.$watch("service.comments", function ()
				{
					parseChangedComments(scope, ele);
				}, true);

				parseComment(scope, ele);

				// Update contents if the container size changes
				commentWidthService.watch(ele);
				ele.off('comment-container-resized');
				ele.on('comment-container-resized', function()
				{
					parseComment(scope, ele);
				});
			}
		};

		function controller($scope)
		{
			$scope.service = service;
		}

		function parseComment(scope, ele)
		{
			var text = parseLinksAndEscapeText(scope.text);

			text = linkUsers(scope, text);

			ele.html(text);

			// RD-41 FRAM-867 Compile only the <cla-mention> elements, and not
            // the text nodes, to mitigate AngularJS expression injection
			$compile(ele.find('cla-mention'))(scope);
		}

		function parseChangedComments(scope, ele)
		{
			for (var i = 0; i < scope.service.comments.length; i++)
			{
				if ((scope.service.comments[i].id == scope.commentId) &&
					(scope.service.comments[i].reloaded))
				{
					parseComment(scope, ele);
					scope.service.comments[i].reloaded = false;
				}
			}
		}

		/**
		 * Convert @[id] to user @mentions (links to user profiles).
		 *
		 * @param {Object} scope
		 * @param {String} text
		 * @return {String} sum
		 */
		function linkUsers(scope, text)
		{
			var regex = /@\[([0-9]+)\]/gm;
			var result;

			scope.comment.mentioned = scope.mentioned;
			scope.comment.commentId = scope.commentId;

			while ((result = regex.exec(text)) !== null) {
				if (result.index === regex.lastIndex) {
					regex.lastIndex++;
				}
				// Check that the user ID is known
				var found = false;
				var hidden = true;
				for (var j = 0; j < scope.comment.mentioned.length; j++)
				{
					if (scope.comment.mentioned[j].user_id == result[1])
					{
						found = true;
						hidden = scope.comment.mentioned[j].hidden;
					}
				}
				if (found)
				{
					if (!hidden)
						text = text.replace('@[' + result[1] + ']', '<cla-mention user-id="' + result[1] + '" users="{{comment.mentioned}}" comment-id="{{comment.commentId}}"></cla-mention>');
					else
						text = text.replace('@[' + result[1] + ']', '@' + lmsg('common.perms.hidden_name'));
				}
			}

			return text;
		}



		/**
		 * Converts html entities in text, URL to links, then newlines to <br/>
		 *
		 * @param {String} text
		 * @return {String} sum
		 */
		function parseLinksAndEscapeText(text) {

			// This regex matches the following:
			// ([\s\S]*?)   	Text, any character including newlines.
			//					\s\S classes are used because JS . (dot) notation won't match newlines.
			//
			//					And zero or more of the following:
			//
			// (?:https?\:|ftp\:)\/\/[-a-z0-9\.\\_]+[-a-z0-9.\\_~:\\/?\#\[\]@!$&\'\(\)*+,;=%\|]*)
			// 					A http, https or ftp address
			//
			// ((?:www.)[-a-z0-9\.\\_]+[-a-z0-9.\\_~:\\/?\#\[\]@!$&\'\(\)*+,;=%\|]*)
			//					A www address without http etc. at the beginning
			//
			// ((?:mailto\:)?([\da-z\._-]+\@[\da-z\._-]+\.[\da-z]{2,4}(?:\?[\+\_\-\.\w\@\?\/\%\~\=\&]*)?))
			// 					Mailto links.
			//
			// (&hellip;)
			//                  Ellipsis entity
			//
			// Links are converted to properly formatted html links and text is escaped.

			var re = /([\s\S]*?)(?:((?:https?\:|ftp\:)\/\/[-a-z0-9\.\\_]+[-a-z0-9.\\_~:\/?\#\[\]@!$&\'\(\)*+,;=%\|]*)|((?:www.)[-a-z0-9\.\\_]+[-a-z0-9.\\_~:\/?\#\[\]@!$&\'\(\)*+,;=%\|]*)|((?:mailto\:)?([\da-z\._-]+\@[\da-z\._-]+\.[\da-z]{2,4}(?:\?[\+\_\-\.\w\@\?\/\%\~\=\&]*)?))|(&hellip;)|($))/ig;


			text = text.replace(re, function(match, p1, p2, p3, p4, p5, p6, offset){

				var result = '';
				if(p1){

					result += escapeHtml(p1);
				}

				if(p2){
					// http\s/ftp
					result += '<a target="_blank" href="' + escapeHtml(p2) + '" title="' + escapeHtml(p2) + '">' + escapeHtml(shorten(p2, commentWidthService)) + '</a>';
				} else if(p3){
					result += '<a target="_blank" href="http://' + escapeHtml(p3) + '" title="http://' + escapeHtml(p3) + '">' + escapeHtml(shorten(p3, commentWidthService)) + '</a>'
				} else if(p4){
					result += '<a href="mailto:' + escapeHtml(p5) + '">' + escapeHtml(shorten(p4, commentWidthService)) + '</a>';
				} else if (p6){
					// Pass ellipsis entity straight through unaltered
					result += p6;
				}

				return result;
			});


			text = convertLineBreaks(text);

			return text;
		}

		function convertLineBreaks(string){
			return String(string).replace(/\r?\n/g, '<br />\n');
		}
	};

	module
		.service('commentUpdateService', commentUpdateService)
		.directive('claComment', ['$compile', '$timeout', 'commentUpdateService', 'commentWidthService', claComment]);
}());
