/**
 * A directive defining a generic popover element
 * Adding css class js-trigger to an element will make this the activator element
 * Adding a css class of js-content to an element will mak this the content element
 *
 * <clang-popover title="Manage Columns">
 *     <a class="js-trigger">Trigger Me!</a>
 *       <div class="js-content">This is content for popover</div>
 * </clang-popover>
 *
 * @author Simon Willan <simon.willan@claromentis.com>
 */
define(['cla_angular', 'jquery'], function (angular, $) {
	'use strict';

    var ng_module_name = 'tile_settings';

	angular.module(ng_module_name)
		.directive('clangPopover', popover);

	function popover() {
		var directive = {
			controller: controller,
			controllerAs: 'popover',
			link: link,
			replace: true,
			restrict: 'E',
			scope: { // properties bound in html
				data: '=',
				disable: '=',
				callback: '&',
				shownCallback: '&',
				autoclose: '@',
				container: '@',
				placement: '@',
				stopPropagation: '@',
				title: '@'
			},
			transclude: true
		};

		return directive;
		//////////////////

		/** directive controller */
		function controller($scope) {
			var ctl = this;

            this.$onInit = function() {
                ctl.data = $scope.data;
                ctl.callback = $scope.callback;
                ctl.shownCallback = $scope.shownCallback;
                ctl.disable = $scope.disable || false;
                ctl.autoclose = ($scope.autoclose !== "false") ? 1 : 0;
                ctl.container = $scope.container;
                ctl.stopPropagation = $scope.stopPropagation;
                ctl.title = $scope.title || "";
                ctl.placementString = $scope.placement;

                init();
            };

			ctl.isOpen = false;

			ctl.close = close;
			ctl.onshown = onshown;
			ctl.onblur = onblur;
			ctl.onhidden = onhidden;

			ctl.trigger = null; // set at compile time
			ctl.content = null; // set at compile time

			ctl.placement = function(context, source) {
				// Let specific placements be used directly
				var requested = ['top', 'bottom', 'left', 'right'];
				if (requested.indexOf(ctl.placementString) > -1)
					return ctl.placementString;

				var clickedElement = source.getBoundingClientRect();
				var navbar = angular.element('.navbar-inner')[0].getBoundingClientRect();
				var footer = angular.element('#ui-edit-homepage')[0].getBoundingClientRect();
				var clicked = {
					top: clickedElement.top - navbar.height,
					bottom: clickedElement.top + clickedElement.height - navbar.height,
					left: clickedElement.left,
					right: clickedElement.left + clickedElement.width
				};
				clicked['halfWidth'] = (clickedElement.right - clickedElement.left) / 2;
				clicked['halfHeight'] = (clickedElement.bottom - clickedElement.top) / 2;
				var wnd = angular.element(window);
				var viewPort = {
					width: wnd.width(),
					height: wnd[0].innerHeight - navbar.height - footer.height
				};

				var bestFit =  'bottom';

				// note. placementString is a preference. It may be overridden if needed, especially near the edges of a page
				switch (ctl.placementString)
				{
					case 'vertical':
					{
						// The split seems to be a bit more natural just below the middle of the screen
						var hSplit = viewPort.height * 0.65;
						if (hSplit < (clicked.top + clicked.halfHeight))
							bestFit = 'top';

						break;
					}
					case 'horizontal':
					{
						// Split right in the center
						var vSplit = viewPort.width / 2;

						if (vSplit < (clicked.left + clicked.halfWidth))
							bestFit = 'left';
						else
							bestFit = 'right';

						// Override at top of page to show the popover below
						if (clicked.top < 100)
							bestFit = 'bottom';

						// Override at bottom of page to show the popover above
						if (clicked.bottom > (viewPort.height - 80))
							bestFit = 'top';

						break;
					}
				}
				return bestFit;
			};

			/** directive initialisation */
			function init() {
				$scope.$watch(function() {
					return $scope.disable;
				}, watcher);
			}

			/** watch disabled attribute */
			function watcher(disabled) {
				if (disabled) {
					ctl.trigger.addClass("disabled");
					ctl.trigger.popover("destroy");
				} else {
					ctl.trigger.removeClass("disabled");

					// prevent event bubbling
					if (ctl.stopPropagation) {
						ctl.trigger.on("click", function (event) {
							ctl.onblur(event);
							event.stopPropagation();
							return false;
						});
					}

                    // bootstrap popover
					ctl.trigger.popover({
						container: ctl.container || false,
						content: ctl.content,
						html: true,
						placement: ctl.placement,
						title: ctl.title,
					}).on("shown.bs.popover",
						{dom: ctl.trigger}, ctl.onshown)
						.on("hide.bs.popover",
							{}, ctl.onhidden);
				}
			}

			/** close the popover */
			function close() {
				ctl.trigger.popover("hide");
			}

			/** popover on show event */
			function onshown(e) {
				// self close every popover
				var dom = e.data.dom;

				if (ctl.autoclose) {
					$("body").on("click", ctl.onblur);

					$(window.document).on("keyup", function(e) {
						if (e.keyCode == 27) {
							// note - includes fix for broken Bootstrap popover hiding bug ( http://stackoverflow.com/a/14857326 - the final update meant for Bs 3.3.6 )
							((dom.popover('hide').data('bs.popover')||{}).inState||{}).click = false;
							$(window.document).off("keyup");
						}
					});
				}

				ctl.isOpen = true;

				if (ctl.shownCallback)
					ctl.shownCallback({event: e});
			}

			/** popover on blur event */
			function onblur(e) {
				$('[data-original-title]').each(function () {
					// Fix for broken Bootstrap popover hiding bug ( http://stackoverflow.com/a/14857326 - the final update meant for Bs 3.3.6 )
					// Also fixes a conflict between Select2 and Popover (deleting item from multi-select closes the popover)
					if (window.cla.pagesEdit.popoverAutoClose &&
						!$(this).is(e.target) &&
						$(this).has(e.target).length === 0 &&
						$('.popover').has(e.target).length === 0 &&
						!$(e.target).hasClass('select2-selection__choice__remove') &&
						($(e.target).parents('.select2-dropdown').length === 0) &&
						($(e.target).parents('.bootstrap-datetimepicker-widget').length === 0))
					{
						(($(this).popover('hide').data('bs.popover')||{}).inState||{}).click = false;
						$("body").off("click", ctl.onblur);
					}
				});
			}

			/** on popover closed */
			function onhidden(event, args) {
				if (!ctl.isOpen)
					return;

				ctl.isOpen = false;
				if (ctl.callback) {
					ctl.callback({event: event, args: args});
				}
			}

            // ToDo: Remove these lines when a Claro >= 8.10 compatible branch of this module is available
            if (angular.version.major === 1 && angular.version.minor < 5) {
                this.$onInit();
            }
		}

		/** DOM manipulation function */
		function link(scope, element, attr, ctrl, transclude) {
			transclude(function (clone, tscope) {
				// add directive controller to transclude scope as a property
				tscope.popover = scope.popover;
				element.append(clone);
				element.attr("title", "");

				var activator = element.find(".js-trigger");
				var content = element.find(".js-content");

				content.remove();

				scope.popover.trigger = activator;
				scope.popover.content = content;
			});
		}
	}
});
