<template>
  <div class="bx-pull-refresh">
    <div
      class="bx-pull-refresh-main"
      :style="style"
      @touchstart="onTouchStart"
      @touchmove="onTouchMove"
      @touchend="onTouchEnd"
      @touchcancel="onTouchEnd"
      >
      <div class="bx-pull-refresh-header">
        <template v-if="status === 'pulling'">
          <span class="bx-pull-refresh-text">{{ pullingText }}</span>
        </template>
        <template v-if="status === 'loosing'">
          <span class="bx-pull-refresh-text">{{ loosingText }}</span>
        </template>
        <template v-if="status === 'loading'">
          <bxs-loading />
          <span class="bx-pull-refresh-text">{{ loadingText }}</span>
        </template>
      </div>
      <slot />
    </div>
  </div>
</template>
<script>
import { create } from '../utils'
import Touch from '../mixins/touch'
import scrollUtils from '../utils/scroll'
import Loading from '../loading'

export default create({
  name: 'pull-refresh',
  components: {
    [Loading.name]: Loading,
  },
  mixins: [Touch],
  props: {
    value: {
      type: Boolean,
      required: true,
    },
    pullingText: {
      type: String,
      default: '下拉即可刷新…',
    },
    loosingText: {
      type: String,
      default: '释放即可刷新…',
    },
    loadingText: {
      type: String,
      default: '正在刷新…',
    },
    animationDuration: {
      type: Number,
      default: 300,
    },
    headHeight: {
      type: Number,
      default: 49,
    },
  },
  data() {
    return {
      status: 'normal',
      height: 0,
      duration: 0,
    }
  },
  computed: {
    style() {
      return {
        transition: `${this.duration}ms`,
        transform: `translate3d(0,${this.height}px, 0)`,
      }
    },
    untouchable() {
      return this.status === 'loading'
    },
  },
  watch: {
    value(val) {
      this.getStatus(val ? this.headHeight : 0, val)
    },
  },
  mounted() {
    this.scrollEl = scrollUtils.getScrollEventTarget(this.$el)
  },
  methods: {
    onTouchStart(event) {
      if (this.untouchable) return

      if (this.getCeiling()) {
        this.duration = 0
        this.touchStart(event)
      }
    },
    onTouchMove(event) {
      if (this.untouchable) return

      this.touchMove(event)

      if (!this.ceiling && this.getCeiling()) {
        this.duration = 0
        this.startY = event.touches[0].clientY
        this.deltaY = 0
      }
      if (this.ceiling && this.deltaY >= 0) {
        if (this.direction === 'vertical') {
          this.getStatus(this.ease(this.deltaY))
          if (event.cancelable) event.preventDefault()
        }
      }
    },
    onTouchEnd() {
      if (this.untouchable) return

      if (this.ceiling && this.deltaY) {
        this.duration = this.animationDuration
        if (this.status === 'loosing') {
          this.getStatus(this.headHeight, true)
          this.$emit('input', true)
          this.$emit('refresh')
        } else {
          this.getStatus(0)
        }
      }
    },
    getCeiling() {
      this.ceiling = scrollUtils.getScrollTop(this.scrollEl) === 0
      return this.ceiling
    },
    ease(height) {
      const { headHeight } = this
      let realHeight
      if (height < headHeight) {
        realHeight = height
      } else if (height < headHeight * 2) {
        realHeight = Math.round(headHeight + (height - headHeight) / 2)
      } else {
        realHeight = Math.round(
          headHeight * 1.5 + (height - headHeight * 2) / 4,
        )
      }
      return realHeight
    },
    getStatus(height, isLoading) {
      this.height = height
      let status
      if (isLoading) {
        status = 'loading'
      } else if (height === 0) {
        status = 'normal'
      } else if (height < this.headHeight) {
        status = 'pulling'
      } else {
        status = 'loosing'
      }
      if (status !== this.status) {
        this.status = status
      }
    },
  },
})
</script>
<style>
@import './index.css';
</style>
