<template>
  <div class="nut-video" ref="videocon">
    <video
      ref="root"
      class="nut-video-player"
      :muted="options.muted"
      :autoplay="options.autoplay"
      :loop="options.loop"
      :poster="options.poster"
      :controls="options.controls"
      :preload="options.preload"
      @error="handleError"
    >
      <source :src="source.src" :type="source.type" />
    </video>
    <div
      class="playing-mask"
      ref="touchMask"
      v-if="showToolbox && !isDisabled"
      @click="play"
    ></div>
    <div
      class="nut-video-play-btn"
      v-if="showToolbox && !isDisabled"
      ref="palyBtn"
      v-show="!state.playing"
      @click="play"
    ></div>
    <div
      class="nut-video-controller"
      v-show="showToolbox && !isDisabled"
      :class="{ 'show-control': !state.playing, 'hide-control': state.playing }"
    >
      <div class="control-play-btn" @click="play"></div>
      <div class="current-time">{{ videoSet.displayTime }}</div>
      <div class="progress-container">
        <div class="progress" ref="progressBar">
          <div class="buffered" :style="{ width: `${videoSet.loaded}%` }"></div>
          <div
            class="video-ball"
            :style="{
              transform: `translate3d(${videoSet.progress.current}px, -50%, 0)`
            }"
            @touchmove.stop.prevent="touchSlidMove($event)"
            @touchstart.stop="touchSlidSrart($event)"
            @touchend.stop="touchSlidEnd($event)"
          >
            <div class="move-handle"></div>
          </div>
          <div class="played" ref="playedBar"></div>
        </div>
      </div>
      <div class="duration-time">{{ videoSet.totalTime }}</div>
      <div
        class="volume"
        @click="handleMuted"
        :class="{ muted: state.isMuted }"
      ></div>
      <div class="fullscreen-icon" @click="fullScreen"></div>
    </div>
    <!-- 错误弹窗 -->
    <div class="nut-video-error" v-show="state.isError">
      <p class="lose">视频加载失败</p>
      <p class="retry" @click="retry">点击重试</p>
    </div>
  </div>
</template>
<script lang="ts">
import {
  computed,
  reactive,
  ref,
  toRefs,
  watch,
  nextTick,
  onMounted
} from 'vue';
import { createComponent } from '../../utils/create';
import { throttle } from '../../utils/throttle.js';
import './index.scss';
const { create } = createComponent('video');

