<template>
  <div class="enjoyPlayer" v-click-outside="handleListOutside">
    <div v-if="!isBase" v-show="isShowEnjoyPlayerList" class="enjoyPlayerList">
      <div class="titNameCon">
        <div class="titName">
          <div class="tit">{{ `音频列表 (${elementInfo.enjoyList?.length || 0})` }}</div>
        </div>
      </div>
      <div class="enjoyPlayerListCon">
        <draggable
          :forceFallback="true"
          :handle="'.drag-handle_enjoyPlayer'"
          v-model="elementInfo.enjoyList"
          itemKey="id"
          :animation="200"
          :scroll="true"
          :scrollSensitivity="50"
          @end="handleDragEnd"
        >
          <template #item="{ element }">
            <div class="playerItem" :class="{ active: element.id === elementInfo.sid }" @click="handlePlayMusic(element)">
              <div class="itemLeft">
                {{ element.title }}
              </div>
              <div class="itemRight">
                <template v-if="!isScreening">
                  <div class="itemListBtn drag-handle_enjoyPlayer" @click.stop></div>
                  <div class="itemCloseBtn" @click.stop="handleEnjoyDel(element.id)"></div>
                </template>
              </div>
            </div>
          </template>
        </draggable>
      </div>
    </div>
    <audio
      v-if="!isBase"
      ref="audioRef"
      :src="elementInfo.src"
      @durationchange="handleDurationchange()"
      @timeupdate="handleTimeupdate()"
      @play="handlePlayed()"
      @pause="handlePaused()"
      @ended="handleEnded()"
      @progress="handleProgress()"
      @error="handleError()"
    ></audio>
    <div class="playerCon">
      <div class="operateBtn" :class="{ paused: paused }" @click="toggle"></div>
      <div class="operateMidCon">
        <div class="titleCon">
          <div class="title">{{ elementInfo.title }}</div>
          <div class="timesCon">
            {{ `${ptime}/${dtime}` }}
          </div>
        </div>
        <div class="bar-wrap" ref="playBarWrap" @mousedown="handleMousedownPlayBar()" @touchstart="handleMousedownPlayBar()">
          <div class="bar">
            <div class="loaded" :style="{ width: loadedBarWidth }"></div>
            <div class="played" :style="{ width: playedBarWidth }">
              <div class="thumb"></div>
            </div>
          </div>
        </div>
      </div>
      <div class="operateRightBtn">
        <div class="preBtn" @click="handleChangeMusic('pre')"></div>
        <div class="nextBtn" @click="handleChangeMusic('next')"></div>
        <div class="listBtn" @click="hanleEnjoyList"></div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, onUnmounted, watch } from "vue"
import message from "@/utils/message"
import type { PPTEnjoyElement } from "@/types/slides"
import { useSlidesStore, useMainStore } from "@/store"
import draggable from "vuedraggable"

const slidesStore = useSlidesStore()
const mainStore = useMainStore()
const props = withDefaults(
  defineProps<{
    scale?: number
    elementInfo: PPTEnjoyElement
    needWaitAnimation?: boolean
    isScreening?: boolean
    isBase?: boolean
  }>(),
  {
    scale: 1,
    isScreening: false,
    isBase: false
  }
)

watch(
  () => props.needWaitAnimation,
  () => {
    if (props.needWaitAnimation) {
      pause()
    }
  }
)

function handleListOutside() {
  if (!props.isBase && isShowEnjoyPlayerList.value) {
    hanleEnjoyList()
  }
}

const isShowEnjoyPlayerList = ref(false)

function hanleEnjoyList() {
  if (props.isBase) {
    return
  }
  isShowEnjoyPlayerList.value = !isShowEnjoyPlayerList.value
  mainStore.setIsPPTWheelPageState(!isShowEnjoyPlayerList.value)
}
// 卸载之后让ppt能滚动
onUnmounted(() => {
  mainStore.setIsPPTWheelPageState(true)
})
function handleDragEnd() {
  slidesStore.updateElement({
    id: props.elementInfo.id,
    props: {
      enjoyList: props.elementInfo.enjoyList
    }
  })
}

