Orb.createNamespace('DeskPRO.Agent');

DeskPRO.Agent.AgentAppFactory = function() {
	var AgentApp = angular.module('AgentApp', ['ngAnimate', 'pasvaz.bindonce']);

	AgentApp.filter('formatTimestampAgo', function() {
		return function(ts) {
			var m;

			if (ts._isAMomentObject) {
				m = ts;
			} else {
				m = moment.unix(ts);
			}

			return m.fromNow();
		}
	});

	AgentApp.filter('formatTimestampCalendar', function() {
		return function(ts) {
			var m;

			if (ts._isAMomentObject) {
				m = ts;
			} else {
				m = moment.unix(ts);
			}

			return m.calendar();
		}
	});

	AgentApp.filter('formatTimestamp', function() {
		if (!window.DESKPRO_DATE_FORMATS) {
			window.DESKPRO_DATE_FORMATS = {};
		}
		return function(ts, format) {
			var m;
			if (!format) format = 'fulltime';
			switch (format) {
				case 'full':
					format = window.DESKPRO_DATE_FORMATS.full;
					break;

				case 'fulltime':
					format = window.DESKPRO_DATE_FORMATS.fulltime;
					break;

				case 'day':
					format = window.DESKPRO_DATE_FORMATS.day;
					break;

				case 'day_short':
					format = window.DESKPRO_DATE_FORMATS.day_short;
					break;

				case 'time':
					format = window.DESKPRO_DATE_FORMATS.time;
					break;
			}

			if (!format) {
				format = 'ddd, D MMM YYYY HH:mm:ss';
			}

			if (ts._isAMomentObject) {
				m = ts;
			} else {
				m = moment.unix(ts);
			}

			return m.format(format);
		}
	});

	AgentApp.filter('formatSeconds', function() {
		return function(seconds, plusDate) {
			if (!seconds) seconds = 0;

			var start = moment().subtract('seconds', seconds);
			var end = moment();
			var plus;

			if (plusDate) {
				plusDate = plusDate+"";
				if (plusDate.length == 10 && plusDate.match(/^\d+$/)) {
					plus = moment.unix(plusDate);
				} else {
					plus = moment(plusDate);
				}

				if (plus && plus.isValid()) {
					start.subtract('seconds', moment().unix() - plus.unix());
				}
			}

			return start.from(end, true);
		}
	});

	AgentApp.directive('dpTimeago', ['$interval', '$filter', function($interval, $filter) {
		return {
			restrict: 'AE',
			template: '<time class="dp-timeago"></time>',
			replace: true,
			scope: {
				timestamp: '@timestamp'
			},
			link: function(scope, element, attrs) {
				var timeoutId,
					noSuffix = attrs['noSuffix'];

				if (typeof noSuffix !== 'undefined') {
					noSuffix = true;
				} else {
					noSuffix = false;
				}

				function update() {
					var time, ts;
					if (!scope.timestamp) {
						return;
					}

					ts = scope.timestamp + "";

					if (ts.length == 10 && ts.match(/^\d+$/)) {
						time = moment.unix(ts);
					} else {
						time = moment(ts);
					}

					if (!time || !time.isValid()) {
						return;
					}

					// Cancel interval if its an old date that is unlikely to change in realtime
					// Saves some cycles when many timeago's are visible
					if (timeoutId && Math.abs(moment().unix() - time.unix()) < 86400) {
						$interval.cancel(timeoutId);
						timeoutId = null;
					}

					element.text(time.fromNow(noSuffix)).attr('title', $filter('formatTimestamp')(time, 'fulltime'));
				}

				element.on('$destroy', function() {
					if (timeoutId) {
						$interval.cancel(timeoutId);
						timeoutId = null;
					}
				});

				if (attrs['autoUpdate'] || attrs['updateInterval']) {
					timeoutId = $interval(function() {
						update();
					}, parseInt(attrs['updateInterval']) || 15000);
				}
				update();
			}
		};
	}]);

	AgentApp.directive('dpTpl', ['$compile', '$timeout', function($compile, $timeout) {
		var cache = {};
		return {
			restrict: 'AE',
			replace: true,
			transclude: false,
			compile: function(element, attrs) {
				var newElement = '<div class="dp-tpl"></div>', tpl;

				if (attrs['tplId'] && cache[attrs['tplId']]) {
					tpl = cache[attrs['tplId']];
				} else {
					tpl = _.template(element.html());
					if (attrs['tplId']) {
						cache[attrs['tplId']] = tpl;
					}
				}

				element.replaceWith(newElement);

				return function(scope, element, attrs) {
					var watch = attrs['watchVars'] ? scope.$eval(attrs['watchVars']) : null;
					var deepWatch = attrs['watchVarsDeep'] ? scope.$eval(attrs['watchVarsDeep']) : null;

					scope._isDirty = false;

					function render() {
						var oldHtml,
							newHtml;

						oldHtml = element.data('oldTplHtml');
						newHtml = tpl.call(scope, scope);

						// Prevents re-compiling the element with angular needlessly
						if (oldHtml != newHtml) {
							element.contents().remove();

							element.html(newHtml);
							element.data('oldTplHtml', newHtml);
							$compile(element.contents())(scope);
						}

						scope._isDirty = false;
					}

					if (watch && watch.length) {
						watch.forEach(function(name) {
							scope.$watch(name, function() {
								scope._isDirty = true;
							});
						});
					}
					if (deepWatch && deepWatch.length) {
						deepWatch.forEach(function(name) {
							scope.$watch(name, function() {
								scope._isDirty = true;
							}, true);
						});
					}

					scope.$watch('_isDirty', function(isDirty) {
						$timeout(function() {
							if (isDirty) {
								render();
							}
						}, 10);
					});
					render();
				};
			}
		};
	}]);

	AgentApp.directive('dpStickyTip', ['$timeout', function($timeout) {
		return {
			restrict: 'AE',
			template: '<div class="dp-stickytip" ng-transclude></div>',
			replace: true,
			transclude: true,
			link: function(scope, element, attrs) {
				var timeoutId,
					hideTimeoutId,
					timeoutMs      = parseInt(attrs['timeout']) || 350,
					hideTimeoutMs  = parseInt(attrs['hideTimeout']) || 100,
					targetEl       = element.parent(),
					targetSel      = attrs['trigger'],
					offsetTop      = parseInt(attrs['offsetTop']) || 15,
					offsetLeft     = parseInt(attrs['offsetLeft']) || 0,
					rightAlign     = typeof attrs['rightAlign'] != 'undefined',
					topAlign       = typeof attrs['topAlign'] != 'undefined',
					maxWidthCalc   = attrs['maxWidthCalc'] ? scope.$eval(attrs['maxWidthCalc']) : null,
					maxWidth       = attrs['maxWidth'] ? attrs['maxWidth'] : null,
					widthCalc      = attrs['widthCalc'] ? scope.$eval(attrs['widthCalc']) : null,
					width          = attrs['width'] ? attrs['width'] : null,
					noHoverTip     = typeof attrs['noHoverTip'] != 'undefined',
					hasInit        = false,
					m;

				if (targetSel) {
					while ((m = targetSel.match(/^@parent/))) {
						targetEl = targetEl.parent();
						targetSel = targetSel.replace(/^@parent\s*/, '');
					}

					if (targetSel.length) {
						targetEl = targetEl.find(targetSel).first();
					}
				}

				if (!targetEl || !targetEl[0]) {
					return;
				}

				if (attrs['style']) {
					element.attr('style', attrs['style']);
				}
				if (attrs['class']) {
					element.addClass(attrs['class']);
				}

				element.hide();

				function show() {
					if (!hasInit) {
						element.detach().appendTo('body');
						hasInit = true;
					}

					var pos = targetEl.offset(), left, top;
					left = pos.left + offsetLeft;
					top = pos.top + offsetTop;

					if (rightAlign) {
						left -= element.width();
						left += targetEl.width();
					}
					if (topAlign) {
						top -= element.height();
					}

					element.css({
						left: left,
						top: top,

						// Make it invisble but display:block
						// so whatever maxWidthCalc might do can use
						// the proper offset()'s
						visibility: 'hidden',
						display: 'block'
					});

					if (width) {
						element.css('width', maxWidth);
					} else if (widthCalc) {
						element.css('width', widthCalc(element, targetEl, attrs, scope));
					} else if (maxWidth) {
						element.css('max-width', maxWidth);
					} else if (maxWidthCalc) {
						element.css('max-width', maxWidthCalc(element, targetEl, attrs, scope));
					}

					// Compatibility with the dpTextOverflow directive
					element.find('.with-dp-text-overflow').trigger('init.dptextoverflow').trigger('update.dot');

					// If it's overflowing the window, align it above instead
					if ((element.offset().top + element.height()) > $(window).height()) {
						top = pos.top;
						top -= element.height();
						element.css('top', top);
					}

					element.trigger('preshow.dpstickytip');
					element.css('visibility', 'visible');
					element.trigger('postshow.dpstickytip');
				};

				function hide() {
					element.hide();
				};

				$timeout(function() {
					targetEl.on('mouseover', function () {
						if (hideTimeoutId) {
							$timeout.cancel(hideTimeoutId);
							hideTimeoutId = null;
						}
					});

					if (!noHoverTip) {
						element.on('mouseover', function () {
							if (hideTimeoutId) {
								$timeout.cancel(hideTimeoutId);
								hideTimeoutId = null;
							}
						});
					}

					targetEl.on('mouseout', function () {
						if (timeoutId) {
							$timeout.cancel(timeoutId);
							timeoutId = null;
						}
						if (hideTimeoutId) {
							$timeout.cancel(hideTimeoutId);
						}
						hideTimeoutId = $timeout(function () {
							hide();
						}, hideTimeoutMs);
					});

					if (!noHoverTip) {
						element.on('mouseout', function () {
							if (hideTimeoutId) {
								$timeout.cancel(hideTimeoutId);
							}
							hideTimeoutId = $timeout(function () {
								hide();
							}, hideTimeoutMs);
						});
					}

					targetEl.on('mouseover', function () {
						if (timeoutId) return;
						timeoutId = $timeout(function () {
							timeoutId = null;
							show();
						}, timeoutMs);
					});
				});

				scope.$on('$destroy', function() {
					if (timeoutId) {
						$timeout.cancel(timeoutId);
						timeoutId = null;
					}
					if (hideTimeoutId) {
						$timeout.cancel(hideTimeoutId);
						hideTimeoutId = null;
					}
					if (hasInit) {
						element.remove();
					}
				});
			}
		};
	}]);

	AgentApp.directive('dpRemoved', [function() {
		return {
			link: function(scope, element, attr) {
				element.on('$destroy', function() {
					scope.$eval(attr.dpRemoved);
				});
			}
		}
	}]);

	AgentApp.directive('dpTextOverflow', [function() {
		return {
			restrict: 'A',
			link: function(scope, element, attr) {
				var hasInit = false;
				function init() {
					if (hasInit) return;
					hasInit = true;
					var options = {
						ellipsis: '...',
						wrap: 'letter'
					};

					if (attr['overflowAppendString']) {
						options['ellipsis'] = attr['overflowAppendString']
					}
					if (attr['overflowWrapType']) {
						options['wrap'] = attr['overflowWrapType']
					}
					if (typeof attr['overflowWatch'] != 'undefined') {
						if (attr['overflowWatch'] == 'window') {
							options['watch'] = 'window';
						} else {
							options['watch'] = true;
						}
					}
					if (attr['overflowHeight']) {
						options['height'] = parseInt(attr['overflowHeight']);
					}
					if (attr['overflowTolerance']) {
						options['tolerance'] = attr['overflowTolerance']
					}
					if (attr['overflowCallback']) {
						options['callback'] = scope.$eval(attr['overflowCallback']);
					}

					element.dotdotdot(options);

					element.on('preshow', function() {
						element.trigger('update.dot');
					});

					scope.$on('$destroy', function() {
						element.trigger('destroy');
					});
				}

				element.addClass('with-dp-text-overflow');
				element.on('init.dptextoverflow', function() { init(); });

				if (typeof attr['overflowManualInit'] == 'undefined') {
					init();
				}
			}
		}
	}]);

	AgentApp.config(['$locationProvider', function($locationProvider) {
		$locationProvider.html5Mode(true).hashPrefix('');
	}]);

	return AgentApp;
};