
'use strict';

/* globals define, ajaxify, utils, config */


define('navigator', ['forum/pagination', 'components'], function (pagination, components) {

	var navigator = {};
	var index = 1;
	var count = 0;
	var navigatorUpdateTimeoutId = 0;

	navigator.scrollActive = false;

	navigator.init = function (selector, count, toTop, toBottom, callback, calculateIndex) {
		index = 1;
		navigator.selector = selector;
		navigator.callback = callback;
		toTop = toTop || function () {};
		toBottom = toBottom || function () {};

		$(window).off('scroll', navigator.delayedUpdate).on('scroll', navigator.delayedUpdate);

		$('.pagination-block .dropdown-menu').off('click').on('click', function (e) {
			e.stopPropagation();
		});

		$('.pagination-block').off('shown.bs.dropdown', '.dropdown').on('shown.bs.dropdown', '.dropdown', function () {
			setTimeout(function () {
				$('.pagination-block input').focus();
			}, 100);
		});

		$('.pagination-block .pageup').off('click').on('click', navigator.scrollUp);
		$('.pagination-block .pagedown').off('click').on('click', navigator.scrollDown);
		$('.pagination-block .pagetop').off('click').on('click', toTop);
		$('.pagination-block .pagebottom').off('click').on('click', toBottom);

		$('.pagination-block input').on('keydown', function (e) {
			if (e.which === 13) {
				var input = $(this);
				if (!utils.isNumber(input.val())) {
					input.val('');
					return;
				}

				var index = parseInt(input.val(), 10);
				if (typeof calculateIndex === 'function') {
					index = calculateIndex(index, count);
				}

				var url = generateUrl(index);
				input.val('');
				$('.pagination-block .dropdown-toggle').trigger('click');
				ajaxify.go(url);
			}
		});

		navigator.setCount(count);
	};

	function generateUrl(index) {
		var pathname = window.location.pathname.replace(config.relative_path, '');
		var parts = pathname.split('/');
		return parts[1] + '/' + parts[2] + '/' + parts[3] + (index ? '/' + index : '');
	}

	navigator.setCount = function (value) {
		count = parseInt(value, 10);
		navigator.updateTextAndProgressBar();
	};

	navigator.show = function () {
		toggle(true);
	};

	navigator.disable = function () {
		count = 0;
		index = 1;
		navigator.selector = navigator.callback = null;
		$(window).off('scroll', navigator.update);

		toggle(false);
	};

	function toggle(flag) {
		var path = ajaxify.removeRelativePath(window.location.pathname.slice(1));
		if (flag && (!path.startsWith('topic') && !path.startsWith('category'))) {
			return;
		}

		$('.pagination-block').toggleClass('ready', flag);
	}

	navigator.delayedUpdate = function () {
		if (navigatorUpdateTimeoutId) {
			clearTimeout(navigatorUpdateTimeoutId);
			navigatorUpdateTimeoutId = 0;
		}
		navigatorUpdateTimeoutId = setTimeout(navigator.update, 100);
	};

	navigator.update = function (threshold) {
		/*
			The "threshold" is defined as the distance from the top of the page to
			a spot where a user is expecting to begin reading.
		*/
		threshold = typeof threshold === 'number' ? threshold : undefined;

		var els = $(navigator.selector);
		if (els.length) {
			index = parseInt(els.first().attr('data-index'), 10) + 1;
		}

		var scrollTop = $(window).scrollTop();
		var windowHeight = $(window).height();
		var documentHeight = $(document).height();
		var middleOfViewport = scrollTop + windowHeight / 2;
		var previousDistance = Number.MAX_VALUE;
		els.each(function () {
			var distanceToMiddle = Math.abs(middleOfViewport - $(this).offset().top);

			if (distanceToMiddle > previousDistance) {
				return false;
			}

			if (distanceToMiddle < previousDistance) {
				index = parseInt($(this).attr('data-index'), 10) + 1;
				previousDistance = distanceToMiddle;
			}
		});

		var atTop = scrollTop === 0 && parseInt(els.first().attr('data-index'), 10) === 0;
		var nearBottom = scrollTop + windowHeight > documentHeight - 100 && parseInt(els.last().attr('data-index'), 10) === count - 1;

		if (atTop) {
			index = 1;
		} else if (nearBottom) {
			index = count;
		}

		// If a threshold is undefined, try to determine one based on new index
		if (threshold === undefined && ajaxify.data.template.topic === true) {
			if (atTop) {
				threshold = 0;
			} else {
				var anchorEl = components.get('post/anchor', index - 1);
				if (anchorEl.length) {
					var anchorRect = anchorEl.get(0).getBoundingClientRect();
					threshold = anchorRect.top;
				}
			}
		}

		if (typeof navigator.callback === 'function') {
			navigator.callback(index, count, threshold);
		}

		navigator.updateTextAndProgressBar();
		toggle(!!count);
	};

	navigator.updateTextAndProgressBar = function () {
		index = index > count ? count : index;

		$('.pagination-block .pagination-text').translateHtml('[[global:pagination.out_of, ' + index + ', ' + count + ']]');
		$('.pagination-block .progress-bar').width((index / count * 100) + '%');
	};

	navigator.scrollUp = function () {
		$('body,html').animate({
			scrollTop: $(window).scrollTop() - $(window).height()
		});
	};

	navigator.scrollDown = function () {
		$('body,html').animate({
			scrollTop: $(window).scrollTop() + $(window).height()
		});
	};

	navigator.scrollTop = function (index) {
		if ($(navigator.selector + '[data-index="' + index + '"]').length) {
			navigator.scrollToPost(index, true);
		} else {
			ajaxify.go(generateUrl());
		}
	};

	navigator.scrollBottom = function (index) {
		if (parseInt(index, 10) < 0) {
			return;
		}
		if ($(navigator.selector + '[data-index="' + index + '"]').length) {
			navigator.scrollToPost(index, true);
		} else {
			index = parseInt(index, 10) + 1;
			ajaxify.go(generateUrl(index));
		}
	};

	navigator.scrollToPost = function (postIndex, highlight, duration) {
		if (!utils.isNumber(postIndex) || !components.get('topic').length) {
			return;
		}

		duration = duration !== undefined ? duration : 400;
		navigator.scrollActive = true;

		if (components.get('post/anchor', postIndex).length) {
			return navigator.scrollToPostIndex(postIndex, highlight, duration);
		}

		if (config.usePagination) {
			var index = postIndex;
			if (config.topicPostSort === 'most_votes' || config.topicPostSort === 'newest_to_oldest') {
				index = ajaxify.data.postcount - index;
			}
			var page = Math.max(1, Math.ceil(index / config.postsPerPage));

			if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) {
				pagination.loadPage(page, function () {
					navigator.scrollToPostIndex(postIndex, highlight, duration);
				});
			} else {
				navigator.scrollToPostIndex(postIndex, highlight, duration);
			}
		} else {
			navigator.scrollActive = false;
			postIndex = parseInt(postIndex, 10) + 1;
			ajaxify.go(generateUrl(postIndex));
		}
	};

	navigator.scrollToPostIndex = function (postIndex, highlight, duration) {
		var scrollTo = components.get('post', 'index', postIndex);
		var postHeight = scrollTo.height();
		var viewportHeight = $(window).height();
		var navbarHeight = components.get('navbar').height();

		if (!scrollTo.length) {
			navigator.scrollActive = false;
			return;
		}

		// Temporarily disable navigator update on scroll
		$(window).off('scroll', navigator.update);

		duration = duration !== undefined ? duration : 400;
		navigator.scrollActive = true;
		var done = false;

		function animateScroll() {
			var scrollTop = 0;
			if (postHeight < viewportHeight) {
				scrollTop = (scrollTo.offset().top - (viewportHeight / 2) + (postHeight / 2));
			} else {
				scrollTop = scrollTo.offset().top - navbarHeight;
			}

			$('html, body').animate({
				scrollTop: scrollTop + 'px'
			}, duration, function () {
				if (done) {
					// Re-enable onScroll behaviour
					$(window).on('scroll', navigator.update);
					var scrollToRect = scrollTo.get(0).getBoundingClientRect();
					navigator.update(scrollToRect.top);
					return;
				}
				done = true;

				navigator.scrollActive = false;
				highlightPost();
				$('body').scrollTop($('body').scrollTop() - 1);
				$('html').scrollTop($('html').scrollTop() - 1);
			});
		}

		function highlightPost() {
			if (highlight) {
				scrollTo.addClass('highlight');
				setTimeout(function () {
					scrollTo.removeClass('highlight');
				}, 10000);
			}
		}

		if (components.get('topic').length) {
			animateScroll();
		} else {
			navigator.scrollActive = false;
		}
	};


	return navigator;
});
