(function($, window, document, undefined) {
    "use strict";

    var masterSlider = function(elm, opts) {
        this.elm = elm;
        this.$elm = $(elm);
        this.DOM = {};
        this.opts = opts;
        this.config = this.$elm.data("config");
    };

    masterSlider.prototype = {
        defaults: {
        	bullets: true,
        	arrows: true,
        	pageNumbers: true,
            autoplay: true,
            duration: 6000
        },
        init: function() {
            this.args = $.extend({}, this.defaults, this.opts, this.config);
            var t = this;
            console.log(t.args)

            // some animation settings.
			t.settings = {
				image: {duration: 900, delay: 0, easing: "cubicBezier(0.8,0,0.2,1)"},
				title: {duration: 700, delay: 200, easing: "cubicBezier(0.8,0,0.2,1)"},
				description: {duration: 500, delay: 100, easing: 'easeOutExpo'},
				pagination: {duration: 200, delay: 100, easing: 'easeInOutQuad'},
			};

            // init variables
            t.arrows = t.$elm.find(".slider-nav"),
            t.prevCtrl = t.$elm.find(".button-prev"),
            t.nextCtrl = t.$elm.find(".button-next"),

            t.entries = t.$elm.find(".slider"),
            t.currentEntry = t.entries[0],
            t.newEntry = t.entries[1],
            t.direction = "next",
            t.currentPos = 0,
            t.entriesTotal = t.$elm.find(".slider").length,
            t.isHidden = true,

            // Set Active
            t.$elm.find(".slider").first().addClass("active")
            
            // Nav Scroll
            t.navScroll = t.$elm.find(".slider-nav-scroll")
            if (t.navScroll.length) {
                t.navScroll.wrap('<div class="nav-scroll-wrap"></div>'),
                t.navScroll.clone().addClass("clone-before").insertBefore(t.navScroll),
                t.navScroll.clone().addClass("clone-after").insertAfter(t.navScroll),
                t.navItems = t.$elm.find(".slider-nav-scroll .nav-item"),
                t.navItems.each(function(i, e) {
                    if ($(e).data("number") == 1) $(e).addClass("active")
                })
            }

            this.build();
            this.loaded();      
            this.event();
            return this;
        },
        loaded: function() {
        	var t = this,
        	entry = t.currentEntry;
            if ( document.readyState == "complete" ) {
                t.$elm.waitForImages(function() {
                    t.$elm.addClass("loaded")
                    t.$elm.find(".slider-title").lettering();
                    Promise.all([
                        loadTitle(),
                        loadDesc(),
                        loadImage()
                    ]);
                })
            } else {
            	$(window).on("load", function() {
                	t.$elm.waitForImages(function() {
                		t.$elm.addClass("loaded")
                		t.$elm.find(".slider-title").lettering();
                		Promise.all([
                			loadTitle(),
                			loadDesc(),
                			loadImage()
                		]);
                	})
                })
            }

            const loadTitle = function() {
	        	var el = $(entry).find(".slider-title > span").get(),
	        	settings = t.settings.title;
	        	return anime({
	        		targets: el,
	        		duration: settings.duration,
	        		delay: (e, i) => i * 30 + settings.delay,
	        		easing: settings.easing,
	        		translateY: [t.direction === "next" ? "100%" : "-100%", 0],
	        		opacity: [0, 1]
	        	}).finished;
            }

            const loadImage = function() {
	        	var el = $(entry).find(".slider-img .image").get(),
	        	settings = t.settings.image;
	        	(t.direction !== "next")
	        		? $(el).css("transform-origin", "50% 0%")
	        		: $(el).css("transform-origin", "50% 100%")
	        	return anime({
	        		targets: el,
	        		duration: settings.duration,
	        		delay: (e, i) => i * 30 + settings.delay,
	        		scale: [1.8,1],
	        		opacity: [1,1],
	        		easing: settings.easing,
	        		translateY: [t.direction === "next" ? "100%" : "-100%", 0],
	        	}).finished;
	        }

	        const loadDesc = function() {
	        	var el = $(entry).find(".slider-desc").get(),
	        	settings = t.settings.description;
	        	return anime({
	        		targets: el,
	        		duration: settings.duration,
	        		easing: settings.easing,
	        		translateY: [t.direction === "next" ? "20%" : "-20%", 0],
	        		opacity: [0, 1],
	        		delay: settings.delay
	        	}).finished;
	        }
        },
        build: function() {
        	var 
        	t = this,
        	a = t.args.bullets,
        	b = t.args.arrows,
        	c = t.args.pageNumbers,
            d = t.args.images,
        	z = t.$elm.find(".slider-control-wrap");

        	// Add bullets
        	if (a) {
        		z.append('<div class="slider-bullets"></div>')
        		for (var i = 0; i < t.entriesTotal; i++) {
        			z.children(".slider-bullets").append('<span class="dot"></span>')
        		}
        		t.bullets = t.$elm.find(".slider-bullets .dot")
        		t.bullets.first().addClass("active background-accent border-accent")
        	}

        	// Add Page Number
        	if (c) {
        		var current = "01",
        		total = t.entriesTotal;
        		(total < 9) && (total = "0" + total.toString())
        		z.append('<div class="page-numbers"></div>')
        		z.children(".page-numbers").append('<span class="page-current">' + current + 
        			'</span><span class="page-total">' + total + '</span>')
        		t.pageCurrent = t.$elm.find(".page-numbers .page-current")
        	}

        	!b && t.arrows.css("display","none") 

            // Image Effect is 3D (WEBGL)
            if (d) {
                var
                a1 = d.effect,
                b1 = d.displacementImage,
                images = t.$elm.find(".slider .slider-img img"),
                container = t.$elm.find(".slider").first().find(".slider-img");

                // Hide normal image
                t.$elm.find(".slider-img .image").css({"display": "none"})

                // Create canvas
                $('<div class="master-image-effect2"><div class="canvas"></div><div class="inner"></div></div>').appendTo(container)
                var wrapper = t.$elm.find(".master-image-effect2");
                var imgContainer = t.$elm.find(".master-image-effect2 .inner");
                $('<img alt="Image" data-sampler="displacement" src="' + b1 + '" />').appendTo(imgContainer)

                images.each(function(i, e) {
                    $(e).appendTo(imgContainer)
                })

                const canvas = wrapper.find('.canvas');
                const planeElement = wrapper.find('.inner');

                // Arrows
                t.$elm.find(".slider-nav .button-next").attr("data-goto","next")
                t.$elm.find(".slider-nav .button-prev").attr("data-goto","prev")
                var navs = t.$elm.find('[data-goto]');

                // Bullet
                var bullets = t.$elm.find(".slider-bullets .dot");
                bullets.each(function(i, e) {
                    $(e).attr("data-number", i + 1)
                })

                t.webgl = new imageEffectMaster({
                    canvas,
                    planeElement,
                    navs,
                    bullets
                })
            }

        },
        event: function() {
        	var t = this;
            // Arrow Navigation
            t.prevCtrl.on("click", function() { t.navigate("prev") })
            t.nextCtrl.on("click", function() { t.navigate("next") })

            // Bullets
            t.bullets.each(function() {
            	$(this).on("click", function() { t.dotsNavigate($(this).index()) })
            })

            // Nav Scroll
            if (t.navItems) {
                var 
                wr = t.navScroll.closest(".nav-scroll-wrap"),
                aw = wr.get(0).scrollWidth * 3 - wr.width();

                // Center nav scroll
                wr.scrollLeft(wr.scrollLeft() / 3)

                // on scroll
                t.navScroll.parent().on("scroll", function(event) {
                    var sl = wr.scrollLeft();
                    if (sl >= aw) {
                        var scrollPos = wr.get(0).scrollWidth/3 + (t.navScroll.get(0).scrollWidth - wr.width());
                        wr.scrollLeft(scrollPos)
                    }
                    if (sl <= 0) {
                        wr.scrollLeft(wr.get(0).scrollWidth/3)
                    }
                })
                
                if (t.navScroll.is(".auto-scroll")) {
                    // Auto Scroll
                    var timeout;
                    var scroll = 0;
                    var requestId;
                    var request20;
                    var requestArr = [];

                    t.$elm.on("mouseover", function() {
                        requestId = requestAnimationFrame(function animate(time) {
                            scroll += 2;
                            wr.scrollLeft(scroll);
                            request20 = requestAnimationFrame(animate);
                            requestArr.push(request20);
                        });
                    })

                    t.$elm.on("mouseout", function() {
                        for (var req of requestArr) {
                          cancelAnimationFrame(req);
                        }
                        cancelAnimationFrame(requestId);
                        requestArr.length = 0;
                    })
                }
                t.navItems.on("mouseover", function() {
                    var index = $(this).data("number");
                    t.scrollNavigate(index - 1);       
                })
            }

            // Autoplay
            console.log("autoplay", t.args.autoplay)
            if (t.args.autoplay) {
                var autoplay = function() {
                    t.nextCtrl.click()
                }
                setInterval(autoplay, t.args.duration)
            }
        },
        navigate: function(direction) {
        	var t = this;
        	// No update if animate still nt finish
        	if ( t.isAnimating ) return;
        	t.isAnimating = true;

			// Store direction
			t.direction = direction;
			// Update currentPos
			const newPos = t.currentPos = t.direction === 'next' ? 
				t.currentPos < t.entriesTotal - 1 ? t.currentPos + 1 : 0 : 
				t.currentPos = t.currentPos > 0 ? t.currentPos - 1 : t.entriesTotal - 1;

			t.newEntry = t.entries[newPos]
			t.currentPos = newPos
			t.update()
        },
        dotsNavigate: function(newPos) {
        	var t = this;

        	// Check if click on active dot
        	if ( newPos == t.currentPos ) return;

        	// No update if animate still not finish
        	if ( t.isAnimating ) return;
        	t.isAnimating = true;

        	// Update direction
        	(newPos > t.currentPos) 
        		? t.direction = "next"
        		: t.direction = "prev"

        	t.newEntry = t.entries[newPos]
			t.currentPos = newPos
			t.update()
        },
        scrollNavigate: function(newPos) {
            var t = this;

            // Check if click on active dot
            if ( newPos == t.currentPos ) return;

            // No update if animate still not finish
            if ( t.isAnimating ) return;
            t.isAnimating = true;

            // Update direction
            (newPos > t.currentPos) 
                ? t.direction = "next"
                : t.direction = "prev"

            t.newEntry = t.entries[newPos]
            t.currentPos = newPos
            t.update()
        },
        update: function() {
        	var t = this;
            t.$elm.addClass("animating")
        	// hide the current entry and show the next/previous one.
			// when both updatePageNumber, hide and show are finished:
			Promise.all([
				t.hideCurrent(),
				t.showNew(),
				t.updateDots(),
				t.updatePageNumbers(),
                t.updateNavScroll(),
				]).then(() => {
					$(t.currentEntry).removeClass("active");
					$(t.newEntry).addClass("active");
					t.currentEntry = t.newEntry;
					this.isAnimating = false;
                    t.$elm.removeClass("animating")
				})
        },
        updateDots: function() {
        	var t = this;
        	t.bullets.removeClass("active background-accent border-accent")
        		.eq(t.currentPos).addClass("active background-accent border-accent")
        },
        updatePageNumbers: function() {
        	var t = this,
        	settings = t.settings.pagination,
        	el = t.$elm.find(".page-numbers .page-current").get();
        	
        	return anime({
				targets: el,
				duration: settings.duration,
				easing: 'easeInOutQuad',
				translateY: [
					{value: t.direction === 'next' ? '-100%' : '100%', delay: settings.delay},
					{value: [t.direction === 'next' ? '100%' : '-100%','0%'], delay: settings.duration}
				],
				opacity: [
					{value: 0, delay: settings.delay},
					{value: [0,1], delay: settings.duration}
				],
				update: (anime) => {
					if ( anime.progress >= 50 ) {
						t.pageCurrent.html(`0${t.currentPos + 1}`);
					}
				}
			}).finished;
        },
        updateNavScroll: function() {
            var t = this;
            if (t.navItems) {
                t.navItems.removeClass("active")
                t.navItems.each(function(i, e) {
                    if ($(e).data("number") == t.currentPos + 1) $(e).addClass("active")
                })
            }
        },
        hideCurrent: function() {
        	var t = this;
            console.log("hideCurrent")
        	return Promise.all([
        		t.hideTitle(),
        		t.hideDesc(),
        		t.hideImage()
        		])
        },
        showNew: function() {
        	var t = this;
            console.log("showNew")

        	return Promise.all([
        		t.showTitle(),
        		t.showDesc(),
        		t.showImage()
        		])
        },
        // Title
        hideTitle: function() {
            console.log("hideTitle")

        	var t = this,
        	entry = t.currentEntry,
        	el = $(entry).find(".slider-title > span").get(),
        	settings = t.settings.title;
        	return anime({
        		targets: el,
        		duration: settings.duration,
        		delay: (e, i) => i * 30 + settings.delay,
        		easing: settings.easing,
        		translateY: [0, t.direction === "next" ? "-100%" : "100%"],
        		opacity: [1, 0]
        	}).finished;
        },
        showTitle: function() {
            console.log("showTitle")

        	var t = this,
        	entry = t.newEntry,
        	el = $(entry).find(".slider-title > span").get(),
        	settings = t.settings.title;
        	return anime({
        		targets: el,
        		duration: settings.duration,
        		delay: (e, i) => i * 30 + settings.delay,
        		easing: settings.easing,
        		translateY: [t.direction === "next" ? "100%" : "-100%", 0],
        		opacity: [0, 1]
        	}).finished;
        },
        // Image
        hideImage: function() {
        	var t = this,
        	entry = t.currentEntry,
        	el = $(entry).find(".slider-img .image").get(),
        	settings = t.settings.image;
        	$(el).css("transform-origin", "50% 50%")
        	return anime({
        		targets: el,
        		duration: settings.duration,
        		//delay: (e, i) => i * 30 + settings.delay,
        		scale: [1,1],
        		easing: settings.easing,
        		translateY: [0, t.direction === "next" ? "-100%" : "100%"],
        		opacity: [1, 1]
        	}).finished;
        },
        showImage: function() {
        	var t = this,
        	entry = t.newEntry,
        	el = $(entry).find(".slider-img .image").get(),
        	settings = t.settings.image;
        	(t.direction !== "next")
        		? $(el).css("transform-origin", "50% 0%")
        		: $(el).css("transform-origin", "50% 100%")
        	return anime({
        		targets: el,
        		duration: settings.duration,
        		//delay: (e, i) => i * 30 + settings.delay,
        		scale: [1.8,1],
        		opacity: [1,1],
        		easing: settings.easing,
        		translateY: [t.direction === "next" ? "100%" : "-100%", 0],
        	}).finished;
        },
        // Description
        hideDesc: function() {
        	var t = this,
        	entry = t.currentEntry,
        	el = $(entry).find(".slider-desc").get(),
        	settings = t.settings.description;
        	return anime({
        		targets: el,
        		duration: settings.duration,
        		easing: settings.easing,
        		translateY: [0, t.direction === "next" ? "-10%" : "10%"],
        		opacity: [1, 0],
        		delay: settings.delay
        	}).finished;
        },
        showDesc: function() {
        	var t = this,
        	entry = t.newEntry,
        	el = $(entry).find(".slider-desc").get(),
        	settings = t.settings.description;
        	return anime({
        		targets: el,
        		duration: settings.duration,
        		easing: settings.easing,
        		translateY: [t.direction === "next" ? "20%" : "-20%", 0],
        		opacity: [0, 1],
        		delay: settings.delay
        	}).finished;
        },
        // From https://davidwalsh.name/javascript-debounce-function.
		// debounce: function (func, wait, immediate) {
		// 	var timeout;
		// 	return function() {
		// 		var context = this, args = arguments;
		// 		var later = function() {
		// 			timeout = null;
		// 			if (!immediate) func.apply(context, args);
		// 		};
		// 		var callNow = immediate && !timeout;
		// 		clearTimeout(timeout);
		// 		timeout = setTimeout(later, wait);
		// 		if (callNow) func.apply(context, args);
		// 	}
		// }
    }

    masterSlider.defaults = masterSlider.prototype.defaults;

    $.fn.masterSlider = function(opts) {
        return this.each(function() {
            new masterSlider(this, opts).init();
        });
    };
}(jQuery, window, document));