/**
 * PowerTip Utility Functions
 *
 * @fileoverview  Private helper functions.
 * @link          https://stevenbenner.github.io/jquery-powertip/
 * @author        Steven Benner (https://stevenbenner.com/)
 * @requires      jQuery 1.7+
 */

/**
 * Determine whether a jQuery object is an SVG element
 * @private
 * @param {jQuery} element The element to check
 * @return {boolean} Whether this is an SVG element
 */
function isSvgElement(element) {
	return Boolean(window.SVGElement && element[0] instanceof SVGElement);
}

/**
 * Determines if the specified jQuery.Event object has mouse data.
 * @private
 * @param {jQuery.Event=} event The jQuery.Event object to test.
 * @return {boolean} True if there is mouse data, otherwise false.
 */
function isMouseEvent(event) {
	return Boolean(event && $.inArray(event.type, MOUSE_EVENTS) > -1 &&
		typeof event.pageX === 'number');
}

/**
 * Initializes the viewport dimension cache and hooks up the mouse position
 * tracking and viewport dimension tracking events.
 * Prevents attaching the events more than once.
 * @private
 */
function initTracking() {
	if (!session.mouseTrackingActive) {
		session.mouseTrackingActive = true;

		// grab the current viewport dimensions on load
		getViewportDimensions();
		$(getViewportDimensions);

		// hook mouse move tracking
		$document.on('mousemove' + EVENT_NAMESPACE, trackMouse);

		// hook viewport dimensions tracking
		$window.on('resize' + EVENT_NAMESPACE, trackResize);
		$window.on('scroll' + EVENT_NAMESPACE, trackScroll);
	}
}

/**
 * Updates the viewport dimensions cache.
 * @private
 */
function getViewportDimensions() {
	session.scrollLeft = $window.scrollLeft();
	session.scrollTop = $window.scrollTop();
	session.windowWidth = $window.width();
	session.windowHeight = $window.height();
}

/**
 * Updates the window size info in the viewport dimensions cache.
 * @private
 */
function trackResize() {
	session.windowWidth = $window.width();
	session.windowHeight = $window.height();
}

/**
 * Updates the scroll offset info in the viewport dimensions cache.
 * @private
 */
function trackScroll() {
	var x = $window.scrollLeft(),
		y = $window.scrollTop();
	if (x !== session.scrollLeft) {
		session.currentX += x - session.scrollLeft;
		session.scrollLeft = x;
	}
	if (y !== session.scrollTop) {
		session.currentY += y - session.scrollTop;
		session.scrollTop = y;
	}
}

/**
 * Saves the current mouse coordinates to the session object.
 * @private
 * @param {jQuery.Event} event The mousemove event for the document.
 */
function trackMouse(event) {
	session.currentX = event.pageX;
	session.currentY = event.pageY;
}

/**
 * Tests if the mouse is currently over the specified element.
 * @private
 * @param {jQuery} element The element to check for hover.
 * @return {boolean} True if the mouse is over the element, otherwise false.
 */
function isMouseOver(element) {
	// use getBoundingClientRect() because jQuery's width() and height()
	// methods do not work with SVG elements
	// compute width/height because those properties do not exist on the object
	// returned by getBoundingClientRect() in older versions of IE
	var elementPosition = element.offset(),
		elementBox = element[0].getBoundingClientRect(),
		elementWidth = elementBox.right - elementBox.left,
		elementHeight = elementBox.bottom - elementBox.top;

	return session.currentX >= elementPosition.left &&
		session.currentX <= elementPosition.left + elementWidth &&
		session.currentY >= elementPosition.top &&
		session.currentY <= elementPosition.top + elementHeight;
}

/**
 * Fetches the tooltip content from the specified element's data attributes.
 * @private
 * @param {jQuery} element The element to get the tooltip content for.
 * @return {(string|jQuery|undefined)} The text/HTML string, jQuery object, or
 *     undefined if there was no tooltip content for the element.
 */
function getTooltipContent(element) {
	var tipText = element.data(DATA_POWERTIP),
		tipObject = element.data(DATA_POWERTIPJQ),
		tipTarget = element.data(DATA_POWERTIPTARGET),
		targetElement,
		content;

	if (tipText) {
		if (typeof tipText === 'function') {
			tipText = tipText.call(element[0]);
		}
		content = tipText;
	} else if (tipObject) {
		if (typeof tipObject === 'function') {
			tipObject = tipObject.call(element[0]);
		}
		if (tipObject.length > 0) {
			content = tipObject.clone(true, true);
		}
	} else if (tipTarget) {
		targetElement = $('#' + tipTarget);
		if (targetElement.length > 0) {
			content = targetElement.html();
		}
	}

	return content;
}

/**
 * Finds any viewport collisions that an element (the tooltip) would have if it
 * were absolutely positioned at the specified coordinates.
 * @private
 * @param {CSSCoordinates} coords Coordinates for the element.
 * @param {number} elementWidth Width of the element in pixels.
 * @param {number} elementHeight Height of the element in pixels.
 * @return {number} Value with the collision flags.
 */
function getViewportCollisions(coords, elementWidth, elementHeight) {
	var viewportTop = session.scrollTop,
		viewportLeft = session.scrollLeft,
		viewportBottom = viewportTop + session.windowHeight,
		viewportRight = viewportLeft + session.windowWidth,
		collisions = Collision.none;

	if (coords.top < viewportTop || Math.abs(coords.bottom - session.windowHeight) - elementHeight < viewportTop) {
		collisions |= Collision.top;
	}
	if (coords.top + elementHeight > viewportBottom || Math.abs(coords.bottom - session.windowHeight) > viewportBottom) {
		collisions |= Collision.bottom;
	}
	if (coords.left < viewportLeft || coords.right + elementWidth > viewportRight) {
		collisions |= Collision.left;
	}
	if (coords.left + elementWidth > viewportRight || coords.right < viewportLeft) {
		collisions |= Collision.right;
	}

	return collisions;
}

/**
 * Counts the number of bits set on a flags value.
 * @param {number} value The flags value.
 * @return {number} The number of bits that have been set.
 */
function countFlags(value) {
	var count = 0;
	while (value) {
		value &= value - 1;
		count++;
	}
	return count;
}