/**
* part of the code from vant https://github.com/youzan/vant/
* @Author momoko
*/

<template>
  <div ref="swipeContent" :class="[c(),cusClass]">
    <div
      :class="c('track')"
      :style="trackStyle"
      @touchstart="touchStart"
      @touchmove="touchMove"
      @touchend="touchEnd"
      @touchcancel="touchEnd"
      @transitionend="transitionend"
    >
      <slot />
    </div>
    <div v-if="showIndicators && count > 1" :class="[c('indicators'), { [c('indicators--vertical')]: vertical }]" >
      <slot name="indicators">
        <i
          v-for="item in count"
          :key="item"
          :class="[c('indicators-item'), {[c('indicators-item--active')]: item - 1 === activeIndex }]"
        />
      </slot>
    </div>
  </div>
</template>

<script>
import SwipeItem from './SwipeItem'
import { base, touch } from '../../common/mixins'

// 上抛的事件集合
const emit = {
  start: 'start',
  move: 'move',
  change: 'change',
}

export default {
  SwipeItem,
  name: 'Swipe',
  mixins: [base, touch],

  props: {
    index: {
      // .sync 当前的位置索引，从0开始
      type: Number,
      default: 0,
    },
    auto: {
      // 是否自动播放
      type: Boolean,
      default: true,
    },
    loop: {
      // 是否循环播放
      type: Boolean,
      default: true,
    },
    interval: {
      // 自动轮播间隔时间
      type: Number,
      default: 3000,
    },
    threshold: {
      // 滑动超过这个距离(px)时才切换
      type: Number,
      default: 100,
    },
    duration: {
      // 动画时长
      type: Number,
      default: 500,
    },
    touchable: {
      // 是否可以手势滑动
      type: Boolean,
      default: true,
    },
    vertical: {
      // 是否纵向滚动
      type: Boolean,
      default: false,
    },
    showIndicators: {
      // 是否显示提示点
      type: Boolean,
      default: true,
    },
    defaultWidth: {
      type: Number,
      default: 0,
    },
    defaultHeight: {
      type: Number,
      default: 0,
    },
    cusClass: {
      type: String,
      default: '',
    },
  },
  data () {
    return {
      width: 0, // swipe 盒子宽
      height: 0, // swipe 盒子高
      offset: 0, // track 偏移量
      deltaX: 0,
      deltaY: 0,
      active: 0, // 活动的 index
      swipes: [], // swipe-items
      swiping: true, // 关闭 swipe 动画
    }
  },
  computed: {
    activeIndex () {
      return (this.active + this.count) % this.count
    },
    count () {
      return this.swipes.length
    },
    delta () {
      // 差异量
      return this.vertical ? this.deltaY : this.deltaX
    },
    size () {
      // swipe-item 长度
      return this[this.vertical ? 'height' : 'width']
    },
    trackSize () {
      return this.count * this.size
    },
    trackStyle () {
      return {
        [this.vertical ? 'height' : 'width']: `${this.trackSize}px`,
        transitionDuration: `${this.swiping ? 0 : this.duration}ms`,
        transform: `translate${this.vertical ? 'Y' : 'X'}(${this.offset}px)`,
      }
    },
  },
  watch: {
    swipes () {
      this.init()
    },
    index (value) {
      value === this.activeIndex || this.init()
    },
    auto (auto) {
      if (!auto) {
        this.clear()
      }
    },
  },

  created () {
    this.timer = null // 定时器
  },
  mounted () {
    this.init()
  },
  destroyed () {
    this.clear()
  },

  methods: {
    init () {
      let vm = this
      vm.clear()
      if (vm.defaultWidth) {
        vm.width = vm.defaultWidth
      } else if (vm.defaultHeight) {
        vm.hight = vm.defaultHeight
      } else {
        if (vm.$el) {
          vm.width = vm.$el.offsetWidth || document.documentElement.offsetWidth
          vm.height = vm.$el.offsetHeight || document.documentElement.offsetHeight
        } else {
          // 获取窗口宽度
          if (window.innerWidth) { vm.width = window.innerWidth } else if ((document.body) && (document.body.clientWidth)) {
            vm.width = document.body.clientWidth
          }
          if (window.innerHeight) { vm.height = window.innerHeight } else if ((document.body) && (document.body.clientHeight)) { vm.height = document.body.clientHeight }
          // 通过深入Document内部对body进行检测，获取窗口大小
          if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth) {
            vm.height = document.documentElement.clientHeight
            vm.width = document.documentElement.clientWidth
          }
        }
        if (!vm.width) {
          vm.width = 375
          vm.height = 667
        }
      }
      this.active = this.index
      this.offset = this.count > 1 ? -this.size * this.active : 0
      this.swipes.forEach(swipe => {
        swipe.offset = 0
      })
      this.autoPlay()
    },

    touchStart (e) {
      if (!this.$props.touchable) return

      this.$emit(emit.start, this.activeIndex)

      this.clear()
      this.swiping = true
      this.onTouchStart(e)
      this.correctPosition()
    },

    touchMove (e) {
      if (!this.touchable) return

      this.onTouchMove(e)

      if ((this.vertical && this.direction === 'vertical') || this.direction === 'horizontal') {
        event.preventDefault()
        event.stopPropagation()
      }

      this.$emit(emit.move, this.delta)

      this.move(0, Math.min(Math.max(this.delta, -this.size), this.size))
    },

    touchEnd (e) {
      if (!this.touchable) return

      if (this.delta) {
        const offset = this.vertical ? this.offsetY : this.offsetX
        this.move(offset > this.threshold ? (this.delta > 0 ? -1 : 1) : 0)
        this.swiping = false
      }

      this.autoPlay()
    },

    transitionend () {
      this.$emit(emit.change, this.activeIndex)
      this.$emit('update:index', this.activeIndex)
    },

    scopeToTop () {
      this.$refs.swipeContent.scrollTop = 0
    },

    move (move = 0, offset = 0) {
      const { delta, active, count, swipes, trackSize } = this
      const atFirst = active === 0
      const atLast = active === count - 1
      const outOfBounds = !this.loop && ((atFirst && (offset > 0 || move < 0)) || (atLast && (offset < 0 || move > 0)))

      if (outOfBounds || count <= 1) {
        return
      }

      if (move) {
        if (active === -1) {
          swipes[count - 1].offset = 0
        }
        swipes[0].offset = atLast && move > 0 ? trackSize : 0
        this.active += move
        this.scopeToTop()
      } else {
        if (atFirst) {
          swipes[count - 1].offset = delta > 0 ? -trackSize : 0
        } else if (atLast) {
          swipes[0].offset = delta < 0 ? trackSize : 0
        }
      }
      this.offset = offset - this.active * this.size
    },

    // 超出边界时移动到正确的位置
    correctPosition () {
      if (this.active <= -1) {
        this.move(this.count)
      }
      if (this.active >= this.count) {
        this.move(-this.count)
      }
    },

    clear () {
      clearTimeout(this.timer)
    },

    autoPlay () {
      if (this.auto && this.count > 1) {
        this.clear()
        this.timer = setTimeout(() => {
          this.swiping = true
          this.correctPosition()

          setTimeout(() => {
            this.swiping = false
            this.move(1)
            this.autoPlay()
          }, 30)
        }, this.interval)
      }
    },
  },
}
</script>

<style lang="stylus">

  .hls-swipe {
    overflow: auto;
    position: relative;
    user-select: none;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    overflow-scrolling: touch;
    width: 100%;

    &-item {
      float: left;
      height: 100%;
      img {
        width: 100%;
        max-width: 100%;
      }
    }

    &-track {
      // height: 100%;
    }

    &-indicators {
      display: flex;
      position: absolute;
      left: 50%;
      bottom: 10px;
      transform: translateX(-50%);

      &--vertical {
        left: 10px;
        top: 50%;
        bottom: auto;
        flex-direction: column;
        transform: translateY(-50%);

        .hips-swipe-indicators-item:not(:last-child) {
          margin-bottom: 12px;
        }
      }

      &-item {
        border-radius: 100%;
        background-color: rgba(0, 0, 0, 0.2);
        width: 6px;
        height: 6px;
        margin-top: 6px;

        &:not(:last-child) {
          margin-right: 6px;
          //margin-top: 12px;
        }
        /*&:not(:first-child){
          margin-top: 12px;
        }*/

        &--active {
          background-color: rgba(0, 0, 0, 0.5);
        }
      }
    }
  }
</style>
