import _toConsumableArray from 'babel-runtime/helpers/toConsumableArray';
import _defineProperty from 'babel-runtime/helpers/defineProperty';
import { preventDefault } from '../_utils/dom/event';
import { TouchMixin } from '../mixins/touch';
import { MouseMixin } from '../mixins/mouse';
import { doubleRaf } from '../_utils/dom/raf';
import { on, off } from '../_utils/dom/event';
import { throttle } from '../utils/commonUtil';
import { range } from '../utils/numberUtil';
import './index.less';

var Carousel = {
    name: 'SnCarousel',
    mixins: [TouchMixin, MouseMixin],

    props: {
        width: Number,
        height: Number,
        autoplay: Number,
        vertical: Boolean,
        indicatorColor: String,
        loop: {
            type: Boolean,
            'default': true
        },
        duration: {
            type: Number,
            'default': 500
        },
        touchable: {
            type: Boolean,
            'default': true
        },
        mouseable: {
            type: Boolean,
            'default': true
        },
        mousewheelable: {
            type: Boolean,
            'default': true
        },
        initialSwipe: {
            type: Number,
            'default': 0
        },
        showIndicators: {
            type: Boolean,
            'default': true
        },
        threshold: {
            type: Number,
            'default': 30
        }
    },

    data: function data() {
        return {
            computedWidth: 0,
            computedHeight: 0,
            offset: 0,
            active: 0,
            deltaX: 0,
            deltaY: 0,
            swipes: [],
            swiping: false,
            draging: false
        };
    },


    watch: {
        swipes: function swipes() {
            this.initialize();
        },
        initialSwipe: function initialSwipe() {
            this.initialize();
        },
        autoplay: function autoplay(_autoplay) {
            if (!_autoplay) {
                this.clear();
            } else {
                this.autoPlay();
            }
        },
        offset: function offset(val) {
            var deltaX = this.deltaX,
                deltaY = this.deltaY,
                size = this.size,
                draging = this.draging;

            this.$emit('offset', {
                deltaX: deltaX,
                deltaY: deltaY,
                offset: Math.abs(val) % (size + 1),
                size: size,
                draging: draging
            });
        }
    },

    computed: {
        count: function count() {
            return this.swipes.length;
        },
        delta: function delta() {
            return this.vertical ? this.deltaY : this.deltaX;
        },
        size: function size() {
            return this[this.vertical ? 'computedHeight' : 'computedWidth'];
        },
        trackSize: function trackSize() {
            return this.count * this.size;
        },
        activeIndicator: function activeIndicator() {
            return (this.active + this.count) % this.count;
        },
        isCorrectDirection: function isCorrectDirection() {
            var expect = this.vertical ? 'vertical' : 'horizontal';
            return this.direction === expect;
        },
        trackStyle: function trackStyle() {
            var _ref;

            var mainAxis = this.vertical ? 'height' : 'width';
            var crossAxis = this.vertical ? 'width' : 'height';

            return _ref = {}, _defineProperty(_ref, mainAxis, this.trackSize + 'px'), _defineProperty(_ref, crossAxis, this[crossAxis] ? this[crossAxis] + 'px' : ''), _defineProperty(_ref, 'transitionDuration', (this.swiping ? 0 : this.duration) + 'ms'), _defineProperty(_ref, 'transform', 'translate' + (this.vertical ? 'Y' : 'X') + '(' + this.offset + 'px)'), _ref;
        },
        indicatorStyle: function indicatorStyle() {
            return {
                backgroundColor: this.indicatorColor
            };
        },
        minOffset: function minOffset() {
            var rect = window.getComputedStyle(this.$el);
            return (this.vertical ? parseFloat(rect.height) : parseFloat(rect.width)) - this.size * this.count;
        }
    },

    mounted: function mounted() {
        on(window, 'resize', this.onResize, true);
    },
    activated: function activated() {
        on(window, 'resize', this.onResize, true);
    },
    deactivated: function deactivated() {
        off(window, 'resize', this.onResize);
    },
    beforeDestroy: function beforeDestroy() {
        off(window, 'resize', this.onResize);
    },


    methods: {
        initialize: function initialize() {
            var active = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.initialSwipe;

            clearTimeout(this.timer);

            if (this.$el) {
                var rect = this.$el.getBoundingClientRect();
                this.computedWidth = this.width || rect.width;
                this.computedHeight = this.height || rect.height;
            }

            this.swiping = true;
            this.active = active;
            this.offset = this.count > 1 ? -this.size * this.active : 0;
            this.swipes.forEach(function (swipe) {
                swipe.offset = 0;
            });
            this.autoPlay();
        },
        onResize: function onResize() {
            this.initialize(this.activeIndicator);
        },
        onTouchStart: function onTouchStart(event) {
            if (!this.touchable) return;

            this.clear();
            this.swiping = true;
            this.draging = true;
            this.touchStart(event);
            this.correctPosition();
        },
        onMouseStart: function onMouseStart(event) {
            if (!this.mouseable) return;

            this.clear();
            this.swiping = true;
            this.draging = true;
            this.mouseStart(event);
            this.correctPosition();
        },
        onTouchMove: function onTouchMove(event) {
            if (!this.touchable || !this.swiping) return;

            this.touchMove(event);

            if (this.isCorrectDirection) {
                preventDefault(event, true);
                this.move({ offset: this.delta });
            }
        },
        onMouseMove: function onMouseMove(event) {
            if (!this.mouseable || !this.swiping) return;

            this.mouseMove(event);

            if (this.isCorrectDirection) {
                preventDefault(event, true);
                this.move({ offset: this.delta });
            }
        },
        onTouchEnd: function onTouchEnd() {
            if (!this.touchable || !this.swiping) return;

            if (this.delta && this.isCorrectDirection) {
                var offset = this.vertical ? this.offsetY : this.offsetX;

                if (offset > this.threshold) {
                    this.move({
                        pace: offset > 0 ? this.delta > 0 ? -1 : 1 : 0,
                        emitChange: true
                    });
                } else {
                    this.move({
                        pace: 0,
                        emitChange: false
                    });
                }
            }

            this.swiping = false;
            this.draging = false;
            this.autoPlay();
        },
        onMouseEnd: function onMouseEnd() {
            if (!this.mouseable || !this.swiping) return;

            if (this.delta && this.isCorrectDirection) {
                var offset = this.vertical ? this.offsetY : this.offsetX;

                if (offset > this.threshold) {
                    this.move({
                        pace: offset > 0 ? this.delta > 0 ? -1 : 1 : 0,
                        emitChange: true
                    });
                } else {
                    this.move({
                        pace: 0,
                        emitChange: false
                    });
                }
            }

            this.swiping = false;
            this.draging = false;
            this.autoPlay();
        },
        onMouseWheel: function onMouseWheel(event) {
            if (!this.mousewheelable) return;

            preventDefault(event, true);
            var wheelDelta = event.wheelDelta;
            var nextIndicator = wheelDelta < 0 ? this.activeIndicator + 1 : this.activeIndicator - 1;

            this.swipeTo(nextIndicator);
        },
        onTransitionEnd: function onTransitionEnd(event) {
            this.$emit('transition-end', event);
        },
        getTargetActive: function getTargetActive(pace) {
            var active = this.active,
                count = this.count;


            if (pace) {
                if (this.loop) {
                    return range(active + pace, -1, count);
                }

                return range(active + pace, 0, count - 1);
            }

            return active;
        },
        getTargetOffset: function getTargetOffset(targetActive, offset) {
            var currentPosition = targetActive * this.size;
            if (!this.loop) {
                currentPosition = Math.min(currentPosition, -this.minOffset);
            }

            var targetOffset = Math.round(offset - currentPosition);
            if (!this.loop) {
                targetOffset = range(targetOffset, this.minOffset, 0);
            }

            return targetOffset;
        },
        move: function move(_ref2) {
            var _ref2$pace = _ref2.pace,
                pace = _ref2$pace === undefined ? 0 : _ref2$pace,
                _ref2$offset = _ref2.offset,
                offset = _ref2$offset === undefined ? 0 : _ref2$offset,
                emitChange = _ref2.emitChange;
            var loop = this.loop,
                count = this.count,
                active = this.active,
                swipes = this.swipes,
                trackSize = this.trackSize,
                minOffset = this.minOffset;


            if (count <= 1) {
                return;
            }

            var targetActive = this.getTargetActive(pace);
            var targetOffset = this.getTargetOffset(targetActive, offset);
            // auto move first and last swipe in loop mode
            if (loop) {
                if (swipes[0]) {
                    var outRightBound = targetOffset < minOffset;
                    swipes[0].offset = outRightBound ? trackSize : 0;
                }

                if (swipes[count - 1]) {
                    var outLeftBound = targetOffset > 0;
                    swipes[count - 1].offset = outLeftBound ? -trackSize : 0;
                }
            }

            this.active = targetActive;
            this.offset = targetOffset;

            if (emitChange && targetActive !== active) {
                this.$emit('change', this.activeIndicator);
            }
        },
        swipeTo: function swipeTo(index) {
            var _this = this;

            var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

            this.swiping = true;
            this.resetTouchStatus();
            this.correctPosition();

            doubleRaf(function () {
                var targetIndex = void 0;
                if (_this.loop && index === _this.count) {
                    targetIndex = _this.active === 0 ? 0 : index;
                } else {
                    targetIndex = index % _this.count;
                }

                _this.move({
                    pace: targetIndex - _this.active,
                    emitChange: true
                });

                if (options.immediate) {
                    doubleRaf(function () {
                        _this.swiping = false;
                    });
                } else {
                    _this.swiping = false;
                }
            });
        },
        correctPosition: function correctPosition() {
            if (this.active <= -1) {
                this.move({ pace: this.count });
            }

            if (this.active >= this.count) {
                this.move({ pace: -this.count });
            }
        },
        clear: function clear() {
            clearTimeout(this.timer);
        },
        autoPlay: function autoPlay() {
            var _this2 = this;

            var autoplay = this.autoplay;


            if (autoplay && this.count > 1) {
                this.clear();
                this.timer = setTimeout(function () {
                    _this2.swiping = true;
                    _this2.resetTouchStatus();
                    _this2.correctPosition();

                    doubleRaf(function () {
                        _this2.swiping = false;
                        _this2.move({
                            pace: 1,
                            emitChange: true
                        });
                        _this2.autoPlay();
                    });
                }, autoplay);
            }
        },
        genIndicator: function genIndicator() {
            var _this3 = this;

            var h = this.$createElement;
            var count = this.count,
                activeIndicator = this.activeIndicator;

            var slot = this.$slots.indicator;

            if (slot) {
                return slot;
            }

            if (this.showIndicators && count > 1) {
                return h(
                    'div',
                    {
                        'class': 'sn-carousel-indicators' + (this.vertical ? ' sn-carousel-indicators-vertical' : '')
                    },
                    [Array.apply(undefined, _toConsumableArray(Array(count))).map(function (empty, index) {
                        return h('i', {
                            'class': 'sn-carousel-indicator' + (index === activeIndicator ? ' sn-carousel-indicator-active' : ''),
                            style: index === activeIndicator ? _this3.indicatorStyle : ''
                        });
                    })]
                );
            }
        }
    },

    render: function render() {
        var h = arguments[0];

        return h(
            'div',
            { 'class': 'sn-carousel' },
            [h(
                'div',
                {
                    ref: 'track',
                    style: this.trackStyle,
                    'class': 'sn-carousel-track',
                    on: {
                        'touchstart': this.onTouchStart,
                        'mousedown': this.onMouseStart,
                        'touchmove': this.onTouchMove,
                        'mousemove': this.onMouseMove,
                        'touchend': this.onTouchEnd,
                        'mouseup': this.onMouseEnd,
                        'touchcancel': this.onTouchEnd,
                        'mouseout': this.onMouseEnd,
                        'mousewheel': throttle(this.onMouseWheel, 150),
                        'transitionend': this.onTransitionEnd
                    }
                },
                [this.$slots['default']]
            ), this.genIndicator()]
        );
    }
};

Carousel.install = function (Vue) {
    Vue.component(Carousel.name, Carousel);
};

export default Carousel;