/**
 * @package     VikBooking
 * @subpackage  vikDotsSlider
 * @version 	1.2.0
 * @author      Alessio Gaggii - E4J srl
 * @copyright   Copyright (C) 2021 E4J srl. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 * @link        https://e4j.com - https://vikwp.com
 */

(function($) {

	var VikDotsSlider = function(elem, options) {

		this.sliderElement = elem;
		this.sliderDataName = 'vik-dots-slider-id';
		this.sliderIdPrefix = 'vik-dots-slider-id-';
		this.xDown = null;
		this.yDown = null;
		this.preloadedImages = [];
		this.currentSlide = 0;
		this.previousSlide = 0;
		this.sliderContainer = null;

		this.defaultSettings = {
			images: [],
			captions: [],
			containerClass: 'vik-dots-slider-container',
			containerHeight: '260px',
			innerClass: 'vik-dots-slider-inner',
			slidesClass: 'vik-dots-slider-slides',
			slideClass: 'vik-dots-slider-slide',
			captionClass: 'vik-dots-slider-slide-caption',
			slideStartClass: 'vik-dots-slider-slide-start',
			slideLeftCurrClass: 'vik-dots-slider-slide-leftcurr',
			slideLeftNextClass: 'vik-dots-slider-slide-leftnext',
			slideRightNextClass: 'vik-dots-slider-slide-rightnext',
			slideRightCurrClass: 'vik-dots-slider-slide-rightcurr',
			dotsContainerClass: 'vik-dots-slider-dots',
			dotClass: 'vik-dots-slider-dot',
			activeDotClass: 'vik-dots-slider-dot-active',
			hasmoreDotClass: 'vik-dots-slider-dot-hasmore',
			maxDots: 5,
			navButContainerClass: 'vik-dots-slider-navbuttons',
			navButClass: 'vik-dots-slider-navbutton',
			navButPrevClass: 'vik-dots-slider-navbutton-prev',
			navButNextClass: 'vik-dots-slider-navbutton-next',
			navButPrevContent: '&lt;',
			navButNextContent: '&gt;',
			enableGestures: true,
			onDisplaySlide: function() {},
		};

		this.settings = $.extend(true, {}, this.defaultSettings, options);

		this.options = function(options) {
			return options ? $.extend(true, this.settings, options) : this.settings;
		};

		this.countSlides = function() {
			return this.settings.images.length;
		};

		this.generateSliderId = function() {
			return ((parseInt(Math.random() * 1000)) + '');
		};

		this.goToSlide = function(e) {
			if (e) {
				e.preventDefault();
			}
			// retrieve dot from data passed to event handler
			var dot = jQuery(e.data.dot);
			var gotopos = dot.data('pos');
			if (gotopos == null || gotopos == this.currentSlide) {
				return false;
			}
			if (gotopos > this.currentSlide) {
				// show requested slide navigating left
				this.showSlide((gotopos + 1));
			} else {
				// show requested slide navigating right
				this.showSlide((gotopos + 1), true);
			}
		};

		this.prevSlide = function(e) {
			if (e) {
				e.preventDefault();
			}
			var gotopos = this.currentSlide - 1;
			if (gotopos < 0) {
				gotopos = (this.countSlides() - 1);
			} else if (gotopos >= this.countSlides()) {
				gotopos = 0;
			}
			if (gotopos == this.currentSlide) {
				return false;
			}
			if (gotopos > this.currentSlide) {
				// show requested slide navigating left
				this.showSlide((gotopos + 1));
			} else {
				// show requested slide navigating right
				this.showSlide((gotopos + 1), true);
			}
		};

		this.nextSlide = function(e) {
			if (e) {
				e.preventDefault();
			}
			var gotopos = this.currentSlide + 1;
			if (gotopos < 0) {
				gotopos = (this.countSlides() - 1);
			} else if (gotopos >= this.countSlides()) {
				gotopos = 0;
			}
			if (gotopos == this.currentSlide) {
				return false;
			}
			if (gotopos > this.currentSlide) {
				// show requested slide navigating left
				this.showSlide((gotopos + 1));
			} else {
				// show requested slide navigating right
				this.showSlide((gotopos + 1), true);
			}
		};

		this.getSlide = function(i) {
			if (this.sliderContainer == null) {
				return [];
			}

			return this.sliderContainer.find('.' + this.settings.slideClass + '[data-pos="' + i + '"]');
		};

		this.getDot = function(i) {
			if (this.sliderContainer == null) {
				return [];
			}

			return this.sliderContainer.find('.' + this.settings.dotClass + '[data-pos="' + i + '"]');
		};

		this.getDotByIndex = function(i) {
			if (this.sliderContainer == null) {
				return [];
			}

			return this.sliderContainer.find('.' + this.settings.dotClass).eq(i);
		};

		this.showSlide = function(pos, right) {
			if (this.sliderContainer == null) {
				return false;
			}

			var animatingClasses = [
				this.settings.slideLeftCurrClass,
				this.settings.slideLeftNextClass,
				this.settings.slideRightNextClass,
				this.settings.slideRightCurrClass,
			];
			var removeClasses = animatingClasses.join(' ');

			if (!pos) {
				pos = ((this.currentSlide + 1) < this.countSlides() ? (this.currentSlide + 2) : 1);
			}

			--pos;
			// update previous slide
			this.getSlide(this.previousSlide).removeClass(removeClasses);

			if (!right) {
				this.getSlide(pos).removeClass(removeClasses).addClass(this.settings.slideLeftNextClass);
				this.getSlide(this.currentSlide).removeClass(removeClasses).addClass(this.settings.slideLeftCurrClass);
			} else {
				this.getSlide(pos).removeClass(removeClasses).addClass(this.settings.slideRightNextClass);
				this.getSlide(this.currentSlide).removeClass(removeClasses).addClass(this.settings.slideRightCurrClass);
			}

			this.previousSlide = this.currentSlide;
			this.currentSlide = pos;

			var currentDot = this.getDot(this.currentSlide);
			if (currentDot.length) {
				currentDot.addClass(this.settings.activeDotClass);
			}
			var prevDot = this.getDot(this.previousSlide);
			if (prevDot.length) {
				prevDot.removeClass(this.settings.activeDotClass);
			}

			// update dots pos attributes in case max dots was reached
			if (this.countSlides() > this.settings.maxDots && this.settings.maxDots > 1) {
				// dots cannot represent all slides
				var isFirstSlide = (this.currentSlide === 0);
				var isLastSlide = ((this.currentSlide + 1) == this.countSlides());

				if (isFirstSlide) {
					// renumber dots from the beginning
					for (var d = 0; d < this.settings.maxDots; d++) {
						var dot = this.getDotByIndex(d);
						if (dot.length) {
							dot.data('pos', d).attr('data-pos', d);
							if (d === 0) {
								dot.addClass(this.settings.activeDotClass).removeClass(this.settings.hasmoreDotClass);
							} else {
								dot.removeClass(this.settings.activeDotClass);
								if (d === (this.settings.maxDots - 1)) {
									dot.addClass(this.settings.hasmoreDotClass);
								}
							}
						}
					}
				} else if (isLastSlide) {
					// renumber dots from the end
					var missingdots = (this.countSlides() - this.settings.maxDots);
					for (var d = 0; d < this.settings.maxDots; d++) {
						var dot = this.getDotByIndex(d);
						if (dot.length) {
							var newpos = d + missingdots;
							dot.data('pos', newpos).attr('data-pos', newpos);
							if (d === (this.settings.maxDots - 1)) {
								dot.addClass(this.settings.activeDotClass).removeClass(this.settings.hasmoreDotClass);
							} else {
								dot.removeClass(this.settings.activeDotClass);
								if (d === 0) {
									dot.addClass(this.settings.hasmoreDotClass);
								}
							}
						}
					}
				} else  {
					// we are in the middle, renumber dots
					var dotselindex = (this.currentSlide + 1) < this.settings.maxDots ? this.currentSlide : (this.settings.maxDots - 2);
					var dotaddpos = (this.currentSlide + 1) < this.settings.maxDots ? 0 : (this.currentSlide - this.settings.maxDots + 2);
					var dothasmoreindex = (this.currentSlide + 1) >= this.settings.maxDots ? [0, dotselindex + 1] : [-1, this.settings.maxDots - 1];
					for (var d = 0; d < this.settings.maxDots; d++) {
						var dot = this.getDotByIndex(d);
						if (dot.length) {
							var newpos = d + dotaddpos;
							dot.data('pos', newpos).attr('data-pos', newpos);
							if (d == dotselindex) {
								dot.addClass(this.settings.activeDotClass);
							} else {
								dot.removeClass(this.settings.activeDotClass);
							}
							if (d === dothasmoreindex[0] || d === dothasmoreindex[1]) {
								dot.addClass(this.settings.hasmoreDotClass);
							} else {
								dot.removeClass(this.settings.hasmoreDotClass);
							}
						}
					}
				}
			}
		};

		this.generateNavButtons = function() {
			var navbuttons = $('<div></div>').addClass(this.settings.navButContainerClass);
			if (this.countSlides() < 2) {
				return navbuttons;
			}
			var navprev = $('<span></span>').addClass(this.settings.navButClass + ' ' + this.settings.navButPrevClass).append(this.settings.navButPrevContent).on('click', this.prevSlide.bind(this));
			var navnext = $('<span></span>').addClass(this.settings.navButClass + ' ' + this.settings.navButNextClass).append(this.settings.navButNextContent).on('click', this.nextSlide.bind(this));
			navbuttons.append(navprev).append(navnext);

			return navbuttons;
		};

		this.generateDots = function() {
			var dots = $('<div></div>').addClass(this.settings.dotsContainerClass);
			var tot_slides = this.countSlides();
			if (this.settings.maxDots < 1 || tot_slides < 2) {
				return dots;
			}
			var canfitall = (this.settings.maxDots >= tot_slides);
			for (var i = 0; i < tot_slides; i++) {
				// generate dot, set pos attribute
				var dot = $('<span></span>').addClass(this.settings.dotClass).data('pos', i).attr('data-pos', i);
				// define click handler by passing the collection and by binding thisArg to the class instance
				dot.on('click', {dot: dot}, this.goToSlide.bind(this))
				if (i == this.currentSlide) {
					dot.addClass(this.settings.activeDotClass);
				}
				if (!canfitall && i == (this.settings.maxDots - 1)) {
					dot.addClass(this.settings.hasmoreDotClass);
				}
				dots.append(dot);
				if ((i + 1) == this.settings.maxDots) {
					break;
				}
			}
			
			return dots;
		};

		this.generateSlides = function() {
			var slides = $('<div></div>').addClass(this.settings.slidesClass);
			for (var i = 0; i < this.countSlides(); i++) {
				var slide = $('<div></div>').data('pos', i).attr('data-pos', i).addClass(this.settings.slideClass);
				if (i == this.currentSlide) {
					slide.addClass(this.settings.slideStartClass);
				}
				if (this.settings.captions.hasOwnProperty(i) && this.settings.captions[i].length) {
					var caption = $('<span></span>').addClass(this.settings.captionClass).text(this.settings.captions[i]);
					slide.append(caption);
				}
				var photo = $('<img src="' + this.settings.images[i] + '" />');
				slide.append(photo);

				// fire onDisplaySlide callback by passing the whole slide content
				this.settings.onDisplaySlide.call(slide);

				// append slide
				slides.append(slide);
			}
			
			return slides;
		};

		this.sliderTouches = function(e) {
			if (!this.settings.enableGestures) {
				return false;
			}
			if (e.touches) {
				// JS
				return e.touches;
			}
			if (e.originalEvent.touches) {
				// jQuery
				return e.originalEvent.touches;
			}
			return false;
		};

		this.sliderTouchStart = function(e) {
			var firstTouch = this.sliderTouches(e)[0];
			if (firstTouch === false) {
				return;
			}
			this.xDown = firstTouch.clientX;
			this.yDown = firstTouch.clientY;
		};

		this.sliderTouchMove = function(e) {
			if (!this.xDown || !this.yDown || this.countSlides() < 2) {
				return;
			}
			// register touch positions
			var xUp = e.touches[0].clientX;
			var yUp = e.touches[0].clientY;
			var xDiff = this.xDown - xUp;
			var yDiff = this.yDown - yUp;

			// detect gesture type
			if (Math.abs(xDiff) > Math.abs(yDiff)) {
				if (xDiff > 0) {
					// left swipe, navigate forward
					this.nextSlide(e);
				} else {
					// right swipe, navigate backward
					this.prevSlide(e);
				}
			} else {
				if (yDiff > 0) {
					// up swipe is ignored
				} else {
					// down swipe is ignored
				}
			}
			
			// reset vars to handle the next touch gesture
			this.xDown = null;
			this.yDown = null;
		};

		this.registerGestures = function(elem) {
			if (!elem) {
				return;
			}
			elem.addEventListener('touchstart', this.sliderTouchStart.bind(this), false);
			elem.addEventListener('touchmove', this.sliderTouchMove.bind(this), false);
		};

		this.preloadURLs = function(urls) {
			if (urls == null || !urls.length) {
				return;
			}
			for (var i = 0; i < urls.length; i++) {
				var photoslide = (new Image()).src = urls[i];
				this.preloadedImages.push(photoslide);
			}
		};

		this.display = function() {
			// update global vars
			this.currentSlide = 0;
			this.previousSlide = (this.countSlides() - 1);

			// if an instance is already present in the current element, remove it
			if (this.sliderElement.find('.' + this.settings.containerClass).length) {
				this.sliderElement.find('.' + this.settings.containerClass).remove();
			}

			// generate slider random ID
			var sliderId = this.generateSliderId();

			// generate slider container by updating global var
			this.sliderContainer = $('<div></div>').addClass(this.settings.containerClass).data(this.sliderDataName, sliderId).attr('id', this.sliderIdPrefix + sliderId);

			// register gesture events for mobiles
			this.registerGestures(this.sliderContainer[0]);

			// builder slider inner
			var inner = $('<div></div>').addClass(this.settings.innerClass).css('height', this.settings.containerHeight);
			
			// build slider container
			this.sliderContainer.append(
				inner.append(
					this.generateSlides().append(
						this.generateNavButtons()
					)
				).append(
					this.generateDots()
				)
			);

			// append slider to current selector
			this.sliderElement.append(this.sliderContainer);
		};

		this.update = function() {
			this.destroy();
			this.display();
		};

		this.destroy = function() {
			this.sliderContainer.remove();
		};

		this.preloadImages = function(gallery) {
			gallery = gallery[0];
			if (gallery == null) {
				return false;
			}
			
			var preloadQueue = {};
			
			for (var gallery_id in gallery) {
				if (!gallery.hasOwnProperty(gallery_id)) {
					continue;
				}
				if (typeof gallery[gallery_id] == 'string' && gallery[gallery_id].indexOf('http') >= 0) {
					// we have an array of photo URLs
					var photoslide = (new Image()).src = gallery[gallery_id];
					this.preloadedImages.push(photoslide);
					continue;
				}
				if (typeof gallery[gallery_id] == 'object' && gallery[gallery_id].length) {
					// we have a gallery object with the photos of each room so we queue the preloading
					for (var i = 0; i < gallery[gallery_id].length; i++) {
						if (!preloadQueue.hasOwnProperty(i)) {
							preloadQueue[i] = [];
						}
						// push the photo url for later loading
						preloadQueue[i].push(gallery[gallery_id][i]);
					}
				}
			}

			// if some preloading was queued, define the loading intervals
			var queue_timer = 0;
			for (var i in preloadQueue) {
				if (!preloadQueue.hasOwnProperty(i)) {
					continue;
				}
				// schedule preloading with timeout
				setTimeout(this.preloadURLs.bind(this, preloadQueue[i]), queue_timer);
				// increase timer for next scheduling (1 second per photo)
				queue_timer += (1000 * preloadQueue[i].length);
			}

			return this.preloadedImages;
		};

	};

	$.fn.vikDotsSlider = function(methodOrOptions) {

		var method = typeof methodOrOptions === 'string' ? methodOrOptions : null;

		if (method) {
			var sliderInstances = [];

			function getVikDotsSliderInstance() {
				var vikDotsSlider = $(this).data('vikDotsSlider');
				sliderInstances.push(vikDotsSlider);
			}

			this.each(getVikDotsSliderInstance);

			var args    = (arguments.length > 1) ? Array.prototype.slice.call(arguments, 1) : null;
			var results = [];

			function applyMethod(index) {
				var vikDotsSlider = sliderInstances[index];

				if (!vikDotsSlider) {
					if (method == 'preloadImages') {
						// preloadImages is the only method that can be called before instantiating vikDotsSlider
						vikDotsSlider = new VikDotsSlider($(this), {});
						var result = vikDotsSlider[method].call(vikDotsSlider, $(this));
						results.push(result);
						return;
					} else {
						console.error('$.vikDotsSlider not instantiated yet');
						results.push(null);
						return;
					}
				}

				if (typeof vikDotsSlider[method] === 'function') {
					var result = vikDotsSlider[method].apply(vikDotsSlider, args);
					results.push(result);
				} else {
					console.error('Method \'' + method + '\' is undefined in $.vikDotsSlider');
				}
			}

			this.each(applyMethod);

			return (results.length > 1) ? results : results[0];
		} else {
			var options = (typeof methodOrOptions === 'object') ? methodOrOptions : null;

			function init() {
				var vikDotsSlider = new VikDotsSlider($(this), options);

				vikDotsSlider.display();

				$(this).data('vikDotsSlider', vikDotsSlider);
			}

			return this.each(init);
		}

	};

})(jQuery);