function handleEnjoyDel(id: string) {
  if (id === props.elementInfo.sid) {
    //删除组件
    if (props.elementInfo.enjoyList.length === 1) {
      slidesStore.deleteElement(props.elementInfo.id)
    } else {
      const index = props.elementInfo.enjoyList.findIndex(item => {
        return item.id === id
      })
      // eslint-disable-next-line vue/no-mutating-props
      props.elementInfo.enjoyList.splice(index, 1)
      const enjoyList = props.elementInfo.enjoyList
      slidesStore.updateElement({
        id: props.elementInfo.id,
        props: {
          sid: enjoyList[0].id,
          title: enjoyList[0].title,
          src: enjoyList[0].src,
          enjoyList: enjoyList
        }
      })
      // 关闭播放按钮
      paused.value = true
    }
  } else {
    const index = props.elementInfo.enjoyList.findIndex(item => {
      return item.id === id
    })
    // eslint-disable-next-line vue/no-mutating-props
    props.elementInfo.enjoyList.splice(index, 1)
    slidesStore.updateElement({
      id: props.elementInfo.id,
      props: {
        enjoyList: props.elementInfo.enjoyList
      }
    })
  }
}

function handleChangeMusic(type: "pre" | "next") {
  if (props.elementInfo.enjoyList.length === 1) {
    seek(0)
  } else {
    let index = props.elementInfo.enjoyList.findIndex(item => {
      return item.id === props.elementInfo.sid
    })
    index += type === "next" ? 1 : -1
    if (index > props.elementInfo.enjoyList.length - 1) {
      index = 0
    } else if (index < 0) {
      index = props.elementInfo.enjoyList.length - 1
    }
    const enjoyData = props.elementInfo.enjoyList[index]
    slidesStore.updateElement({
      id: props.elementInfo.id,
      props: {
        sid: enjoyData.id,
        title: enjoyData.title,
        src: enjoyData.src
      }
    })
    currentTime.value = 0
    duration.value = 0
    loaded.value = 0
  }
}

function handlePlayMusic(item: Record<string, any>) {
  if (item.id === props.elementInfo.sid) {
    toggle()
  } else {
    slidesStore.updateElement({
      id: props.elementInfo.id,
      props: {
        sid: item.id,
        title: item.title,
        src: item.src
      }
    })
    currentTime.value = 0
    duration.value = 0
    loaded.value = 0
  }
}

const secondToTime = (second = 0) => {
  if (second === 0 || isNaN(second)) return "00:00"

  const add0 = (num: number) => (num < 10 ? "0" + num : "" + num)
  const hour = Math.floor(second / 3600)
  const min = Math.floor((second - hour * 3600) / 60)
  const sec = Math.floor(second - hour * 3600 - min * 60)
  return (hour > 0 ? [hour, min, sec] : [min, sec]).map(add0).join(":")
}

const getBoundingClientRectViewLeft = (element: HTMLElement) => {
  return element.getBoundingClientRect().left
}

const audioRef = ref<HTMLAudioElement>()
const playBarWrap = ref<HTMLElement>()

const paused = ref(true)
const currentTime = ref(0)
const duration = ref(0)
const loaded = ref(0)

const isThumbDown = ref(false)

const ptime = computed(() => secondToTime(currentTime.value))
const dtime = computed(() => secondToTime(duration.value))
const playedBarWidth = computed(() => (currentTime.value / duration.value) * 100 + "%")
const loadedBarWidth = computed(() => (loaded.value / duration.value) * 100 + "%")

const seek = (time: number) => {
  if (!audioRef.value) return

  time = Math.max(time, 0)
  time = Math.min(time, duration.value)

  audioRef.value.currentTime = time
  currentTime.value = time
}

