// Adjustable constants.
var showPersonDelay = 500; // Time that mouse cursor must be over a person before it will be shown.
var showPersonTestInterval = 500; // Time between attempts to show a person while waiting for another animation to finish.
var personFadeInDuration = 700; // Time that a person takes to fade in.
var personFadeOutDuration = 350; // Time that a person takes to fade out.
var bubbleFadeInDelay = 150; // Time after which the bubble fades in, once a person has started to fade in.
var bubbleFadeInDuration = 400; // Time that the bubble takes to fade in.
var bubbleFadeDistance = -10; // Distance that the bubble travels during fade in. Positive values move down, negative up.
var autoAnimationEnabled = true;
var autoAnimationInterval = 7000; // Time between switching persons in auto-animation mode.


// Bubble layout.
var pointPaddingX = 40; // Horizontal distance of bubble point from image left/right.
var pointPaddingY = 11; // Vertical distance of bubble point from image top.


// These arrays will be populated in the HTML file.
var bubbleDirection = new Array; // Direction in which the bubble protrudes.
var bubblePointX = new Array; // Coordinates of the point from which the bubble protrudes.
var bubblePointY = new Array;
var personNames = new Array;
var personTestimonials = new Array;


// Used for custom mouse tracking and animation control. Initialize to “no person”.
var NO_PERSON_INDEX = -1;
var currentPerson = null;
var currentPersonIndex = NO_PERSON_INDEX;
var previousPerson = null;
var previousPersonIndex = NO_PERSON_INDEX;

var indexOfPersonToBeShown = NO_PERSON_INDEX; // Compare indices because person objects may be at different addresses (?).
var showPersonDelayTimerLabel = 'showPersonDelayTimer';
var showBubbleDelayTimerLabel = 'showBubbleDelayTimer';


// Animation.
var autoAnimationLabel = 'autoAnimation';
var previousAutoAnimatedPerson = null;
var previousAutoAnimatedPerson2 = null;