export default create({
  props: {
    source: {
      type: Object,
      default: {}
    },
    options: {
      type: Object,
      default: {
        autoplay: false, //是否自动播放
        volume: 0.5,
        poster: '',
        loop: false,
        controls: true,
        muted: false, //是否静音
        disabled: false, //禁止操作
        playsinline: false, //行内展示
        touchPlay: false,
        preload: ''
      },
      required: true
    },
    model: {
      type: String,
      default: ''
    }
  },
  components: {},
  emits: ['click', 'play', 'pause', 'playend'],

  setup(props, { emit }) {
    const state = reactive({
      videoElm: null,
      initial: true, //控制封面的显示
      showToolbox: false, //控制控制器和标题的显示
      // 视频容器元素
      player: {
        $player: null,
        pos: null
      },
      // progress进度条元素
      progressBar: {
        progressElm: null, // 进度条DOM对象
        pos: null
      },
      // video控制显示设置
      videoSet: {
        loaded: 0, // 缓存长度
        displayTime: '00:00', // 进度时间
        totalTime: '00:00', // 总时间
        progress: {
          width: 0, // 进度条长度
          current: 0 // 进度条当前位置
        }
      },
      state: {
        controlShow: true,
        vol: 0.5, //音量
        currentTime: 0, //当前时间
        fullScreen: false,
        playing: false, //是否正在播放
        isLoading: false,
        isEnd: false,
        isError: false,
        isMuted: false
      },
      showTouchMask: false
    });
    const root = ref<HTMLElement>();
    const isDisabled = computed(() => {
      return props.options.disabled;
    });

    watch(props.source, (newValue) => {
      if (newValue.src) {
        nextTick(() => {
          (state.videoElm as any).load();
        });
      }
    });

    watch(
      props.options,
      (newValue) => {
        state.state.isMuted = newValue.muted ? newValue.muted : false;
      },
      { immediate: true }
    );
    const init = () => {
      (state.videoElm as any) = root.value;

      if (props.options.autoplay) {
        (state.videoElm as any).play();
      }

      if (props.options.touchPlay) {
        state.showTouchMask = true;
      }

      if (props.options.playsinline) {
        (state.videoElm as any).setAttribute(
          'playsinline',
          props.options.playsinline
        );
        (state.videoElm as any).setAttribute(
          'webkit-playsinline',
          props.options.playsinline
        );
        (state.videoElm as any).setAttribute('x5-video-player-type', 'h5-page');
        (state.videoElm as any).setAttribute(
          'x5-video-player-fullscreen',
          false
        );
      }
      volumeHandle();

      if (state.showToolbox) {
        customerInit();
      } else {
        (state.videoElm as any).addEventListener('play', () => {
          state.state.playing = true;
          emit('play', state.videoElm as any);
        });
        (state.videoElm as any).addEventListener('pause', () => {
          state.state.playing = false;
          emit('pause', state.videoElm as any);
        });
        (state.videoElm as any).addEventListener('ended', playEnded);

        (state.videoElm as any).addEventListener(
          'timeupdate',
          throttle(getPlayTime, 100, 1)
        );
      }
    };

    const customerInit = () => {
      const $player = root.value;
      const $progress = root.value!.getElementsByClassName('progress')[0];
      // 播放器位置
      (state.player.$player as any) = $player;
      (state.progressBar.progressElm as any) = $progress;
      (state.progressBar.pos as any) = $progress.getBoundingClientRect();
      state.videoSet.progress.width = Math.round(
        $progress.getBoundingClientRect().width
      );
    };

    const play = () => {
      if (props.options.autoplay && props.options.disabled) {
        state.state.playing = true;
        return false;
      }
      state.state.playing = !state.state.playing;
      if (state.videoElm) {
        // 播放状态
        if (state.state.playing) {
          try {
            (state.videoElm as any).play();
            // 监听缓存进度
            (state.videoElm as any).addEventListener('progress', () => {
              getLoadTime();
            });
            // 监听播放进度
            (state.videoElm as any).addEventListener(
              'timeupdate',
              throttle(getPlayTime, 100, 1)
            );
            // 监听结束
            (state.videoElm as any).addEventListener('ended', playEnded);
            emit('play', state.videoElm);
          } catch (e) {
            // 捕获url异常出现的错误
            handleError();
          }
        }
        // 停止状态
        else {
          (state.videoElm as any).pause();
          emit('pause', state.videoElm);
        }
      }
    };
    const timeFormat = (t: number) => {
      var h = Math.floor(t / 3600) as string | number;
      if (h < 10) {
        h = '0' + h;
      }
      var m = Math.floor((t % 3600) / 60) as string | number;
      if (m < 10) {
        m = '0' + m;
      }
      var s = Math.round((t % 3600) % 60) as string | number;
      if (s < 10) {
        s = '0' + s;
      }
      var str = '';
      if (h != 0) {
        str = h + ':' + m + ':' + s;
      } else {
        str = m + ':' + s;
      }
      return str;
    };

    const getLoadTime = () => {
      if (state.videoSet.loaded)
        state.videoSet.loaded =
          ((state.videoElm as any).buffered.end(0) /
            (state.videoElm as any).duration) *
          100;
    };

    const getPlayTime = () => {
      const percent =
        (state.videoElm as any).currentTime / (state.videoElm as any).duration;
      state.videoSet.progress.current = Math.round(
        state.videoSet.progress.width * percent
      );

      // 赋值时长
      state.videoSet.totalTime = timeFormat((state.videoElm as any).duration);
      state.videoSet.displayTime = timeFormat(
        (state.videoElm as any).currentTime
      );
    };

    const playEnded = () => {
      state.state.playing = false;
      state.state.isEnd = true;
      state.videoSet.displayTime = '00:00';
      state.videoSet.progress.current = 0;
      (state.videoElm as any).currentTime = 0;
      emit('playend', state.videoElm);
    };

    const handleError = () => {
      state.state.isError = true;
    };

    const volumeHandle = () => {
      state.state.vol = props.options.volume;
    };

    const handleMuted = () => {
      state.state.isMuted = !state.state.isMuted;
      (state.videoElm as any).muted = state.state.isMuted;
    };

    const touchSlidSrart = () => {};

    const touchSlidMove = (e: any) => {
      let currentX = e.targetTouches[0].pageX;
      let offsetX = currentX - (state.progressBar.pos as any).left;
      // 边界检测
      if (offsetX <= 0) {
        offsetX = 0;
      }

      if (offsetX >= state.videoSet.progress.width) {
        offsetX = state.videoSet.progress.width;
      }
      state.videoSet.progress.current = offsetX;

      let percent =
        state.videoSet.progress.current / state.videoSet.progress.width;
      (state.videoElm as any).duration &&
        setPlayTime(percent, (state.videoElm as any).duration);
    };

    const touchSlidEnd = (e: any) => {
      let currentX = e.changedTouches[0].pageX;
      let offsetX = currentX - (state.progressBar.pos as any).left;
      state.videoSet.progress.current = offsetX;
      // 这里的offsetX都是正数
      let percent = offsetX / state.videoSet.progress.width;
      (state.videoElm as any).duration &&
        setPlayTime(percent, (state.videoElm as any).duration);
    };

    const setPlayTime = (percent: number, totalTime: number) => {
      (state.videoElm as any).currentTime = Math.floor(percent * totalTime);
    };

    const retry = () => {
      // console.log('error');
      state.state.isError = false;
      init();
    };

    const fullScreen = () => {
      if (!state.state.fullScreen) {
        state.state.fullScreen = true;
        (state.videoElm as any).webkitRequestFullScreen();
      } else {
        state.state.fullScreen = false;
        (document as any).webkitCancelFullScreen();
      }
    };

    onMounted(() => {
      init();
    });

    return {
      root,
      ...toRefs(props),
      ...toRefs(state),
      handleError,
      isDisabled,
      play,
      handleMuted,
      touchSlidSrart,
      touchSlidMove,
      touchSlidEnd,
      retry,
      fullScreen
    };
  }
});
</script>

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