<template>
    <sv-popup v-model="visible" position="bottom">
      <div class="sv-picker" v-if="visible" @touchmove.prevent="">
        <div class="sv-picker-header">
          <slot name="header">
            <sv-button type="text" color="stable" @click="cancel">取消</sv-button>
            <sv-button type="text" color="primary" @click="confirm">确定</sv-button>
          </slot>
        </div>
        <section class="sv-picker-body" :style="pickerBodyStyle" ref="svPickerBodyRef">
          <div class="sv-picker-group" v-for="(slot, $listIndex) in slots" ref="svPickerGroupRef">
            <div class="sv-picker-handle" :style="itemHeightStyle">
              <ul class="sv-picker-list sv-picker-handle-list" :style="itemHeightStyle" ref="handleListRef">
                <li class="sv-picker-item sv-picker-handle-item" :style="itemHeightStyle"
                    v-for="(textOrValue, $itemIndex) in slot.texts && slot.texts.length > 0 ? slot.texts : slot.values">
                  {{textOrValue}}
                </li>
              </ul>
            </div>

            <div class="sv-picker-wheel" :style="itemHeightStyle">
              <ul class="sv-picker-list sv-picker-wheel-list" :style="itemHeightStyle" ref="wheelListRef">
                <li class="sv-picker-item sv-picker-wheel-item"
                    :class="{'is-hidden': isHiddenWheelItem($listIndex, $itemIndex)}"
                    :style="[itemHeightStyle, computeWheelItemStyles($listIndex, $itemIndex)]"
                    v-for="(textOrValue, $itemIndex) in slot.texts">
                  {{textOrValue}}
                </li>
              </ul>
            </div>

          </div>
        </section>
      </div>
    </sv-popup>
</template>

<script type="es6">
  import svPopup from '../popup/Popup'
  import v from '../../scripts/var'
  import {htmlFontSize} from '../../scripts/calcHtmlFontSize'
  const originalItemHeight = 70
  const originalRadius = 200

  let itemHeight = `${(originalItemHeight / v.getDesignDraftHtmlFontSize()) * htmlFontSize}`
//  let itemHeight = 36
  let radius = `${(originalRadius / v.getDesignDraftHtmlFontSize()) * htmlFontSize}`