const play = () => {
  if (!audioRef.value) return

  paused.value = false
  audioRef.value.play()
}

const pause = () => {
  if (!audioRef.value) return

  paused.value = true
  audioRef.value.pause()
}

const toggle = () => {
  if (paused.value) play()
  else pause()
}

const handleDurationchange = () => {
  duration.value = audioRef.value?.duration || 0
}

const handleTimeupdate = () => {
  currentTime.value = audioRef.value?.currentTime || 0
}

const handlePlayed = () => {
  paused.value = false
}

const handlePaused = () => {
  paused.value = true
}
const handleEnded = () => {
  return
  if (isThumbDown.value) {
    return
  }
  seek(0)
  play()
}

const handleProgress = () => {
  loaded.value = audioRef.value?.buffered.length ? audioRef.value.buffered.end(audioRef.value.buffered.length - 1) : 0
  if (!paused.value) {
    play()
  }
}

const handleError = () => message.error("音频加载失败")

const thumbMove = (e: MouseEvent | TouchEvent) => {
  if (!audioRef.value || !playBarWrap.value) return
  const clientX = "clientX" in e ? e.clientX : e.changedTouches[0].clientX
  let percentage = (clientX - getBoundingClientRectViewLeft(playBarWrap.value)) / playBarWrap.value.clientWidth / props.scale
  percentage = Math.max(percentage, 0)
  percentage = Math.min(percentage, 1)
  const time = percentage * duration.value

  audioRef.value.currentTime = time
  currentTime.value = time
}

const thumbUp = (e: MouseEvent | TouchEvent) => {
  if (!audioRef.value || !playBarWrap.value) return

  const clientX = "clientX" in e ? e.clientX : e.changedTouches[0].clientX
  let percentage = (clientX - getBoundingClientRectViewLeft(playBarWrap.value)) / playBarWrap.value.clientWidth / props.scale
  percentage = Math.max(percentage, 0)
  percentage = Math.min(percentage, 1)
  const time = percentage * duration.value

  audioRef.value.currentTime = time
  currentTime.value = time
  const _time = setTimeout(() => {
    clearTimeout(_time)
    isThumbDown.value = false
  }, 500)
  document.removeEventListener("mousemove", thumbMove)
  document.removeEventListener("touchmove", thumbMove)
  document.removeEventListener("mouseup", thumbUp)
  document.removeEventListener("touchend", thumbUp)
}

const handleMousedownPlayBar = () => {
  isThumbDown.value = true
  document.addEventListener("mousemove", thumbMove)
  document.addEventListener("touchmove", thumbMove)
  document.addEventListener("mouseup", thumbUp)
  document.addEventListener("touchend", thumbUp)
}
</script>

