<template>
  <div v-mousewheel="handleMouseWheel" class="ans-scroller">
    <div
      ref="content"
      class="scroll-area-wrapper"
      :class="{'scroll-transition': !barDraggingY && !barDraggingX}"
    >
      <slot></slot>
    </div>

    <div
      v-if="scrollY"
      class="table-body-scroller ans-v-scrollbar"
      :style="vStyle"
      :class="{'active': barDraggingY}"
    >
      <div
        :class="{'scroll-transition': !barDraggingY, 'active': barDraggingY}"
        class="scrollbar-thumb"
        :style="vInnerStyle"
        @mousedown="startDragY"
        @touchstart="startDragY"
      ></div>
    </div>

    <div
      v-if="scrollX"
      class="table-body-scroller ans-h-scrollbar"
      :style="hStyle"
      :class="{'active': barDraggingX}"
    >
      <div
        class="scrollbar-thumb"
        :style="hInnerStyle"
        :class="{'scroll-transition': !barDraggingX, 'active': barDraggingX}"
        @mousedown="startDrag"
        @touchstart="startDrag"
      ></div>
    </div>
  </div>
</template>

<script>
import { mousewheel } from '../../../../../src/util'
import { debounce } from 'throttle-debounce'

export default {
  name: 'scroller',
  directives: { mousewheel },
  props: {
    // 内容的height
    contentHeight: {
      type: Number,
      default: 0
    },
    contentWidth: {
      type: Number,
      default: 0
    },
    offsetTop: {
      type: Number,
      default: 0
    },
    offsetBottom: {
      type: Number,
      default: 0
    },
    offsetLeft: {
      type: Number,
      default: 0
    },
    offsetRight: {
      type: Number,
      default: 0
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      // 是横向滚动
      scrollX: false,
      // 是否纵向滚动
      scrollY: false,
      // 容器的宽度
      wrapperWidth: 0,
      // 容器的高度
      wrapperHeight: 0,
      // 横向滚动bar的位置
      left: 0,
      // 纵向滚动bar的位置
      top: 0,
      // 横向bar拖动中
      barDraggingX: false,
      // 纵向bar拖动中
      barDraggingY: false
    }
  },
  computed: {
    hStyle() {
      return {
        width: this.viewWidth + 'px',
        left: this.offsetLeft + 'px'
      }
    },
    hInnerStyle() {
      return {
        width: this.width + 'px',
        transform: 'translateX(' + this.left + 'px)'
      }
    },
    vStyle() {
      return {
        height: this.viewHeight + 'px',
        top: this.offsetTop + 'px'
      }
    },
    vInnerStyle() {
      return {
        height: this.height + 'px',
        transform: 'translateY(' + this.top + 'px)'
      }
    },
    viewWidth() {
      return this.wrapperWidth - this.offsetLeft - this.offsetRight
    },
    viewHeight() {
      return this.wrapperHeight - this.offsetTop - this.offsetBottom
    },
    height() {
      return Math.max(40, Math.floor(this.viewHeight * this.wrapperHeight / this.contentHeight))
    },
    width() {
      return Math.max(40, Math.floor(this.viewWidth * this.wrapperWidth / this.contentWidth))
    },
    maxLeft() {
      return Math.max(this.viewWidth - this.width, 0)
    },
    maxTop() {
      return Math.max(this.viewHeight - this.height, 0)
    },
    maxScrollLeft() {
      return Math.max(this.contentWidth - this.wrapperWidth, 0)
    },
    maxScrollTop() {
      return Math.max(this.contentHeight - this.wrapperHeight, 0)
    }
  },
  created() {
    // 缓存滚动位置
    this.curPosition = { left: 0, top: 0 }
    this.debounceSetTop = debounce(3, (top) => this.setTop(top))
    this.debounceSetLeft = debounce(3, (left) => this.setLeft(left))
  },
  mounted() {
    document.addEventListener('mousemove', this.onDrag)
    // document.addEventListener('touchmove', this.onDrag)
    document.addEventListener('mouseup', this.endDrag)
    // document.addEventListener('touchend', this.endDrag)
  },
  beforeDestroy() {
    document.removeEventListener('mousemove', this.onDrag)
    // document.removeEventListener('touchmove', this.onDrag)
    document.removeEventListener('mouseup', this.endDrag)
    // document.removeEventListener('touchend', this.endDrag)
  },
  methods: {
    _getValidLeft(left) {
      return Math.min(this.maxLeft, Math.max(left, 0))
    },
    _getValidTop(top) {
      return Math.min(this.maxTop, Math.max(top, 0))
    },
    _getValidScrollLeft(left) {
      return Math.min(this.maxScrollLeft, Math.max(left, 0))
    },
    _getValidScrollTop(top) {
      return Math.min(this.maxScrollTop, Math.max(top, 0))
    },
    // check是否可滚动
    checkScrollable() {
      const wrapper = this.$el
      this.wrapperWidth = wrapper.clientWidth
      this.wrapperHeight = wrapper.clientHeight

      if (!this.wrapperWidth || !this.contentWidth ||
        !this.wrapperHeight || !this.contentHeight) {
        this.scrollX = false
        this.scrollY = false
        return
      }

      this.scrollX = this.contentWidth - this.wrapperWidth > 2
      this.scrollY = this.contentHeight - this.wrapperHeight > 2
    },

    startDrag(event) {
      if (this.disabled) return

      this.barDraggingX = true
      document.onselectstart = () => false
      document.ondragstart = () => false

      event = event.changedTouches ? event.changedTouches[0] : event

      this.startLeft = this.left
      this.startClientX = event.clientX
      this.$emit('on-start-drag', false)
    },

    onDrag(event) {
      if (this.disabled) return

      event = event.changedTouches ? event.changedTouches[0] : event
      if (this.barDraggingX) {
        const delta = event.clientX - this.startClientX
        this.left = this._getValidLeft(this.startLeft + delta)
        this.emitHorizontalChange((this.left / this.maxLeft) * this.maxScrollLeft)
      }

      if (this.barDraggingY) {
        const deltaY = event.clientY - this.startClientY
        this.top = this._getValidTop(this.startTop + deltaY)
        this.emitVerticalChange((this.top / this.maxTop) * this.maxScrollTop)
      }
    },
    startDragY(event) {
      if (this.disabled) return

      this.barDraggingY = true
      document.onselectstart = () => false
      document.ondragstart = () => false

      event = event.changedTouches ? event.changedTouches[0] : event

      this.startTop = this.top
      this.startClientY = event.clientY
      this.$emit('on-start-drag', true)
    },

    endDrag() {
      if (this.barDraggingX || this.barDraggingY) {
        document.onselectstart = null
        document.ondragstart = null
      }

      if (this.barDraggingX) {
        this.barDraggingX = false
        this.$emit('on-end-drag', false)
      }

      if (this.barDraggingY) {
        this.barDraggingY = false
        this.$emit('on-end-drag', true)
      }
    },

    handleMouseWheel(e, data) {
      const { pixelX, pixelY, spinX, spinY } = data

      const positionX = Math.abs(pixelX) - Math.abs(pixelY) >= 0

      if (Math.abs(spinX) > 0 && positionX && this.scrollX) {
        const left = this.curPosition.left + pixelX
        this.debounceSetLeft(left)
        e.preventDefault()
      }

      if (Math.abs(spinY) > 0 && !positionX && this.scrollY) {
        const top = this.curPosition.top + pixelY

        this.debounceSetTop(top)

        // y滚动条是否在滚动中
        clearTimeout(this.isScrollyRunTime)

        this.isScrollyRunTime = setTimeout(() => {
          this.isScrollyRun = false
        }, 30)

        if (pixelY > 0) { // 向下滚动
          if (this.top < this.maxTop || this.isScrollyRun) {
            this.isScrollyRun = true
            e.preventDefault()
          }
        } else {
          if (this.top > 0 && this.top <= this.maxTop || this.isScrollyRun) {
            console.log(this.top)
            this.isScrollyRun = true
            e.preventDefault()
          }
        }
      }
    },

    setTop(top) {
      this.top = !this.maxScrollTop ? 0 : this._getValidTop((top / this.maxScrollTop) * this.maxTop)
      this.emitVerticalChange(top)
    },

    setLeft(left) {
      this.left = !this.maxScrollLeft ? 0 : this._getValidLeft((left / this.maxScrollLeft) * this.maxLeft)
      this.emitHorizontalChange(left)
    },
    emitHorizontalChange(scrollLeft) {
      this.curPosition.left = this._getValidScrollLeft(scrollLeft)

      this.$emit('on-horizontal-drag', this.curPosition.left, {
        isHitLeft: this.curPosition.left <= 0,
        isHitRight: this.curPosition.left >= this.maxScrollLeft
      })
    },
    emitVerticalChange(scrollTop) {
      this.curPosition.top = this._getValidScrollTop(scrollTop)

      this.$emit('on-vertical-drag', this.curPosition.top, {
        isHitTop: this.curPosition.top <= 0,
        isHitBottom: this.curPosition.top >= this.maxScrollTop
      })
    }
  }
}
</script>

<style scoped lang="scss">
  .ans-scroller {
    width: 100%;
    height: 100%;
    &::-webkit-scrollbar {
      width: 0;
      height: 0;
    }
    &::-webkit-scrollbar-track {
      margin: 0;
    }
    .scroll-area-wrapper {
      height: 100%;
      width: 100%;
      overflow: hidden;
      &::-webkit-scrollbar {
        width: 0;
        height: 0;
      }
      &::-webkit-scrollbar-track {
        margin: 0;
      }
      /*隐藏滚动条，当IE下溢出，仍然可以滚动*/
      -ms-overflow-style:none;
      /*火狐下隐藏滚动条*/
      overflow:-moz-scrollbars-none;
    }
  }
</style>
