/**
 * main.js
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 * 
 * Copyright 2015, Codrops
 * http://www.codrops.com
 */
; (function (window) {

    'use strict';

    var support = { animations: Modernizr.cssanimations },
        animEndEventNames = { 'WebkitAnimation': 'webkitAnimationEnd', 'OAnimation': 'oAnimationEnd', 'msAnimation': 'MSAnimationEnd', 'animation': 'animationend' },
        animEndEventName = animEndEventNames[Modernizr.prefixed('animation')],
        onEndAnimation = function (el, callback) {
            var onEndCallbackFn = function (ev) {
                if (support.animations) {
                    if (ev.target != this) return;
                    this.removeEventListener(animEndEventName, onEndCallbackFn);
                }
                if (callback && typeof callback === 'function') { callback.call(); }
            };
            if (support.animations) {
                el.addEventListener(animEndEventName, onEndCallbackFn);
            }
            else {
                onEndCallbackFn();
            }
        };

    function extend(a, b) {
        for (var key in b) {
            if (b.hasOwnProperty(key)) {
                a[key] = b[key];
            }
        }
        return a;
    }

    function Stack(el, options) {
        this.el = el;
        this.options = extend({}, this.options);
        extend(this.options, options);
        this.items = [].slice.call(this.el.children);
        this.itemsTotal = this.items.length;
        if (this.options.infinite && this.options.visible >= this.itemsTotal || !this.options.infinite && this.options.visible > this.itemsTotal || this.options.visible <= 0) {
            this.options.visible = 1;
        }
        this.current = 0;
        this._init();
    }

    Stack.prototype.options = {
        // cardstack's perspective value
        perspective: 1000,
        // cardstack's perspective origin
        perspectiveOrigin: '50% -50%',
        // number of visible items in the cardstack
        visible: 3,
        // infinite navigation
        infinite: true,
        // callback: when reaching the end of the cardstack
        onEndStack: function () { return false; },
        // animation settings for the items' movements in the cardstack when the items rearrange
        // object that is passed to the dynamicsjs animate function (see more at http://dynamicsjs.com/)
        // example:
        // {type: dynamics.spring,duration: 1641,frequency: 557,friction: 459,anticipationSize: 206,anticipationStrength: 392}
        cardstackItemsAnimation: {
            duration: 500,
            type: dynamics.bezier,
            points: [{ 'x': 0, 'y': 0, 'cp': [{ 'x': 0.25, 'y': 0.1 }] }, { 'x': 1, 'y': 1, 'cp': [{ 'x': 0.25, 'y': 1 }] }]
        },
        // delay for the items' rearrangement / delay before cardstackItemsAnimation is applied
        cardstackItemsAnimationDelay: 0,
        // animation settings for the items' movements in the cardstack before the rearrangement
        // we can set up different settings depending on whether we are approving or rejecting an item
		/*
		cardstackItemsPreAnimation : {
			reject : {
				// if true, then the settings.properties parameter will be distributed through the items in a non equal fashion
				// for instance, if we set settings.properties = {translateX:100} and we have options.visible = 4, 
				// then the second item in the cardstack will translate 100px, the second one 75px and the third 50px
				elastic : true,
				// object that is passed into the dynamicsjs animate function - second parameter -  (see more at http://dynamicsjs.com/)
				animationProperties : {},
				// object that is passed into the dynamicsjs animate function - third parameter - (see more at http://dynamicsjs.com/)
				animationSettings : {} 
			},
			accept : {
				// if true, then the settings.properties parameter will be distributed through the items in a non equal fashion
				// for instance, if we set settings.properties = {translateX:100} and we have options.visible = 4, 
				// then the second item on the cardstack will translate 100px, the second one 75px and the third 50px
				elastic : true,
				// object that is passed into the dynamicsjs animate function - second parameter -  (see more at http://dynamicsjs.com/)
				animationProperties : {},
				// object that is passed into the dynamicsjs animate function (see more at http://dynamicsjs.com/)
				animationSettings : {}
			}
		}
		*/
    };

    // set the initial styles for the visible items
    Stack.prototype._init = function () {
        // set default styles
        // first, the cardstack
        this.el.style.WebkitPerspective = this.el.style.perspective = this.options.perspective + 'px';
        this.el.style.WebkitPerspectiveOrigin = this.el.style.perspectiveOrigin = this.options.perspectiveOrigin;

        var self = this;

        // the items
        for (var i = 0; i < this.itemsTotal; ++i) {
            var item = this.items[i];
            if (i < this.options.visible) {
                item.style.opacity = 1;
                item.style.pointerEvents = 'auto';
                item.style.zIndex = i === 0 ? parseInt(this.options.visible + 1) : parseInt(this.options.visible - i);
                item.style.WebkitTransform = item.style.transform = 'translate3d(0px, 0px, ' + parseInt(-1 * 50 * i) + 'px)';
            }
            else {
                item.style.WebkitTransform = item.style.transform = 'translate3d(0,0,-' + parseInt(this.options.visible * 50) + 'px)';
            }
        }

        classie.add(this.items[this.current], 'cardstack__item--current');
    };

    Stack.prototype.reject = function (callback) {
        this._next('reject', callback);
    };

    Stack.prototype.accept = function (callback) {
        this._next('accept', callback);
    };

    Stack.prototype.restart = function () {
        this.hasEnded = false;
        this._init();
    };

    Stack.prototype._next = function (action, callback) {
        if (this.isAnimating || (!this.options.infinite && this.hasEnded)) return;
        this.isAnimating = true;

        // current item
        var currentItem = this.items[this.current];
        classie.remove(currentItem, 'cardstack__item--current');

        // add animation class
        classie.add(currentItem, action === 'accept' ? 'cardstack__item--accept' : 'cardstack__item--reject');

        var self = this;
        onEndAnimation(currentItem, function () {
            // reset current item
            currentItem.style.opacity = 0;
            currentItem.style.pointerEvents = 'none';
            currentItem.style.zIndex = -1;
            currentItem.style.WebkitTransform = currentItem.style.transform = 'translate3d(0px, 0px, -' + parseInt(self.options.visible * 50) + 'px)';

            classie.remove(currentItem, action === 'accept' ? 'cardstack__item--accept' : 'cardstack__item--reject');

            self.items[self.current].style.zIndex = self.options.visible + 1;
            self.isAnimating = false;

            if (callback) callback();

            if (!self.options.infinite && self.current === 0) {
                self.hasEnded = true;
                // callback
                self.options.onEndStack(self);
            }
        });

        // set style for the other items
        for (var i = 0; i < this.itemsTotal; ++i) {
            if (i >= this.options.visible) break;

            if (!this.options.infinite) {
                if (this.current + i >= this.itemsTotal - 1) break;
                var pos = this.current + i + 1;
            }
            else {
                var pos = this.current + i < this.itemsTotal - 1 ? this.current + i + 1 : i - (this.itemsTotal - this.current - 1);
            }

            var item = this.items[pos],
                // cardstack items animation
                animateStackItems = function (item, i) {
                    item.style.pointerEvents = 'auto';
                    item.style.opacity = 1;
                    item.style.zIndex = parseInt(self.options.visible - i);

                    dynamics.animate(item, {
                        translateZ: parseInt(-1 * 50 * i)
                    }, self.options.cardstackItemsAnimation);
                };

            setTimeout(function (item, i) {
                return function () {
                    var preAnimation;

                    if (self.options.cardstackItemsPreAnimation) {
                        preAnimation = action === 'accept' ? self.options.cardstackItemsPreAnimation.accept : self.options.cardstackItemsPreAnimation.reject;
                    }

                    if (preAnimation) {
                        // items "pre animation" properties
                        var animProps = {};

                        for (var key in preAnimation.animationProperties) {
                            var interval = preAnimation.elastic ? preAnimation.animationProperties[key] / self.options.visible : 0;
                            animProps[key] = preAnimation.animationProperties[key] - Number(i * interval);
                        }

                        // this one remains the same..
                        animProps.translateZ = parseInt(-1 * 50 * (i + 1));

                        preAnimation.animationSettings.complete = function () {
                            animateStackItems(item, i);
                        };

                        dynamics.animate(item, animProps, preAnimation.animationSettings);
                    }
                    else {
                        animateStackItems(item, i);
                    }
                };
            }(item, i), this.options.cardstackItemsAnimationDelay);
        }

        // update current
        this.current = this.current < this.itemsTotal - 1 ? this.current + 1 : 0;
        classie.add(this.items[this.current], 'cardstack__item--current');
    }

    window.Stack = Stack;

})(window);