<script lang="ts" setup>
import { computed, defineComponent, nextTick, onMounted, onUnmounted, reactive, watch } from 'vue'
import { PREFIX } from '../_constants'
import { useExtend } from '../_hooks'
import { cloneDeep, getMainClass } from '../_utils'
import type { IData } from './countup'
import { countupEmits, countupProps } from './countup'

const props = defineProps(countupProps)
const emit = defineEmits(countupEmits)
defineExpose({ machineLuck })
const classes = computed(() => {
  return getMainClass(props, componentName)
})
const data = reactive<IData>({
  valFlag: false,
  current: 0,
  sortFlag: 'add',
  initDigit1: 0,
  initDigit2: 0,
  to0_10: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
  to10_0: [0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1],
  timer: null,
  totalCount: 0, // 正整数
  pointNum: 0, // 小数位
  numberVal: 0, // 数字
  num_total_len: 0, // 数字长度
  relNum: 0, // 去除小数点
  customNumber: 1,
  prizeLevelTrun: 0,
  prizeY: [],
  prizeYPrev: [],
  // machineTransition: 'none',
  finshMachine: 0,
  notPrize: [],
  typeMachine: '',
})
const { startFlag, scrolling, customBgImg, type } = reactive(props)
watch(
  () => props.customChangeNum,
  (count, prevCount) => {
    clearIntervalTime()
    // data.customNumber = count;
    countGo(0)
  },
)
watch(
  () => props.machinePrizeLevel,
  (count, prevCount) => {
    data.prizeLevelTrun = count
  },
)
watch(
  () => props.initNum,
  (count, prevCount) => {
    data.current = count
    data.valFlag = false
    valChange()
  },
)
watch(
  () => props.endNum,
  (count, prevCount) => {
    data.current = props.initNum
    data.valFlag = false
    valChange()
  },
)
function valChange() {
  if (data.valFlag)
    return false

  if (startFlag) {
    if (scrolling || customBgImg) {
      if (type !== 'machine')
        countGo()
    }
    else {
      countChange()
      setTimeout(() => {
        data.valFlag = true
      }, 300)
    }
  }
}
// 清空定时器
function clearIntervalTime() {
  clearInterval(Number(data.timer))
  data.timer = null
}
// 精确计算
function calculation(num1: number, num2: number, type: string) {
  const num1Digits = (num1.toString().split('.')[1] || '').length
  const num2Digits = (num2.toString().split('.')[1] || '').length
  const baseNum = 10 ** Math.max(num1Digits, num2Digits)
  if (type === '-') {
    const n = Number((num1 * baseNum - num2 * baseNum).toFixed(0))
    return n / baseNum
  }
  else {
    const m = Number((num1 * baseNum + num2 * baseNum).toFixed(0))
    return m / baseNum
  }
}
// 数字滚动-top值
function topNumber(index: number) {
  const { num_total_len, pointNum, initDigit1, initDigit2, sortFlag } = data
  const idx1
        = sortFlag === 'add' || sortFlag === 'equal'
          ? String(initDigit2)[index - (num_total_len - pointNum)]
          : 10 - Number(String(initDigit2)[index - (num_total_len - pointNum)])
  const idx2
        = sortFlag === 'add' || sortFlag === 'equal' ? String(initDigit1)[index] : 10 - Number(String(initDigit1)[index])
  let num
        = index > num_total_len - pointNum - 1
          ? `${-idx1 * 100}%`
          : index <= String(initDigit1).length - 1
            ? `${-idx2 * 100}%`
            : 0
  if (num === '-1000%')
    num = 0

  return num
}
// 数字滚动-到哪里了
function turnNumber(index: number) {
  const { num_total_len, pointNum, initDigit1, initDigit2, sortFlag } = data
  const idx1 = String(initDigit2)[index - (num_total_len - pointNum)]
  const num
        = index > num_total_len - pointNum - 1
          ? idx1 || 0
          : index <= String(initDigit1).length - 1
            ? String(initDigit1)[index]
            : 0
  return num
}
// 基础用法
function countChange() {
  const { endNum, initNum, speed, toFixed } = props
  const countTimer = setInterval(() => {
    if (initNum > endNum) {
      // 减少
      if (data.current <= endNum || data.current <= speed) {
        // 数字减小，有可能导致current小于speed
        data.current = Number(endNum.toFixed(toFixed))
        clearInterval(countTimer)
        emit('scrollEnd')
        data.valFlag = false
      }
      else {
        data.current = Number((Number.parseFloat(String(data.current)) - Number.parseFloat(String(speed))).toFixed(toFixed))
      }
    }
    else {
      // 增加
      if (data.current >= endNum) {
        data.current = Number(endNum.toFixed(toFixed))
        clearInterval(countTimer)
        emit('scrollEnd')
        data.valFlag = false
      }
      else {
        data.current = Number((Number.parseFloat(String(data.current)) + Number.parseFloat(String(speed))).toFixed(toFixed))
      }
    }
  }, props.during)
}
function countGo(flag?: number): void {
  let { initNum, endNum, toFixed, customBgImg } = props
  if (customBgImg)
    initNum = props.customChangeNum

  // --------------
  let startNumber1, startNumber2, endNumber1, endNumber2
  if (initNum !== 0) {
    if (toFixed !== 0)
      initNum = Number(initNum.toFixed(toFixed))

    if (String(initNum).includes('.')) {
      startNumber1 = String(initNum).split('.')[0].length
      startNumber2 = String(initNum).split('.')[1].length
    }
    else {
      startNumber1 = String(initNum).length
      startNumber2 = 0
    }
  }
  else {
    startNumber1 = 1
    startNumber2 = 0
  }
  if (endNum !== 0) {
    if (toFixed !== 0)
      endNum = Number(endNum.toFixed(toFixed))

    if (String(endNum).includes('.')) {
      endNumber1 = String(endNum).split('.')[0].length
      endNumber2 = String(endNum).split('.')[1].length
    }
    else {
      endNumber1 = String(endNum).length
      endNumber2 = 0
    }
  }
  else {
    endNumber1 = 1
    endNumber2 = 0
  }
  const len1 = startNumber1 >= endNumber1 ? startNumber1 : endNumber1
  const len2 = startNumber2 >= endNumber2 ? startNumber2 : endNumber2
  data.num_total_len = len1 + len2

  data.pointNum = len2

  // --------------
  if (initNum > endNum) {
    // 减少
    data.sortFlag = 'reduce'
    data.to0_10 = [0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    data.totalCount = calculation(initNum, endNum, '-')
    data.numberVal = Number(String(initNum))
  }
  else if (initNum < endNum) {
    // 增加
    data.sortFlag = 'add'
    data.to0_10 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
    data.totalCount = calculation(endNum, initNum, '-')
    data.numberVal = Number(String(endNum))
  }
  else {
    data.sortFlag = 'equal'
  }
  // 将小数位数计算后，补0
  let unit = 1
  for (let i = 0; i < data.pointNum; i++)
    unit *= 10

  const rel_big = data.numberVal * unit // 去除小数点后的数，unit几个零表示有几个小数
  data.relNum = rel_big
  // this.totalCount = rel_big;
  if (toFixed !== 0) {
    // 计算小数点后的位数，小数位
    data.pointNum = String(data.numberVal).split('.')[1] ? String(data.numberVal).split('.')[1].length : 0
    // 数字长度
    data.num_total_len = String(rel_big).length
  }
  if (String(initNum).includes('.')) {
    const n = String(initNum).split('.')
    data.initDigit1 = Number(n[0])
    data.initDigit2 = Number(n[1])
  }
  else {
    data.initDigit1 = initNum
    data.initDigit2 = 0
  }

  if (scrolling && !customBgImg) {
    // #ifdef H5
  // 数字都是从小加到大的，所以我们循环转动最后一个数字，传入最后一个数字的DOM
    nextTick(() => {
      if (data.sortFlag === 'equal')
        return false

      const refsDom: HTMLCollectionOf<Element> = document.getElementsByClassName('nut-countup__number-item')

      const element = refsDom[data.num_total_len - 1]
      runTurn(element)
    })
    // #endif
  }
  else {
    if (flag !== 0)
      imgNumberScroll()
  }
}
function runTurn(el: Element) {
  clearIntervalTime()
  let m = 1
  if (data.pointNum !== 0)
    m = 1 / 10 ** data.pointNum;

  // 设置定时器
  (data.timer as any) = setInterval(() => {
    runStep(el)
    data.totalCount = calculation(data.totalCount, m, '-')
    // that.totalCount--;
    if (data.totalCount <= 0) {
      clearIntervalTime()
      emit('scrollEnd')
      data.valFlag = false
    }
  }, props.during)
}

function runStep(el: any) {
  // let currentTurn = el.getAttribute('turn-number');
  const currentTurn = el.style.all
  let turningNum: number
  if (data.sortFlag === 'add')
    turningNum = Number.parseInt(String(currentTurn)) + 1
  else
    turningNum = Number.parseInt(String(currentTurn)) - 1 >= 0 ? Number.parseInt(String(currentTurn)) - 1 : 9

  // el.setAttribute('turn-number', String(turningNum));
  el.style.all = String(turningNum)

  if (el.style.transition === 'none' || turningNum === 1 || !el.style.transition)
    el.style.transition = `all linear ${props.during}ms`

  if (turningNum === 10 || (data.sortFlag === 'reduce' && turningNum === 0)) {
    let timeOut: any = null
    // el.style.top = `-${turningNum * 100}%`;
    el.style.top = `-${data.sortFlag === 'add' ? turningNum * 100 : (10 - turningNum) * 100}%`
    // el.setAttribute('turn-number', '0');
    el.style.all = '0'
    timeOut = setTimeout(() => {
      timeOut && clearTimeout(timeOut)
      el.style.transition = 'none'
      el.style.top = '0'
      // 前面数字的滚动，用于递增
      if (turningNum === 10) {
        if (el.previousSibling)
          runStep(el.previousSibling as HTMLElement)
      }
    }, 0.975 * props.during)
  }
  else {
    // el.style.top = `-${(10-turningNum)*100}%`;
    el.style.top = `-${data.sortFlag === 'add' ? turningNum * 100 : (10 - turningNum) * 100}%`
  }
  // 用于递减的时候
  if (el.style.top === '-100%' && data.sortFlag === 'reduce')
    runStep(el.previousSibling as HTMLElement)
}
// 自定义图片
function imgNumberScroll() {
  let m = 1
  if (data.pointNum !== 0)
    m = 10 ** data.pointNum
  // #ifdef H5
  nextTick(() => {
    const f = document.getElementsByClassName('nut-countup__numberimg')[0]
    // setTimeout(() => {
    //   data.relNum = calculation(data.relNum, m * props.speed, '+');
    // }, props.during);
    f.addEventListener('webkitTransitionEnd', () => {
      emit('scrollEnd')
      data.valFlag = false
      // setTimeout(() => {
      //   data.relNum = calculation(data.relNum, m * props.speed, '+');
      // }, props.during);
    })
  })
  // #endif
}
// 不中奖设置随机数
function generateRandom() {
  data.notPrize = []
  while (data.notPrize.length < 3) {
    const rand: number = Math.floor(Math.random() * props.machinePrizeNum + 1)
    if (!data.notPrize.includes(rand))
      data.notPrize.push(rand)
  }
}
// 抽奖
function machineLuck() {
  const machineTurnMoreNum = props.machineTurnMore < 0 ? 0 : props.machineTurnMore
  const distance = props.numHeight * props.machinePrizeNum // 所有奖品的高度，雪碧图的高度
  if (data.prizeLevelTrun < 0)
    generateRandom()

  for (let i = 0; i < props.machineNum; i++) {
    setTimeout(() => {
      const turn = distance * (i + 1 + Number.parseFloat(String(machineTurnMoreNum)))
      if (data.prizeYPrev.length !== 0) {
        // this.machineTransition = 'none';
        // console.log(this.prizeYPrev[i]-(this.numHeight * this.machinePrizeNum));
        // this.$set(data.prizeY, i, data.prizeYPrev[i]);
        data.prizeY[i] = data.prizeYPrev[i]
      }
      const local = data.prizeYPrev[i] ? data.prizeYPrev[i] : 0
      let newLocation
            = turn + local + (props.machinePrizeNum - data.prizeLevelTrun + 1) * props.numHeight + (distance - local)
      if (data.prizeLevelTrun < 0)
        newLocation += props.numHeight * data.notPrize[i]

      scrollTime(
        i,
        // parseFloat((this.machinePrizeNum-(this.prizeLevelTrun-1))*this.numHeight + turn + local),
        newLocation,
        local,
      )
    }, 500 * i)
  }
}
useExtend({ machineLuck })
function scrollTime(index: number, total: number, num: number) {
  // this.machineTransition = `all linear ${this.during/this.machinePrizeNum}ms`;
  let t: any = setInterval(() => {
    if (num <= total) {
      num += 10
      data.prizeY[index] = Number.parseFloat(String(num))
    }
    else {
      clearInterval(t)
      t = null
      data.finshMachine += 1
      data.prizeY[index] = total
      if (data.finshMachine === props.machineNum) {
        const distance = props.numHeight * props.machinePrizeNum
        data.prizeYPrev = []
        const prevAry = cloneDeep(data.prizeY)
        prevAry.forEach((item: any) => {
          let n = item
          while (n > distance)
            n -= distance

          data.prizeYPrev.push(n)
        })
        setTimeout(() => {
          data.finshMachine = 0
          if (data.prizeLevelTrun < 0) {
            emit('scrollEnd', false)
            data.valFlag = false
          }
          else {
            emit('scrollEnd', true)
            data.valFlag = false
          }
        }, 130)
      }
    }
  }, 30)
}

onMounted(() => {
  data.current = props.initNum
  nextTick(() => {
    valChange()
  })
})

onUnmounted(() => {
  clearIntervalTime()
  data.timer = null
})
</script>

<script lang="ts">
const componentName = `${PREFIX}-countup`

export default defineComponent({
  name: componentName,
  options: {
    virtualHost: true,
    addGlobalClass: true,
    styleIsolation: 'shared',
  },
})
</script>

<template>
  <view :class="classes" :style="customStyle">
    <template v-if="customBgImg !== ''">
      <template v-if="type === 'machine'">
        <view class="nut-countup__machine" :style="{ height: `${numHeight}px` }">
          <view
            v-for="(val, index) of machineNum"
            :key="`mImg${index}`"
            class="nut-countup__machine-item"
            :style="{
              width: `${numWidth}px`,
              height: `${numHeight}px`,
              background: `url(${customBgImg}) `,
              backgroundPosition: `0 ${data.prizeY[index]}px`,
            }"
          />
        </view>
      </template>
      <template v-else>
        <view class="nut-countup__numberimg" :style="{ height: `${numHeight}px` }">
          <view
            v-for="(val, index) of data.num_total_len"
            :key="`cImg${index}`"
            class="nut-countup__numberimg__item"
            :style="{
              width: `${numWidth}px`,
              height: `${numHeight}px`,
              left:
                `${numWidth
                  * (index > data.num_total_len - data.pointNum - 1
                    ? index === data.num_total_len - data.pointNum
                      ? index * 1.5
                      : index * 1.3
                    : index)
                }px`,
              backgroundImage: `url(${customBgImg})`,
              backgroundPosition:
                `0 ${-(+String(data.relNum)[index] * numHeight + customSpacNum * +String(data.relNum)[index])}px`,
              transition: `all linear ${during / 10}ms`,
            }"
          />
          <view
            v-if="data.pointNum > 0"
            class="nut-countup-pointstyl"
            :style="{
              width: `${numWidth / 2}px`,
              bottom: 0,
              left: `${numWidth * (data.num_total_len - data.pointNum) * 1.1}px`,
              fontSize: '30px',
            }"
          >
            .
          </view>
        </view>
      </template>
    </template>
    <template v-else>
      <view
        v-if="scrolling"
        class="nut-countup__number"
        :style="{
          width: `${numWidth * data.num_total_len + numWidth / 3}px`,
          height: `${numHeight}px`,
          lineHeight: `${numHeight}px`,
        }"
      >
        <view
          v-for="(val, index) of data.num_total_len"
          :key="val"
          class="nut-countup__number-item"
          :style="{
            all: turnNumber(index) as any,
            top: topNumber(index),
            left: `${numWidth * (index > data.num_total_len - data.pointNum - 1 ? index * 1.1 : index)}px`,
          }"
          :turn-number="turnNumber(index)"
        >
          <view
            v-for="(item, idx) of data.to0_10"
            :key="`dote${idx}`"
            class="nut-countup__number-item__span"
            :style="{
              width: `${numWidth}px`,
              height: `${numHeight}px`,
              lineHeight: `${numHeight}px`,
            }"
          >
            {{ item }}
          </view>
        </view>
        <view
          v-if="data.pointNum > 0"
          class="nut-countup-pointstyl"
          :style="{
            width: `${numWidth / 3}px`,
            height: `${numHeight}px`,
            lineHeight: `${numHeight}px`,
            top: 0,
            left: `${numWidth * (data.num_total_len - data.pointNum)}px`,
          }"
        >
          .
        </view>
      </view>
      <template v-else>
        {{ data.current }}
      </template>
    </template>
  </view>
</template>

<style lang="scss">
@import './index';
</style>