<style lang="scss" scoped>
.enjoyPlayer {
  .playerCon {
    background: linear-gradient(180deg, #ffffff 0%, #dfdfdf 100%);
    box-shadow:
      0px 4px 22px 0px rgba(0, 0, 0, 0.12),
      inset 0px -8px 8px 0px rgba(0, 0, 0, 0.08);
    border-radius: 157px;
    padding: 14px 24px 14px 20px;
    width: 940px;
    display: flex;
    align-items: center;
    position: relative;
    z-index: 1;
    .operateBtn {
      flex-shrink: 0;
      background: url("./imgs/pause.png") no-repeat;
      background-size: 100% 100%;
      width: 100px;
      height: 100px;
      cursor: pointer;
      &.paused {
        background: url("./imgs/play.png") no-repeat;
        background-size: 100% 100%;
      }
    }
    .operateMidCon {
      margin: 0 30px;
      flex-grow: 1;
      .titleCon {
        display: flex;
        align-items: center;
        justify-content: space-between;
        .title {
          width: 288px;
          font-weight: 600;
          font-size: 30px;
          color: #131415;
          line-height: 42px;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }
        .timesCon {
          flex-shrink: 0;
          font-weight: 400;
          font-size: 25px;
          color: #999999;
          line-height: 36px;
        }
      }
      .bar-wrap {
        position: relative;
        cursor: pointer;
        width: 100%;
        margin-top: 14px;
        .bar {
          position: relative;
          height: 20px;
          width: 100%;
          background: #dcdcdc;
          box-shadow: inset 0px 3px 4px 0px #a9a9a9;
          border-radius: 12px;
          border: 3px solid rgba(255, 255, 255, 0.5);
          .loaded {
            position: absolute;
            left: 0;
            top: 0;
            bottom: 0;
            background: rgba(255, 255, 255, 0.4);
            height: 14px;
            //transition: all 0.5s ease; //这个和ppt动画效果冲突，见禅道12278
            will-change: width;
            border-radius: 12px;
          }
          .played {
            position: absolute;
            left: 0;
            top: 0;
            bottom: 0;
            height: 14px;
            will-change: width;
            background: linear-gradient(270deg, #97d1fd 0%, #0d93ff 100%);
            border-radius: 12px;

            .thumb {
              position: absolute;
              top: -16px;
              right: -27px;
              cursor: pointer;
              width: 53px;
              height: 53px;
              background: url("./imgs/td.png") no-repeat;
              background-size: 100% 100%;
            }
          }
        }
      }
    }
    .operateRightBtn {
      flex-shrink: 0;
      display: flex;
      align-items: center;
      .preBtn {
        background: url("./imgs/pre.png") no-repeat;
        background-size: 100% 100%;
        width: 80px;
        height: 80px;
        margin-right: 24px;
        cursor: pointer;
      }
      .nextBtn {
        background: url("./imgs/next.png") no-repeat;
        background-size: 100% 100%;
        width: 80px;
        height: 80px;
        margin-right: 24px;
        cursor: pointer;
      }
      .listBtn {
        background: url("./imgs/list.png") no-repeat;
        background-size: 100% 100%;
        width: 80px;
        height: 80px;
        cursor: pointer;
      }
    }
  }
  .enjoyPlayerList {
    width: 100%;
    position: absolute;
    left: 0;
    bottom: 64px;
    background: #ffffff;
    box-shadow: 0px 4px 31px 0px rgba(0, 0, 0, 0.12);
    border-radius: 28px 28px 0px 0px;
    padding: 30px 0 66px 15px;
    .titNameCon {
      padding-left: 15px;
      display: flex;
      align-items: center;
      height: 38px;
      .titName {
        font-weight: 600;
        font-size: 30px;
        color: #131415;
        line-height: 42px;
        position: relative;
        &::after {
          position: absolute;
          left: 0;
          bottom: 0;
          content: "";
          width: 100%;
          height: 17px;
          background: linear-gradient(90deg, #77bbff 0%, rgba(163, 231, 255, 0.22) 100%);
        }
        .tit {
          position: relative;
          z-index: 1;
        }
      }
    }
    .enjoyPlayerListCon {
      margin-top: 26px;
      overflow-y: auto;
      max-height: 316px;
      min-height: 140px;
      padding-right: 15px;
      .playerItem {
        padding: 22px 5px 22px 15px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        &.active {
          background: #e6f3ff;
          border-radius: 13px;
          .itemLeft {
            color: #198cfe;
            font-weight: 600;
          }
        }
        .itemLeft {
          font-weight: 400;
          font-size: 28px;
          color: #131415;
          line-height: 40px;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }
        .itemRight {
          margin-left: 10px;
          flex-shrink: 0;
          display: flex;
          .itemListBtn {
            width: 44px;
            height: 44px;
            margin-right: 16px;
            background: url("./imgs/td2.png") no-repeat;
            background-size: 34px 34px;
            background-position: center;
            cursor: grab;
          }
          .itemCloseBtn {
            width: 44px;
            height: 44px;
            background: url("./imgs/del.png") no-repeat;
            background-size: 34px 34px;
            background-position: center;
            cursor: pointer;
          }
        }
      }
    }
  }
}
</style>
