/**
 * This module contains a session timeout counter that may be used to prompt an
 * inactive user if their session is close to its end.
 * 
 * @param {number}
 *            sessionTimeout - the maximum amount of time in seconds that a
 *            session can be inactive
 */
function SessionTimeoutCounterModule(sessionTimeout) {
	/**
	 * Helper function to determine if there is no timeout on the session length.
	 * 
	 * @return true if the session length is not limited
	 */
	function isSessionInfinite() {
		return isNaN(sessionTimeout) || sessionTimeout <= 0;
	}
	/**
	 * Helper function to determine if a url is to the original server of the page
	 * @param {string} url - url of new (probably ajax) request
	 */
	function isToSameServer(url) {
		var absUrlRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
		// if URL is relative
		if (!absUrlRegex.test(url)) {
			return true;
		}
		var loc = window.location;
		var pageServer = loc.protocol + '//' + loc.hostname
				+ (loc.port ? ':' + loc.port : '');
		// Check if absolute URL starts with the same server that the page came
		// from
		return url && url.indexOf(pageServer) === 0;
	}
	// Store reference to original XHR request "open" method
	var originalXHROpenMethod = XMLHttpRequest.prototype.open;
	// Only listen to ajax requests if session limit is set
	if (!isSessionInfinite()) {
		// Monitor all ajax calls after the page is loaded to update the
		// sessionLastUsed
		$(window).on("load", function() {
			// Update the session's last use whenever the load event fires (this
			// could be fired by an iframe loading new content if this module is in
			// an iframe)
			ApplicationStorage.set('sessionLastUsed', Date.now());
			// If we've already overriden the original method, skip the rest (the
			// window load event fired again)
			if (originalXHROpenMethod !== XMLHttpRequest.prototype.open) {
				return;
			}
			// Override method
			XMLHttpRequest.prototype.open = function(_, url) {
				// Don't update session if ajax request is to another server
				if (isToSameServer(url)) {
					// The "load" event fires when the request completes
					this.addEventListener('load', function() {
						// Use application storage because this could be called from
						// another tab or iframe
						ApplicationStorage.set('sessionLastUsed', Date.now());
					}, false);
				}
				// Call the original open method normally
				originalXHROpenMethod.apply(this, arguments);
			};
		});
	}
	// Return publicly available SessionTimeoutCounter module exports.
	return {
		interval : 10000,
		buffer : -3000,
		precheck : 60000,
		userWarned : false,
		timeout : null,
		ajaxRequestActive : false,
		reset : function() {
			var $this = this;
			if (PrimeFaces.widgets[this.dialogWidgetVar]) {
				PF($this.dialogWidgetVar).hide();
			}
			ApplicationStorage.set('sessionLastUsed', Date.now());
			$this.userWarned = false;
			clearTimeout($this.timeout);
			// Set the timeout again
			$this.timeout = setTimeout(function() {
				$this.checkSession()
			}, $this.interval);
		},
		/**
		 * Checks that the session is still valid
		 */
		checkSession : function() {
			var $this = this;
			if (GFaces.filesUploading || $this.ajaxRequestActive) {
				// If there is files uploading skip checking
				$this.timeout = setTimeout(function() {
					$this.checkSession()
				}, $this.interval);
				return;
			}
			var sessionLastUsed = ApplicationStorage.get('sessionLastUsed');
			var sessionExpiry = sessionLastUsed + (sessionTimeout * 1000);
			var localTime = (new Date()).getTime();
			if ((sessionExpiry + $this.buffer) <= localTime) {
				ApplicationStorage.remove('sessionLastUsed');
				window.location.href = $this.actionURL;
			} else {
				if (!$this.userWarned
						&& (sessionExpiry - $this.precheck) <= localTime) {
					$this.userWarned = true;
					PF($this.dialogWidgetVar).show();
				}

				$this.timeout = setTimeout(function() {
					$this.checkSession()
				}, $this.interval);
			}
		},
		/**
		 * Start the session timer
		 * 
		 * @param {string}
		 *            actionURL - the action to take when the session expires
		 * @param {string}
		 *            dialogWidgetVar - the widgetVar for the dialog
		 */
		startTimer : function(actionURL, dialogWidgetVar) {
			this.dialogWidgetVar = dialogWidgetVar;
			this.actionURL = actionURL;
			// Only start timer if session limit is set
			if (!isSessionInfinite()) {
				var $this = this;
				this.timeout = setTimeout(function() {
					$this.checkSession()
				}, this.interval);
			}
		}
	}
}
