<template>
  <el-dialog
    :visible="showDialog"
    :title="title"
    :close-on-press-escape="false"
    :close-on-click-modal="false"
    class="rnr-dialog"
    width="480px"
    top="10vh"
    @close="closeDialog"
  >
    <div v-loading="loading" class="take-camera">
      <!-- 提示读取的数字，或者动作 -->
      <div class="random-num-wrapper">
        <p class="important-tips">
          录制时请{{ livenessType === LIVENESS_TYPE.ACTION ? '做出如下动作' : '用普通话大声读如下数字' }}
        </p>
        <p class="random-num">
          <i v-if="status === VIDEO_STATUS.SUCCESS" class="el-icon-success" />
          {{ livenessType === LIVENESS_TYPE.ACTION ? headAction : randomNum }}
          <span v-if="status === VIDEO_STATUS.TAKING" class="countdown">{{ countdown }}</span>
        </p>
      </div>
      <!-- 如果当前是录制前，或者录制中 -->
      <video v-show="status != VIDEO_STATUS.SUCCESS" ref="video1" name="video1" autoplay="autoplay" muted />
      <!-- 如果当前是录制成功，则可以预览视频 -->
      <div v-show="status == VIDEO_STATUS.SUCCESS" class="record-video-wrapper">
        <video ref="video2" :muted="false" name="video2" />
        <i class="btn-video" :class="isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'" @click="handlePlayVideo" />
      </div>
      <div class="take-camera-operation">
        <el-button v-if="status == VIDEO_STATUS.READY" class="btn-record" type="primary" @click.stop="handleStartRecording"><i class="imageicon-video-pause" />开始录制</el-button>
        <el-button v-if="status == VIDEO_STATUS.TAKING" class="btn-record" type="primary" @click="handleStopRecording"><i class="imageicon-video-pause" />停止录制</el-button>
        <el-button v-if="status == VIDEO_STATUS.SUCCESS" class="btn-record" type="primary" @click="handleStartRecording"><i class="imageicon-video-pause" />重新录制</el-button>
      </div>
    </div>
    <div slot="footer" class="dialog-footer">
      <el-button size="small" @click="closeDialog">取消</el-button>
      <el-button :loading="isUploading" size="small" type="primary" @click="uploadPhoto">确定</el-button>
    </div>
  </el-dialog>
</template>
<script>
import { postVinUpload } from '@/api/realname-enterprise'
import { getToken } from '@/utils/auth'
import { getConnectDeviceId } from '@/utils/device'
import request from '@/utils/request'
import '@/styles/dialog.scss'

const LIVENESS_TYPE = {
  NUMBER: 'LIP',
  ACTION: 'ACTION'
}

// 视频状态
const VIDEO_STATUS = {
  // 准备状态
  READY: 0,
  // 录制中
  TAKING: 1,
  // 录制成功
  SUCCESS: 2
}