jQuery(document).ready
(
	function()
	{
		// Start automatic animation before any user interaction occurs.
		if (autoAnimationEnabled == true)
		{
			startAutoAnimation();
		}
		
		
		// Automatic animation.
		
		function startAutoAnimation()
		{
			//window.console.log("startAutoAnimation");
			$('.stage').everyTime(autoAnimationInterval, autoAnimationLabel, autoAnimateNextPerson);
		}
		
		
		function stopAutoAnimation()
		{
			//window.console.log("stopAutoAnimation");
			$('.stage').stopTime(autoAnimationLabel, autoAnimateNextPerson);
			resetAutoAnimatedPerson();
		}
		
		
		function autoAnimateNextPerson()
		{
			//window.console.log("autoAnimateNextPerson");
			// Pick new person.
			// With two previous persons stored, the same person will not be repeated until at least two others have been shown.
			// We need, however, to account for cases where there are less than three persons in total.
			var personCount = $('.person').length;
			if (personCount < 3)
			{
				previousAutoAnimatedPerson2 = null; // Make while loop break.
			}
			// NO else here but continue with more specific case.
			if (personCount < 2)
			{
				previousAutoAnimatedPerson = null; // Make while loop break.
			}
			
			var nextAutoAnimatedPerson;
			do
			{
				nextAutoAnimatedPerson = $('.person').get( Math.floor( Math.random() * personCount) );
			}
			while ( (nextAutoAnimatedPerson == previousAutoAnimatedPerson) || (nextAutoAnimatedPerson == previousAutoAnimatedPerson2) );
			
			// These must be set for showPersonAfterDelay() to work.
			currentPerson = nextAutoAnimatedPerson;
			currentPersonIndex = $('.person').index(currentPerson);
			indexOfPersonToBeShown = currentPersonIndex;

			resetAutoAnimatedPerson();
			
			previousAutoAnimatedPerson2 = previousAutoAnimatedPerson;
			previousAutoAnimatedPerson = nextAutoAnimatedPerson;

			// Animate next person.
			showPersonAfterDelay();
		}
		
		
		function resetAutoAnimatedPerson()
		{
			//window.console.log("resetAutoAnimatedPerson");
			// Stop current auto-animation, if any.
			$(previousAutoAnimatedPerson).hidePerson();
		}
		
		
		// Implement our own mouse tracking because .mouseenter and .mouseleave are often called multiple times 
		// while the mouse cursor is still moving over the same element.

		$('body')
		
		.mousemove
		(
			function(anEvent) // This parameter is needed by Firefox.
			{
				// Capture the event in case it is not passed in (IE?).
				if ( ! anEvent )
				{
					anEvent = window.event;
				}

				// Filter person element that contains the current mouse position.
				currentPersonIndex = NO_PERSON_INDEX;
				currentPerson = $('.person').filter
				(
					function(index)
					{
						var isPersonHit = $(this).hitTest(anEvent);
						if (isPersonHit)
						{
							currentPersonIndex = index;
						}
						return isPersonHit;
					}
				)
				.first(); // Even if there were more than one person hit simultaneously (which is virtually impossible), we are interested in the first one only.

				//window.console.log(event.timeStamp + ", body.mousemove, currentPersonIndex: " + currentPersonIndex);

				// Trigger custom mouse entered/left functions.
				if (currentPersonIndex != previousPersonIndex)
				{
					if (previousPersonIndex > NO_PERSON_INDEX)
					{
						previousPerson.mouseLeft();
					}
					
					if (currentPersonIndex > NO_PERSON_INDEX)
					{
						currentPerson.mouseEntered();
					}
					else
					{
						// Mouse cursor is outside stage.
						if (autoAnimationEnabled == true)
						{
							startAutoAnimation();
						}
					}
				}
				
				// Save state for next mouse moved event.
				previousPerson = currentPerson;
				previousPersonIndex = currentPersonIndex;
			}
		)
		
		
		$.fn.hitTest = function(anEvent)
		{
			var x = anEvent.pageX;
			var y = anEvent.pageY;
			var offset = $(this).offset();
			var minX = offset.left;
			var minY = offset.top;
			var maxX = minX + $(this).width() - 1;
			var maxY = minY + $(this).height() - 1;
			return (x >= minX) && (x <= maxX) && (y >= minY) && (y <= maxY);
		}


		$.fn.mouseEntered = function()
		{
			//window.console.log("mouseEntered, " + $('.person').index(this));
			if (autoAnimationEnabled == true)
			{
				stopAutoAnimation();
			}
			
			indexOfPersonToBeShown = currentPersonIndex;
			$(this).oneTime( showPersonDelay, showPersonDelayTimerLabel, showPersonAfterDelay );
		}


		$.fn.mouseLeft = function()
		{
			//window.console.log("mouseLeft, " + $('.person').index(this));
			indexOfPersonToBeShown = NO_PERSON_INDEX;
			$(this).stopTime( showPersonDelayTimerLabel, showPersonAfterDelay );
			
			// Hide only if actually visible.
			if ( $('.person_large > img:visible', this).length > 0 )
			{
				$(this).hidePerson();
			}
		}
		
		
		// Timer callback. If the mouse is still over the same person and no other animation is running, show it.
		
		function showPersonAfterDelay()
		{
			//window.console.log("showPersonAfterDelay, " + indexOfPersonToBeShown);
			if (indexOfPersonToBeShown != currentPersonIndex)
			{
				//window.console.log("personToBeShown != currentPerson");
				return;
			}
			//window.console.log("personToBeShown == currentPerson");
			
			if ( ($('.person_large > img:animated').length > 0) || ($('#bubble:animated').length > 0) )
			{
				$(currentPerson).stopTime( showPersonDelayTimerLabel, showPersonAfterDelay );
				$(currentPerson).oneTime( showPersonTestInterval, showPersonDelayTimerLabel, showPersonAfterDelay );
				return;
			}
			
			$(currentPerson).showPerson();
		}


		// Visual effects. Note: fadeIn and fadeOut are existing jQuery functions, so we need to pick unique names.
		
		$.fn.showPerson = function() // TODO: make this and hidePerson global functions?
		{
			// Must be determined here so it can be passed to the callback (“this” has a different meaning there).
			var personIndex = $('.person').index(this);
			//window.console.log("showPerson, " + personIndex);

			$('.person_large > img', this).animate
			(
				{
					opacity: 'show'
				}, 
				personFadeInDuration, 
				'linear', 
				null
			);
			
			// Bubble should begin fading in before person is finished with fading in.
			$('#bubble').oneTime( bubbleFadeInDelay, showBubbleDelayTimerLabel, showBubble );
		}


		$.fn.hidePerson = function()
		{
			// Must be determined here so it can be passed to the callback (“this” has a different meaning there).
			var personIndex = $('.person').index(this);
			//window.console.log("hidePerson, " + personIndex);
			
			hideBubble();

			$('.person_large > img', this).animate
			(
				{
					opacity: 'hide'
				}, 
				personFadeOutDuration, 
				'linear', 
				null
			);
		}


		// Animation callback that will be called when showPerson finishes.

		function showBubble()
		{
			var personIndex = currentPersonIndex;
			//window.console.log("showBubble, " + personIndex);
			
			if (personIndex == NO_PERSON_INDEX)
			{
				return;
			}
			//window.console.log("actually showing bubble, " + personIndex);

			var bubble = $('div#bubble');
			
			// Load bubble text.
			$('div.bubble_content_text').html('<b>' + personNames[personIndex] + '</b><br>„' + personTestimonials[personIndex] + '“');
			
			// Switch class so bubble will point in the correct direction.
			if ( (bubbleDirection[personIndex] == 'right') && (bubble.hasClass('bubble_left')) )
			{
				bubble.removeClass('bubble_left').addClass('bubble_right');
			}
			else if ( (bubbleDirection[personIndex] == 'left') && (bubble.hasClass('bubble_right')) )
			{
				bubble.removeClass('bubble_right').addClass('bubble_left');
			}
			
			// Position bubble according to its direction. (.position() does not seem to work properly, so we need to sum the widths ...)
			var widthsOfPersonsToTheLeft = 0;
			$('.person:lt(' + personIndex + ')')
			.each
			(
				function()
				{
					widthsOfPersonsToTheLeft += $(this).width();
				}
			);
			var directionalBubbleOffset = (bubbleDirection[personIndex] == 'right') ? -pointPaddingX : -( bubble.width() ) + pointPaddingX;
			var bubbleLeft = widthsOfPersonsToTheLeft + directionalBubbleOffset + bubblePointX[personIndex];
			bubble.css('left', bubbleLeft + 'px');
			
			var bubblePointTargetY = bubblePointY[personIndex] - pointPaddingY;
			
			$('#bubble')
			.css('top', bubblePointTargetY - bubbleFadeDistance + 'px')
			.animate
			(
				{
					opacity: 'show',
					top: bubblePointTargetY + 'px'
				},
				bubbleFadeInDuration,
				'swing',
				null
			);
		};


		function hideBubble()
		{
			//window.console.log("hideBubble");
			$('#bubble:visible').hide();
		};


	}
);

