<template>
  <div class="bx-swiper">
    <div
      class="bx-swiper-scroller"
      >
      <slot />
    </div>
    <div
      class="bx-swiper-paginations"
      :class="paginationClass"
      v-if="isPagination"
      >
      <div
        class="bx-swiper-pagination bx-swiper-pagination--translation"
        v-for="key in count"
        :key="key"
        :class="(key-1)==currentIndex && 'bx-swiper-pagination--active'"
        />
    </div>
  </div>
</template>
<script>
import Env from '../../src/utils/env'
import Transition from '../../src/utils/transition'
import Dom from '../../src/utils/dom'
import Props from './props'
import { create } from '../utils/index'

const touchEvents = {
  start: Env.supportTouch ? 'touchstart' : 'mousedown',
  move: Env.supportTouch ? 'touchmove' : 'mousemove',
  end: Env.supportTouch ? 'touchend' : 'mouseup',
}
let timer = null
export default create({
  name: 'swiper',
  props: Props,
  data() {
    return {
      width: 0,
      currentIndex: 0, // 当前活动项
      count: 0, // 子元素个数
      touches: {
        startX: 0, // touch前一个点
        startY: 0, // touch前一个点
        currentX: 0, // touch后一个点
        currentY: 0, // touch后一个点
        distanceX: 0, // 两个touch之间的距离
        distanceY: 0, // 两个touch之间的距离
      },
      offset: {
        x: 0, // 横向滑动距离
        y: 0, // 纵向滑动距离
        distance: 0, // 滑动的距离
      },
      direction: null, // 滑动的方向
      startTime: null, // 控制滑动的时间不能太长
      endTime: null, // 控制滑动的时间不能太长
      isPagination: this.pagination, // 是否展示分页指示器
      isLoop: this.loop, // 是否循环滚动
      isBounce: this.bounce, // 是否回弹
      isTouch: false, // 是否点击
      isSliding: false,
      isTouchStart: false,
      isTouchEnd: false,
    }
  },
  methods: {
    init() {
      this.element = this.$el
      this.scroller = this.$el.firstChild
      this.width = this.element.clientWidth
      // 防止swiper元素上设置了小数点宽度，而clientWidth取出来是整数
      this.scroller.style.width = `${this.width}px`
      this.count = this.$children.length
      // 当设置了分页指示器，并且数量大于指定数量，才展示指示器
      this.isPagination = this.pagination && this.count > this.paginationLimit
      // 当数量为1时，自动取消循环
      this.isLoop = this.loop && this.count > 1
      this.setIndex()
      if (this.isLoop) {
        // 循环滚动的数量
        this.loopCount = this.count + 2
        this.createLoop()
      }
      if (this.autoplay && this.count > 1) {
        this.startAutoPlay()
      }
    },
    // 是否可以回弹
    canBounce() {
      let result = false
      if (this.bounce && !this.isLoop) {
        if (this.currentIndex === 0 && this.direction === 'right') {
          result = true
        }
        if (this.currentIndex === this.count - 1 && this.direction === 'left') {
          result = true
        }
      }
      return result
    },
    // 动态为每个项设置index
    setIndex() {
      this.$children.forEach((item, index) => {
        /* eslint no-param-reassign: ["error", { "props": false }] */
        item.$data.index = index
      })
    },
    // 更新index位置
    updateIndex() {
      if (this.isLoop) {
        this.slideTo(-1 * (this.currentIndex + 1) * this.width, 0, 0)
      } else {
        this.slideTo(-1 * this.currentIndex * this.width, 0, 0)
      }
    },
    // 创建无限循环
    createLoop() {
      const firstNode = this.$children[0].$el.cloneNode(true)
      const lastNode = this.$children[this.count - 1].$el.cloneNode(true)
      Dom.removeClass(firstNode, 'bx-swiper-item--active')
      Dom.removeClass(lastNode, 'bx-swiper-item--active')
      this.scroller.insertBefore(lastNode, this.$children[0].$el)
      this.scroller.appendChild(firstNode)
      // 定位到第一个slideitem
      this.translate(-1 * this.width, 0)
    },
    // 处理循环滚动位置
    handleLoop() {
      if (this.offset.x === 0) {
        this.slideTo(-1 * this.count * this.width, 0, 0)
      }
      if (this.offset.x === -1 * (this.loopCount - 1) * this.width) {
        this.slideTo(-1 * this.width, 0, 0)
      }
    },
    /* eslint-disable consistent-return */
    // 开启自动播放
    startAutoPlay() {
      timer = window.setInterval(() => {
        if (this.isTouchStart) {
          if (this.isTouchEnd) {
            this.isTouchStart = false
            this.isTouchEnd = false
          }
          return false
        }
        this.next()
      }, this.speed)
    },
    addEvent() {
      const $element = this.element
      const $scroller = this.scroller
      // touchStart、touchMove、touchEnd事件
      $element.addEventListener(touchEvents.start, this.handleEvent, false)
      $element.addEventListener(touchEvents.move, this.handleEvent, false)
      $element.addEventListener(touchEvents.end, this.handleEvent, false)
      // 兼容PC
      // if (Env.supportTouch && !Env.isIos && !Env.isAndroid) {
      //   this.wrapper['addEventListener']('mousedown', this.handleEvent, false);
      //   this.wrapper['addEventListener']('mousemove', this.handleEvent, false);
      //   this.wrapper['addEventListener']('mouseup', this.handleEvent, false);
      // }
      // 动画结束事件
      $scroller.addEventListener('transitionend', this.handleEvent, false)
      $scroller.addEventListener('webkitTransitionEnd', this.handleEvent, false)
      // 窗口resize事件
      window.addEventListener('resize', this.handleEvent, false)
    },
    handleEvent(e) {
      // if (e.originalEvent) e = e.originalEvent;
      switch (e.type) {
        case 'touchstart':
          this.onTouchStart(e)
          break
        case 'touchmove':
          this.onTouchMove(e)
          break
        case 'touchend':
          this.onTouchEnd(e)
          break
        case 'mousedown':
          this.onTouchStart(e)
          break
        case 'mousemove':
          this.onTouchMove(e)
          break
        case 'mouseup':
          this.onTouchEnd(e)
          break
        case 'transitionend':
          this.transitionEnd(e)
          break
        case 'webkitTransitionEnd':
          this.transitionEnd(e)
          break
        case 'resize':
          this.onResize(e)
          break
        default:
          break
      }
    },
    onTouchStart(e) {
      // 大于一个touch点,不做处理
      if (e.touches && e.touches.length > 1) {
        e.preventDefault()
        return false
      }
      this.touches.startX =
        e.type === 'touchstart' ? e.touches[0].pageX : e.pageX
      this.touches.startY =
        e.type === 'touchstart' ? e.touches[0].pageY : e.pageY
      this.isTouch = true
      this.isSliding = false
      this.offset.distance = 0
      this.startTime = Date.now()
      this.isTouchStart = true
      Transition.setDuration(this.scroller, 0)
      this.$emit('touch-start', e)
    },
    onTouchMove(e) {
      // 大于一个touch点,不做处理
      if (
        (!this.isTouch && e.type === 'mousemove') ||
        (e.touches && e.touches.length > 1)
      ) {
        e.preventDefault()
        return false
      }
      // this.isLoop && this.handleLoop();
      this.touches.currentX =
        e.type === 'touchmove' ? e.touches[0].pageX : e.pageX
      this.touches.currentY =
        e.type === 'touchmove' ? e.touches[0].pageY : e.pageY
      const touchAngle =
        (Math.atan2(
          Math.abs(this.touches.currentY - this.touches.startY),
          Math.abs(this.touches.currentX - this.touches.startX),
        ) *
          180) /
        Math.PI
      // 滑动的角度小于{angle}才能正常滑动
      if (!this.isSliding) {
        this.isSliding = touchAngle <= this.angle
      }
      if (!this.isSliding) {
        return false
      }
      this.touches.distanceX = this.touches.currentX - this.touches.startX
      this.touches.distanceY = this.touches.currentY - this.touches.startY
      this.direction = this.touches.distanceX >= 0 ? 'right' : 'left'
      this.isBounce = this.canBounce()
      this.touches.distanceX = this.isBounce
        ? this.touches.distanceX / 2
        : this.touches.distanceX
      this.offset.x += this.touches.distanceX
      this.offset.distance += this.touches.distanceX
      this.translate(this.offset.x, 0)
      this.touches.startX = this.touches.currentX
      this.touches.startY = this.touches.currentY
      e.preventDefault()
      this.$emit('touch-move', e)
    },
    /* eslint-enable consistent-return */
    onTouchEnd(e) {
      this.endTime = Date.now()
      const duration = this.endTime - this.startTime
      if (
        !this.isBounce &&
        ((duration <= this.duration &&
          Math.abs(this.offset.distance) > this.distance) ||
          Math.abs(this.offset.distance) > this.width / 2)
      ) {
        if (this.offset.distance > 0) {
          this.prev()
        } else {
          this.next()
        }
      } else {
        this.slideTo(this.offset.x - this.offset.distance, 0)
      }
      this.isTouch = false
      this.isTouchEnd = true
      this.$emit('touch-end', e)
    },
    transitionEnd(e) {
      if (this.isLoop) {
        this.handleLoop()
      }
      this.$emit('transition-end', e)
    },
    onResize() {
      this.width = this.element.clientWidth
      this.updateIndex()
    },
    // 滚动到指定的x轴/y轴位置, time为动画时间
    slideTo(x, y, time) {
      /* eslint-disable no-param-reassign */
      if (time !== 0) {
        time = this.transitionSpeed
      }
      Transition.setProperty(this.scroller, Transition.prefixStyle('transform'))
      Transition.setDuration(this.scroller, time)
      this.translate(x, y)
    },
    // 滚动到指定的index
    slideToIndex(index, type) {
      if (this.loop) {
        if (type === 'prev') {
          if (index === this.count - 1) {
            this.slideTo(0, 0)
          } else {
            this.slideTo(-1 * (index + 1) * this.width, 0)
          }
        } else if (index === 0) {
          this.slideTo(-1 * (this.loopCount - 1) * this.width, 0)
        } else {
          this.slideTo(-1 * (index + 1) * this.width, 0)
        }
      } else {
        this.slideTo(-1 * index * this.width, 0)
      }
      this.$emit('slide', index)
    },
    // 滑动到上一个
    prev() {
      this.currentIndex -= 1
      if (this.currentIndex < 0) {
        this.currentIndex = this.count - 1
      }
      this.slideToIndex(this.currentIndex, 'prev')
      this.$emit('slide-prev', this.currentIndex)
    },
    // 滑动到下一个
    next() {
      this.currentIndex += 1
      if (this.currentIndex >= this.count) {
        this.currentIndex = 0
      }
      this.slideToIndex(this.currentIndex, 'next')
      this.$emit('slide-next', this.currentIndex)
    },
    // 页面滑动到指定位置
    /* eslint-disable no-param-reassign */
    translate(x, y) {
      if (!this.bounce) {
        if (x > 0) {
          x = 0
        }
        const total = this.loop ? this.loopCount : this.count
        const totalDistance = (total - 1) * this.width
        if (Math.abs(x) > totalDistance) {
          x = -1 * totalDistance
        }
      }
      this.offset.x = x
      Transition.setTransform(this.scroller, x, y)
    },
  },
  mounted() {
    this.init()
    this.addEvent()
  },
  destroyed() {
    if (timer) {
      window.clearInterval(timer)
    }
  },
})
</script>
<style lang="postcss">
@import './index.css';
</style>
