// Thumbnail Touch Swipe module for Slider Pro.
// 
// Adds touch-swipe functionality for thumbnails.
;(function( window, $ ) {

	"use strict";
	
	var NS = 'ThumbnailTouchSwipe.' + $.SliderPro.namespace;

	var ThumbnailTouchSwipe = {

		// The x and y coordinates of the pointer/finger's starting position
		thumbnailTouchStartPoint: { x: 0, y: 0 },

		// The x and y coordinates of the pointer/finger's end position
		thumbnailTouchEndPoint: { x: 0, y: 0 },

		// The distance from the starting to the end position on the x and y axis
		thumbnailTouchDistance: { x: 0, y: 0 },

		// The position of the thumbnail scroller when the touch swipe starts
		thumbnailTouchStartPosition: 0,

		// Indicates if the thumbnail scroller is being swiped
		isThumbnailTouchMoving: false,

		// Indicates if the touch swipe was initialized
		isThumbnailTouchSwipe: false,

		// Stores the names of the events
		thumbnailTouchSwipeEvents: { startEvent: '', moveEvent: '', endEvent: '' },

		// Indicates whether the previous 'start' event was a 'touchstart' or 'mousedown'
		thumbnailPreviousStartEvent: '',

		initThumbnailTouchSwipe: function() {
			this.on( 'update.' + NS, $.proxy( this._thumbnailTouchSwipeOnUpdate, this ) );
		},

		_thumbnailTouchSwipeOnUpdate: function() {

			// Return if there are no thumbnails
			if ( this.isThumbnailScroller === false ) {
				return;
			}

			// Initialize the touch swipe functionality if it wasn't initialized yet
			if ( this.settings.thumbnailTouchSwipe === true && this.isThumbnailTouchSwipe === false ) {
				this.isThumbnailTouchSwipe = true;

				this.thumbnailTouchSwipeEvents.startEvent = 'touchstart' + '.' + NS + ' mousedown' + '.' + NS;
				this.thumbnailTouchSwipeEvents.moveEvent = 'touchmove' + '.' + NS + ' mousemove' + '.' + NS;
				this.thumbnailTouchSwipeEvents.endEvent = 'touchend' + '.' + this.uniqueId + '.' + NS + ' mouseup' + '.' + this.uniqueId + '.' + NS;
				
				// Listen for touch swipe/mouse move events
				this.$thumbnails.on( this.thumbnailTouchSwipeEvents.startEvent, $.proxy( this._onThumbnailTouchStart, this ) );
				this.$thumbnails.on( 'dragstart.' + NS, function( event ) {
					event.preventDefault();
				});
			
				// Add the grabbing icon
				this.$thumbnails.addClass( 'sp-grab' );
			}

			// Remove the default thumbnailClick
			$.each( this.thumbnails, function( index, thumbnail ) {
				thumbnail.off( 'thumbnailClick' );
			});
		},

		// Called when the thumbnail scroller starts being dragged
		_onThumbnailTouchStart: function( event ) {

			// Return if a 'mousedown' event follows a 'touchstart' event
			if ( event.type === 'mousedown' && this.thumbnailPreviousStartEvent === 'touchstart' ) {
				this.thumbnailPreviousStartEvent = event.type;
				return;
			}

			// Assign the new 'start' event
			this.thumbnailPreviousStartEvent = event.type;

			// Disable dragging if the element is set to allow selections
			if ( $( event.target ).closest( '.sp-selectable' ).length >= 1 ) {
				return;
			}

			var that = this,
				eventObject = typeof event.originalEvent.touches !== 'undefined' ? event.originalEvent.touches[0] : event.originalEvent;

			// Prevent default behavior for mouse events
			if ( typeof event.originalEvent.touches === 'undefined' ) {
				event.preventDefault();
			}

			// Disable click events on links
			$( event.target ).parents( '.sp-thumbnail-container' ).find( 'a' ).one( 'click.' + NS, function( event ) {
				event.preventDefault();
			});

			// Get the initial position of the mouse pointer and the initial position
			// of the thumbnail scroller
			this.thumbnailTouchStartPoint.x = eventObject.pageX || eventObject.clientX;
			this.thumbnailTouchStartPoint.y = eventObject.pageY || eventObject.clientY;
			this.thumbnailTouchStartPosition = this.thumbnailsPosition;

			// Clear the previous distance values
			this.thumbnailTouchDistance.x = this.thumbnailTouchDistance.y = 0;

			// If the thumbnail scroller is being grabbed while it's still animating, stop the
			// current movement
			if ( this.$thumbnails.hasClass( 'sp-animated' ) ) {
				this.isThumbnailTouchMoving = true;
				this._stopThumbnailsMovement();
				this.thumbnailTouchStartPosition = this.thumbnailsPosition;
			}

			// Listen for move and end events
			this.$thumbnails.on( this.thumbnailTouchSwipeEvents.moveEvent, $.proxy( this._onThumbnailTouchMove, this ) );
			$( document ).on( this.thumbnailTouchSwipeEvents.endEvent, $.proxy( this._onThumbnailTouchEnd, this ) );

			// Swap grabbing icons
			this.$thumbnails.removeClass( 'sp-grab' ).addClass( 'sp-grabbing' );

			// Add 'sp-swiping' class to indicate that the thumbnail scroller is being swiped
			this.$thumbnailsContainer.addClass( 'sp-swiping' );
		},

		// Called during the thumbnail scroller's dragging
		_onThumbnailTouchMove: function( event ) {
			var eventObject = typeof event.originalEvent.touches !== 'undefined' ? event.originalEvent.touches[0] : event.originalEvent;

			// Indicate that the move event is being fired
			this.isThumbnailTouchMoving = true;

			// Get the current position of the mouse pointer
			this.thumbnailTouchEndPoint.x = eventObject.pageX || eventObject.clientX;
			this.thumbnailTouchEndPoint.y = eventObject.pageY || eventObject.clientY;

			// Calculate the distance of the movement on both axis
			this.thumbnailTouchDistance.x = this.thumbnailTouchEndPoint.x - this.thumbnailTouchStartPoint.x;
			this.thumbnailTouchDistance.y = this.thumbnailTouchEndPoint.y - this.thumbnailTouchStartPoint.y;
			
			// Calculate the distance of the swipe that takes place in the same direction as the orientation of the thumbnails
			// and calculate the distance from the opposite direction.
			// 
			// For a swipe to be valid there should more distance in the same direction as the orientation of the thumbnails.
			var distance = this.thumbnailsOrientation === 'horizontal' ? this.thumbnailTouchDistance.x : this.thumbnailTouchDistance.y,
				oppositeDistance = this.thumbnailsOrientation === 'horizontal' ? this.thumbnailTouchDistance.y : this.thumbnailTouchDistance.x;

			// If the movement is in the same direction as the orientation of the thumbnails, the swipe is valid
			if ( Math.abs( distance ) > Math.abs( oppositeDistance ) ) {
				event.preventDefault();
			} else {
				return;
			}

			// Make the thumbnail scroller move slower if it's dragged outside its bounds
			if ( this.thumbnailsPosition >= 0 ) {
				var infOffset = - this.thumbnailTouchStartPosition;
				distance = infOffset + ( distance - infOffset ) * 0.2;
			} else if ( this.thumbnailsPosition <= - this.thumbnailsSize + this.thumbnailsContainerSize ) {
				var supOffset = this.thumbnailsSize - this.thumbnailsContainerSize + this.thumbnailTouchStartPosition;
				distance = - supOffset + ( distance + supOffset ) * 0.2;
			}
			
			this._moveThumbnailsTo( this.thumbnailTouchStartPosition + distance, true );
		},

		// Called when the thumbnail scroller is released
		_onThumbnailTouchEnd: function( event ) {
			var that = this,
				thumbnailTouchDistance = this.thumbnailsOrientation === 'horizontal' ? this.thumbnailTouchDistance.x : this.thumbnailTouchDistance.y;

			// Remove the move and end listeners
			this.$thumbnails.off( this.thumbnailTouchSwipeEvents.moveEvent );
			$( document ).off( this.thumbnailTouchSwipeEvents.endEvent );

			// Swap grabbing icons
			this.$thumbnails.removeClass( 'sp-grabbing' ).addClass( 'sp-grab' );

			// Check if there is intention for a tap/click
			if ( this.isThumbnailTouchMoving === false ||
				this.isThumbnailTouchMoving === true &&
				Math.abs( this.thumbnailTouchDistance.x ) < 10 &&
				Math.abs( this.thumbnailTouchDistance.y ) < 10
			) {
				var targetThumbnail = $( event.target ).hasClass( 'sp-thumbnail-container' ) ? $( event.target ) : $( event.target ).parents( '.sp-thumbnail-container' ),
					index = targetThumbnail.index();

				// If a link is cliked, navigate to that link, else navigate to the slide that corresponds to the thumbnail
				if ( $( event.target ).parents( 'a' ).length !== 0 ) {
					$( event.target ).parents( 'a' ).off( 'click.' + NS );
					this.$thumbnailsContainer.removeClass( 'sp-swiping' );
				} else if ( index !== this.selectedThumbnailIndex && index !== -1 ) {
					this.gotoSlide( index );
				}

				return;
			}

			this.isThumbnailTouchMoving = false;

			$( event.target ).parents( '.sp-thumbnail' ).one( 'click', function( event ) {
				event.preventDefault();
			});

			// Remove the 'sp-swiping' class but with a delay
			// because there might be other event listeners that check
			// the existence of this class, and this class should still be 
			// applied for those listeners, since there was a swipe event
			setTimeout(function() {
				that.$thumbnailsContainer.removeClass( 'sp-swiping' );
			}, 1 );

			// Keep the thumbnail scroller inside the bounds
			if ( this.thumbnailsPosition > 0 ) {
				this._moveThumbnailsTo( 0 );
			} else if ( this.thumbnailsPosition < this.thumbnailsContainerSize - this.thumbnailsSize ) {
				this._moveThumbnailsTo( this.thumbnailsContainerSize - this.thumbnailsSize );
			}

			// Fire the 'thumbnailsMoveComplete' event
			this.trigger({ type: 'thumbnailsMoveComplete' });
			if ( $.isFunction( this.settings.thumbnailsMoveComplete ) ) {
				this.settings.thumbnailsMoveComplete.call( this, { type: 'thumbnailsMoveComplete' });
			}
		},

		// Destroy the module
		destroyThumbnailTouchSwipe: function() {
			this.off( 'update.' + NS );

			if ( this.isThumbnailScroller === false ) {
				return;
			}

			this.$thumbnails.off( this.thumbnailTouchSwipeEvents.startEvent );
			this.$thumbnails.off( this.thumbnailTouchSwipeEvents.moveEvent );
			this.$thumbnails.off( 'dragstart.' + NS );
			$( document ).off( this.thumbnailTouchSwipeEvents.endEvent );
			this.$thumbnails.removeClass( 'sp-grab' );
		},

		thumbnailTouchSwipeDefaults: {

			// Indicates whether the touch swipe will be enabled for thumbnails
			thumbnailTouchSwipe: true
		}
	};

	$.SliderPro.addModule( 'ThumbnailTouchSwipe', ThumbnailTouchSwipe );

})( window, jQuery );