//  let radius = 20
  /*
   根据三角形余弦公式
   反余弦得到弧度再转换为度数,这个度数是单行文字所占有的。
   余弦定理: a平方 = b平方+c平方 - 2bcCosA
   */
  let deg = Math.round((Math.acos((((radius * radius) + (radius * radius)) - (itemHeight * itemHeight)) / (2 * radius * radius)) * 180) / Math.PI)
  // deg这个值须360能整除，因为当滚动列占满一周后可以再次均匀的覆盖在上一周文字上；滚动时不会出现错位
  while (360 % deg !== 0 && deg <= 360) {
    deg += 1
  }
  // 半圆下的内容条数
  let circleCount = Math.floor(360 / deg)

  export default {
    name: 'svPicker',
    components: {
      svPopup
    },
    data () {
      return {
        visible: false,
        wheelSlotTexts: [],
        wheelSlotValues: [],
        range: {
          visualCount: 9,
          circleCount,
          startIndex: 0,
          endIndex: circleCount
        },
        pickerListHandlers: []
      }
    },
    props: {
      isPopup: {
        type: Boolean,
        default: false
      },
      slots: {
        type: Array,
        default () {
          return []
        }
      }
    },
    computed: {
      pickerBodyStyle () {
        return {
          height: `${itemHeight * (this.range.visualCount - 2)}px`
        }
      },
      itemHeightStyle () {
        return {
          height: `${itemHeight}px`,
          lineHeight: `${itemHeight}px`
        }
      },
    },
    methods: {
      initData () {
        this.pickerListHandlers = []
        for (let i = 0; i < this.slots.length; i++) {
          this.pickerListHandlers.push({
            scrollHeight: 0,
            startY: 0,
            endY: 0,
            startTime: 0,
            endTime: 0,
            currentY: 0,
            activeIndex: 0,
            startTranslateY: 0,
            currentTranslateY: 0
          })
        }
      },
      computeWheelItemStyles (listIndex, itemIndex) {
        return {
          transform: `rotate3d(1, 0, 0, ${(-1 * itemIndex) * deg}deg) translate3d(0, 0, ${radius}px)`
        }
      },
      isHiddenWheelItem ($listIndex, $itemIndex) {
        let activeIndex = this.pickerListHandlers[$listIndex].activeIndex
        let rangeBranch = this.range.circleCount / 2
        if (Math.abs($itemIndex - activeIndex) <= rangeBranch) {
          return false
        }
        return true
      },
      bindEvents () {
        let $pickerGroups = this.$refs.svPickerGroupRef
        $pickerGroups.forEach(($pickerGroup, index) => {
          $pickerGroup.addEventListener('touchstart', event => {
            this.handleTouchStart(index, event)
          })
          $pickerGroup.addEventListener('touchmove', event => {
            event.preventDefault()
            event.stopPropagation()
            this.handleTouchMove(index, event)
          })
          $pickerGroup.addEventListener('touchend', event => {
            this.handleTouchEnd(index, event)
          })
        })
      },
      handleTouchStart (index, event) {
        let currentPickerList = this.pickerListHandlers[index]
        currentPickerList.startY = event.touches[0].pageY
        currentPickerList.startTime = event.timestamp || Date.now()
        currentPickerList.startTranslateY = currentPickerList.currentTranslateY
      },
      handleTouchMove (index, event) {
        let currentPickerList = this.pickerListHandlers[index]
        currentPickerList.currentY = event.touches[0].pageY
        let drift = currentPickerList.currentY - currentPickerList.startY
        this.move(index, drift)
        this.sliding(index)
      },
      handleTouchEnd (index, event) {
        let currentPickerList = this.pickerListHandlers[index]
        currentPickerList.endY = event.changedTouches[0].pageY
        currentPickerList.endTime = event.timestamp || Date.now()

        /*
         此段代码实现惯性缓动
         需要计算两个值：
         1.由于惯性产生的制动距离
         2.惯性持续的时间，即动画执行持续的时间
         */

        let s = currentPickerList.endY - currentPickerList.startY
        let t = currentPickerList.endTime - currentPickerList.startTime
        let v = s / t
        const a = 1.8 // 加速度

        if (t <= 300) {
          s = v * t * a
          t = 1000 + t * a
          this.move(index, s, true, t)
        } else {
          this.move(index, s, true)
        }
        this.sliding(index, true)
      },
      move (index, drift, isEnd, duration) {
        let currentPickerList = this.pickerListHandlers[index]
        let minTranslateY = 0
        let maxTranslateY = -(this.slots[index].texts.length - 1) * itemHeight
        let translateY = drift + Number(currentPickerList.startTranslateY)
        if (translateY > minTranslateY) {
          translateY = minTranslateY
        }
        if (translateY < maxTranslateY) {
          translateY = maxTranslateY
        }
        this.translateHandle(index, translateY, isEnd, duration)
        this.rotateWheel(index, translateY, isEnd, duration)
      },
      translateHandle (index, translateY, isEnd, duration) {
        let $target = this.$refs.handleListRef[index]
        let currentPickerList = this.pickerListHandlers[index]

        let endTranslateY = Math.round(translateY / itemHeight) * itemHeight
        if (isEnd) {
          currentPickerList.currentTranslateY = endTranslateY
          $target.style.transform = `translate3d(0, ${endTranslateY}px, 0)`
          $target.style.transition = `transform ${duration}ms cubic-bezier(0.19, 1, 0.22, 1)`
        } else {
          currentPickerList.currentTranslateY = translateY
          $target.style.transform = `translate3d(0, ${translateY}px, 0)`
          $target.style.transition = ''
        }

        currentPickerList.activeIndex = Math.abs(Math.round(currentPickerList.currentTranslateY / itemHeight))
      },
      rotateWheel (index, translateY, isEnd, duration) {
        let $target = this.$refs.wheelListRef[index]
        let currentPickerList = this.pickerListHandlers[index]
        let endTranslateY = Math.round(translateY / itemHeight) * itemHeight
        const singleDeg = deg / itemHeight
        let updateDeg = -translateY * singleDeg
        /* 计算touchEnd移动的整数距离 */
        let endDeg = Math.round(updateDeg / deg) * deg
        if (isEnd) {
          currentPickerList.currentTranslateY = endTranslateY
          $target.style.transform = `rotate3d(1, 0, 0, ${endDeg}deg)`
          $target.style.transition = `transform ${duration}ms cubic-bezier(0.19, 1, 0.22, 1)`
        } else {
          currentPickerList.currentTranslateY = translateY
          $target.style.transform = `rotate3d(1, 0, 0, ${updateDeg}deg)`
          $target.style.transition = ''
        }
        currentPickerList.activeIndex = Math.abs(Math.round(currentPickerList.currentTranslateY / itemHeight))
      },
      cancel (event) {
        this.$emit('cancel', event)
        this.close()
      },
      confirm () {
        this.$emit('confirm', this.getCheckedValues(), this.getCheckedTexts())
        this.close()
      },
      sliding (slotIndex, isTouchEnd = false) {
        this.$emit('sliding', this, slotIndex, isTouchEnd)
        if (isTouchEnd) {
          this.slided(slotIndex)
        }
      },
      slided (slotIndex) {
        this.$emit('slided', this, slotIndex)
      },
      setSlotCheckedIndex (slotIndex, checkedIndex) {
        this.translateHandle(slotIndex, -checkedIndex * itemHeight, true)
        this.rotateWheel(slotIndex, -checkedIndex * itemHeight, true)
      },
      getSlotCheckedIndex (slotIndex) {
        let currentPickerList = this.pickerListHandlers[slotIndex]
        return Math.abs(Math.round(currentPickerList.currentTranslateY / itemHeight))
      },
      setCheckedIndexes (indexes) {
        for (let i = 0; i < this.slots.length; i++) {
          this.setSlotCheckedIndex(indexes[i])
        }
      },
      getCheckedIndexes () {
        let indexes = []

        for (let i = 0; i < this.slots.length; i++) {
          indexes.push(this.getSlotCheckedIndex(i))
        }
        return indexes
      },
      setSlotCheckedValue (slotIndex, value) {
        this.setSlotCheckedIndex(slotIndex, this.slots[slotIndex].values.indexOf(value))
      },
      getSlotCheckedValue (slotIndex) {
        if (!this.slots[slotIndex].values || this.slots[slotIndex].values.length === 0) {
          return this.slots[slotIndex].texts[this.getSlotCheckedIndex(slotIndex)]
        }
        return this.slots[slotIndex].values[this.getSlotCheckedIndex(slotIndex)]
      },
      getSlotCheckedText (slotIndex) {
        return this.slots[slotIndex].texts[this.getSlotCheckedIndex(slotIndex)]
      },
      setSlotValues (slotIndex, values) {
        this.slots[slotIndex].values = values
      },
      getSlotValues (slotIndex) {
        return this.slots[slotIndex].values
      },
      setCheckedValues (values) {
        values.forEach((value, index) => {
          this.setSlotCheckedValue(index, value)
        })
      },
      getCheckedValues () {
        let values = []

        for (let i = 0; i < this.slots.length; i++) {
          values.push(this.getSlotCheckedValue(i))
        }
        return values
      },
      getCheckedTexts () {
        let texts = []

        for (let i = 0; i < this.slots.length; i++) {
          texts.push(this.getSlotCheckedText(i))
        }
        return texts
      },
      open () {
        this.visible = true
        this.initData()
        window.setTimeout(() => {
          this.bindEvents()
        }, 100)
      },
      close () {
        this.visible = false
        // this.clearSlots()
        this.$emit('close')
      },
      clearSlots () {
        this.slots.forEach(slot => {
          slot.values = []
          slot.texts = []
        })
      }
    }
  }
