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

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

    masterSlider2.prototype = {
        defaults: {
        	bullets: true,
        	arrows: true,
        	pageNumbers: true,
            displacement: "assets/img/displacement/1.jpg"
        },
        init: function() {
            this.args = $.extend({}, this.defaults, this.opts, this.config);
            var t = this;

            // 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: 900, delay: 400, easing: 'easeOutExpo'},
				pagination: {duration: 300, delay: 400, 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,

            t.$elm.find(".slider").first().addClass("active"),

            t.initImages();
            t.build();
            t.loaded();      
            t.event();
            return this;
        },
        initImages: function() {
            var vertex = `
                varying vec2 vUv;
                void main() {
                  vUv = uv;
                  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
                }
            `;

            var fragment = `
                varying vec2 vUv;

                uniform sampler2D texture;
                uniform sampler2D texture2;
                uniform sampler2D disp;

                // uniform float time;
                // uniform float _rot;
                uniform float dispFactor;
                uniform float effectFactor;

                // vec2 rotate(vec2 v, float a) {
                //  float s = sin(a);
                //  float c = cos(a);
                //  mat2 m = mat2(c, -s, s, c);
                //  return m * v;
                // }

                void main() {

                    vec2 uv = vUv;

                    // uv -= 0.5;
                    // vec2 rotUV = rotate(uv, _rot);
                    // uv += 0.5;

                    vec4 disp = texture2D(disp, uv);

                    vec2 distortedPosition = vec2(uv.x + dispFactor * (disp.r*effectFactor), uv.y);
                    vec2 distortedPosition2 = vec2(uv.x - (1.0 - dispFactor) * (disp.r*effectFactor), uv.y);

                    vec4 _texture = texture2D(texture, distortedPosition);
                    vec4 _texture2 = texture2D(texture2, distortedPosition2);

                    vec4 finalTexture = mix(_texture, _texture2, dispFactor);

                    gl_FragColor = finalTexture;
                    // gl_FragColor = disp;
                }
            `;

            var parent;
        },
        loaded: function() {
        	var t = this,
        	entry = t.currentEntry;
        	$(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,
        	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") 
        },
        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()) })
            })
        },
        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()
        },
        update: function() {
        	var t = this;

        	// 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()
				]).then(() => {
					$(t.currentEntry).removeClass("active");
					$(t.newEntry).addClass("active");
					t.currentEntry = t.newEntry;
					this.isAnimating = false;
				})
        },
        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;
        },
        hideCurrent: function() {
        	var t = this;
        	return Promise.all([
        		//t.hideTitle(),
        		t.hideDesc(),
        		t.hideImage()
        		])
        	// t.isHidden = true
        	// return t.toggle()
        },
        showNew: function() {
        	var t = this;
        	return Promise.all([
        		//t.showTitle(),
        		t.showDesc(),
        		t.showImage()
        		])
        },
        // Title
        hideTitle: function() {
        	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() {
        	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,
        		translateX: [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", "0% 50%")
        		: $(el).css("transform-origin", "100% 50%")
        	return anime({
        		targets: el,
        		duration: settings.duration,
        		delay: (e, i) => i * 30 + settings.delay,
        		scale: [1.8,1],
        		opacity: [1,1],
        		easing: settings.easing,
        		translateX: [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);
		// 	}
		// }
    }

    masterSlider2.defaults = masterSlider2.prototype.defaults;

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