export default {
  name: 'DeviceVideo',
  props: {
    // 展示弹窗
    show: {
      type: Boolean,
      default: false
    },
    // 弹窗标题
    title: {
      type: String,
      default: '拍摄视频'
    },
    randomNum: {
      type: String,
      default: ''
    },
    // 验证方式
    livenessType: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      VIDEO_STATUS,
      LIVENESS_TYPE,
      loading: false,
      showDialog: false,
      recordedBlobs: [],
      mediaRecorder: null,
      // 初始化视频是等待录制
      status: VIDEO_STATUS.READY,
      // 当前文件正在上传
      isUploading: false,
      // 动作提示
      actionTip: '',
      // 是否展示动画效果
      showAnimation: false,
      // 是否初始化摄像头
      initCameraSuccess: false,
      // 动作索引
      actionIndex: 0,
      // 倒计时
      countdown: 2,
      // 是否正在播放中
      isPlaying: false
    }
  },
  computed: {
    /**
     * 头部动作
     */
    headAction() {
      // 取出所有动作
      const actions = this.randomNum.split(',')
      // 活体序列
      const { livenessActionSeq } = this.$store.getters.dict
      // 如果未开始录制
      if (this.status === VIDEO_STATUS.READY) {
        return `请先${actions.map(action => livenessActionSeq.find(seq => `${seq.value}` === `${action}`).label).join('，再')}`
      }
      // 如果当前正在录制中，则展示需要展示的动作
      if (this.status === VIDEO_STATUS.TAKING) {
        // 当前需要做的动作
        const action = actions[this.actionIndex]
        return action ? `请${livenessActionSeq.find(seq => `${seq.value}` === `${action}`).label}` : '请停止录制'
      }
      // 如果当前录制完成
      if (this.status === VIDEO_STATUS.SUCCESS) {
        return '已完成录制'
      }
      return ''
    }
  },
  watch: {
    show(val) {
      // 将内外状态同步
      this.showDialog = val
      // 如果当前是展示状态，且没有初始化过摄像头，则开始初始化摄像头
      if (val && !this.initCameraSuccess) {
        this.initCamera()
      }
    }
  },
  destroyed() {
    // 如果当前有流，则关闭流通道
    if (window.stream) {
      const tracks = window.stream.getTracks()
      tracks.forEach(t => t.stop())
    }
  },
  methods: {
    /**
    * 初始化摄像头
    */
    async initCamera() {
      this.loading = true
      try {
        // 获取设备id
        const deviceId = await getConnectDeviceId()
        // 将视频流保存到window下
        window.stream = await navigator.mediaDevices.getUserMedia({
          audio: true,
          video: deviceId ? { deviceId: { exact: deviceId }} : true
        })
        if (window.URL) {
          this.$refs.video1.srcObject = window.stream
        } else {
          this.$refs.video1.src = window.stream
        }
        // 标记当前初始化摄像头成功
        this.initCameraSuccess = true
      } catch (error) {
        console.error('Error accessing media devices.', error)
      }
      this.loading = false
    },

    /**
     * 关闭视频录制
     */
    handleStopRecording() {
      // 如果视频正在录制中，则关闭视频录制，并将状态置为成功
      if (this.status === VIDEO_STATUS.TAKING) {
        this.stopRecording()
        this.status = VIDEO_STATUS.SUCCESS
      }
    },

    /**
     * 开始录制视频
     */
    handleStartRecording() {
      // 如果当前是准备中|已经录完需要重新录制，则将状态置为录制中
      if ([VIDEO_STATUS.READY, VIDEO_STATUS.SUCCESS].includes(this.status)) {
        // 开始录制
        this.startRecording()
        // 从第一个动作开始
        this.actionIndex = 0
        // 如果当前是动作系统，则开始倒计时
        if (this.livenessType === LIVENESS_TYPE.ACTION) {
          this.startActionCountDown()
        }
        // 将视频状态置为拍摄中
        this.status = VIDEO_STATUS.TAKING
      }
    },

    /**
     * 录制视频
     */
    startRecording() {
      this.recordedBlobs = []
      let options = { mimeType: 'video/webm;codecs=vp9' }
      if (!MediaRecorder.isTypeSupported(options.mimeType)) {
        console.log(options.mimeType + ' is not Supported')
        options = { mimeType: 'video/webm;codecs=vp8' }
        if (!MediaRecorder.isTypeSupported(options.mimeType)) {
          console.log(options.mimeType + ' is not Supported')
          options = { mimeType: 'video/webm' }
          if (!MediaRecorder.isTypeSupported(options.mimeType)) {
            console.log(options.mimeType + ' is not Supported')
            options = { mimeType: '' }
          }
        }
      }

      try {
        this.mediaRecorder = new MediaRecorder(window.stream, options)
      } catch (e) {
        console.log('Exception while creating MediaRecorder: ' + e + '. mimeType: ' + options.mimeType)
        return
      }
      console.log('Created MediaRecorder', this.mediaRecorder, 'with options', options)

      this.mediaRecorder.onstop = event => console.log('Recorder stopped: ', event)
      this.mediaRecorder.ondataavailable = this.handleDataAvailable
      this.mediaRecorder.start(10)
      console.log('MediaRecorder started', this.mediaRecorder)
    },

    /**
     * 停止录制
     */
    stopRecording() {
      this.mediaRecorder.stop()
      const buffer = new Blob(this.recordedBlobs, { type: 'video/mp4' })
      this.$refs.video2.src = window.URL.createObjectURL(buffer)
      console.log('Recorded Blobs: ', this.recordedBlobs)
    },

    handleDataAvailable(event) {
      if (event.data && event.data.size > 0) {
        this.recordedBlobs.push(event.data)
      }
    },

    /**
     * 播放已经录制的视频
     */
    handlePlayVideo() {
      // 如果当前是播放中，则停止
      if (this.isPlaying) {
        this.$refs.video2.pause()
      } else {
        this.$refs.video2.play()
        // 视频播放方法
        const videoPlay = () => {
          // 移除监听事件
          this.$refs.video2.removeEventListener('ended', videoPlay)
          // 关闭播放状态
          this.isPlaying = false
        }
        // 如果播放结束，则重置
        this.$refs.video2.addEventListener('ended', videoPlay)
      }
      // 置反播放状态
      this.isPlaying = !this.isPlaying
    },

    /**
    * 关闭弹窗
    * @time 2022-05-10 16:21:17
    */
    closeDialog() {
      // 如果当前正在录制中
      if (this.status === VIDEO_STATUS.TAKING) {
        return this.$message({
          message: '视频正在录制中，请先停止录制',
          type: 'warning'
        })
      }
      // 将视频状态重置为准备状态
      this.status = VIDEO_STATUS.READY
      // 重置动作索引
      this.actionIndex = 0
      // 关闭弹框
      this.$emit('update:show', false)
    },

    /**
     * 开始呈现倒计时
     */
    startActionCountDown(time = 2) {
      // 如果有倒计时，清空倒计时
      this.timer && clearTimeout(this.timer)
      // 取出当前动作
      const actions = this.randomNum.split(',')
      // 如果动作执行完成，则不继续进行定时器
      if (this.actionIndex >= actions.length) {
        return
      }
      // 记录当前倒计时
      this.countdown = time
      // 开始倒计时
      this.timer = setTimeout(() => {
        // 如果当前倒计时到1s，则动作索引累加
        if (time === 1) {
          this.actionIndex++
        }
        // 继续倒计时
        this.startActionCountDown(time === 1 ? 2 : 1)
      }, 1000)
    },

    /**
    * 上传视频
    */
    async uploadPhoto() {
      // 如果当前正在录制中
      if (this.status === VIDEO_STATUS.TAKING) {
        return this.$message({
          message: '视频正在录制中，请先停止录制',
          type: 'warning'
        })
      }
      // 标记当前正在上传
      this.isUploading = true
      const data = new FormData()
      const opts = {
        url: postVinUpload,
        headers: {
          'X-Requested-With': 'XMLHttpRequest',
          'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
          'Authorization': `bearer ${getToken()}`
        },
        data: {
          rootPath: '',
          path: ''
        }
      }
      data.append('file', new File([new Blob(this.recordedBlobs, { type: 'video/mp4' })], `${Date.now()}.mp4`))
      data.append('path', opts.data.path)
      data.append('rootPath', opts.data.rootPath)
      opts.data = data
      try {
        const respData = await request(opts)
        this.$emit('upload-success', respData)
        // 关闭弹框
        this.closeDialog()
      } catch (error) {
        console.error('上传错误：', error)
      }
      // 标记上传结束
      this.isUploading = false
    }
  }
}
</script>
<style lang="scss" scoped>
.take-camera {
  position: relative;
  video {
    background-color: #000000;
    display: block;
    width: 432px;
    height: 220px;
    margin: 0 auto;
  }
  .record-video-wrapper {
    position: relative;
    .btn-video {
      color: #ffffff;
      font-size: 48px;
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
    }
  }
  .take-camera-operation {
    align-items: center;
    display: flex;
    justify-content: center;
    margin-top: 16px;
    .operation {
      color: #ffffff;
      cursor: pointer;
      font-size: 20px;
      margin-left: 30px;
      &:first-child {
        margin-left: 0px;
      }
    }
    .btn-record {
      height: 32px;
      padding: 0 16px;

      .imageicon-video-pause {
        background-image: url(../../assets/image/ico_video_pause.png);
        background-size: 100% 100%;
        display: inline-block;
        width: 16px;
        height: 16px;
        margin-right: 2px;
        margin-bottom: -3px;
      }
    }
  }
  .random-num-wrapper {
    .important-tips{
      color: #71747B;
      font-family: PingFangSC-Regular, PingFang SC;
      font-size: 14px;
      font-weight: 400;
      line-height: 20px;
      text-align: center;
      margin: 0px;
    }
    .random-num {
      align-items: center;
      color: #212026;
      display: flex;
      font-size: 18px;
      font-family: PingFangSC-Semibold, PingFang SC;
      font-weight: 600;
      line-height: 26px;
      height: 26px;
      margin: 4px 0 8px;
      justify-content: center;
      i {
        color: #25c343;
        font-size: 20px;
        margin-right: 4px;
      }
      .countdown {
        align-items: center;
        background: #E7EBFF;
        border-radius: 10px;
        color: #2A68FF;
        display: flex;
        font-size: 14px;
        font-family: PingFangSC-Semibold, PingFang SC;
        font-weight: 600;
        width: 20px;
        height: 20px;
        justify-content: center;
        margin-left: 8px;
      }
    }
  }
}
.dialog-footer {
  text-align: right;
}

</style>