</script>

<style lang="scss" rel="stylesheet/scss">
  @import "../../styles/base";
  .sv-picker{
    .sv-picker-header{
      display: flex;
      justify-content: space-between;
    }
    .sv-picker-body{
      display: flex;
      font-size: pxToRem(28);
      background: #f2f2f2;
      overflow: hidden;
      .sv-picker-group{
        flex: 1;
        position: relative;
        .sv-picker-handle, .sv-picker-wheel{
          position: absolute;
          top:50%;
          transform: translateY(-50%);
          width: 100%;
          border-top: 1px solid #ccc;
          border-bottom: 1px solid #ccc;
          font-size: pxToRem(32);
          .sv-picker-handle-list{
            .sv-picker-handle-item{
            }
          }
          .sv-picker-list{
            .sv-picker-item{
              text-align: center;
            }
          }
        }
        .sv-picker-handle{
          overflow: hidden;
          z-index: 2;
          color:#333;
        }
        .sv-picker-wheel{
          z-index: 1;
          color:#666;
        }
        .sv-picker-wheel{
          width: 100%;
          position: relative;
          .sv-picker-wheel-list{
            transform-style: preserve-3d;
            width: 100%;
            left: 50%;
            .sv-picker-wheel-item{
              backface-visibility: hidden;
              position: absolute;
              top: 0;
              left:0;
              width: 100%;
              &.is-hidden{
                display: none;
              }
            }
          }
        }
      }
    }
  }
</style>
