<template>
  <div>
    <h2 class="tc">
      <span>{{curTimes|useTime}}</span>
      <strong>/</strong>
      <span>{{media.duration|useTime}}</span>
    </h2>
    <el-row>
      <el-col :lg="12">

        <div>
          <video-player id="myVideo" class="video-player-box" ref="videoPlayer" @pause="onPlayerPause($event)"
            @play="onPlayerStart($event)" @ready="playerReadied" @timeupdate="onPlayerTimeupdate($event)"
            @ended="onPlayerEnded($event)" :globalOptions="{controls:true}" :options="options">
          </video-player>

        </div>

        <div class="tc">
          <p v-if="errMsg" style="font-size: 30px;color: red;"> {{errMsg}}</p>
        </div>

      </el-col>

      <el-col :lg="12">
        <div class="account-tit2">
          <a :class="{'current':required===1}" @click="required=1" style="width: 30%;">必修课程</a>
          <a :class="{'current':required===0}" @click="required=0" style="width: 30%;">选修课程</a>
          <a :class="{'current':required===2}" @click="required=2" style="width: 30%;">讨论区</a>
        </div>


        <el-menu v-if="required==1" style="min-height: 400px;font-size: 10px;">
          <ul class="m-chapter-list">
            <li v-for="(item, index) in chapter.required" :key="index" :class="{'current':item.name==activeChapter}">
              <a href="javascript:void(0)" style="text-decoration: none" @click="goState(item)" class="ng-binding">
                {{parseInt(item.xs/10)}}学时 {{item.name}}</a>

              <div class="sub-list" v-if="item.name==activeChapter">
                <a v-for="(subItem,index)  in list" :key="subItem.id" v-if="subItem.chapterName == activeChapter"
                  @click="goSubState(subItem, index)" :class="{'current':subItem.name==activeName}">
                  <span class="media-process">
                    <el-progress :percentage="subItem.percent" type="circle" :width="16" :height="16"
                      :format="()=>{return ''}" v-if="subItem.percent>=100" color="green"></el-progress>
                    <el-progress :percentage="subItem.percent" type="circle" :width="16" :height="16"
                      :format="()=>{return ''}" v-else-if="subItem.percent>=50" color="cyan"></el-progress>
                    <el-progress :percentage="subItem.percent" type="circle" :width="16" :height="16"
                      :format="()=>{return ''}" v-else></el-progress>
                  </span>
                  <span class="media-name"> {{subItem.name}} </span>
                </a>
              </div>
            </li>
          </ul>
        </el-menu>

        <el-menu v-if="required==0">
          <ul class="m-chapter-list" style="width: 400px;font-size: 10px;">
            <li v-for="(item, index) in chapter.normal" :key="index" :class="{'current':item.name==activeChapter}">
              <a href="javascript:void(0)" style="text-decoration: none" @click="goState(item)"
                class="ng-binding">{{parseInt(item.xs/10)}}学时 {{item.name}}</a>

              <div class="sub-list pt10" v-if="item.name==activeChapter">
                <a v-for="(subItem,index) in list" :key="subItem.id" v-if="subItem.chapterName == activeChapter"
                  @click="goSubState(subItem, index)" :class="{'current':subItem.name==activeName}">
                  <span class="media-process">
                    <el-progress :percentage="subItem.percent" type="circle" :width="16" :height="16"
                      :format="()=>{return ''}" v-if="subItem.percent>=100" color="green"></el-progress>
                    <el-progress :percentage="subItem.percent" type="circle" :width="16" :height="16"
                      :format="()=>{return ''}" v-else-if="subItem.percent>=50" color="cyan"></el-progress>
                    <el-progress :percentage="subItem.percent" type="circle" :width="16" :height="16"
                      :format="()=>{return ''}" v-else></el-progress>
                  </span>
                  <span class="media-name"> {{subItem.name}} </span>
                </a>
                </a>
              </div>
            </li>
          </ul>
          <!-- <el-menu-item v-for="item in chapter.normal" :key="item">{{item}}</el-menu-item> -->
        </el-menu>

        <div v-if="required==2">
          <i-message :media-id="media.mediaId"></i-message>
        </div>

      </el-col>

    </el-row>
    <div class="left-float" v-if="!closeFace" v-drag v-show="identifyFacePass">
      <video ref="video" width="240" height="180" autoplay></video>
      <canvas ref="canvas" v-show="ontakebtn" width="240" height="180"></canvas>
    </div>

    <el-dialog title="人脸认证" center :visible.sync="identifyFace" width="500px" :close-on-click-modal="false">
      <div style="width: 240px;margin: 100px auto;">
        <video ref="video2" width="240" height="180" autoplay></video>
        <p style="margin-top: 20px;">当前照片：</p>
        <canvas ref="canvas" width="240" height="180"></canvas>
        <p v-if="errMsg" style="font-size: 30px;color: red;"> {{errMsg}}</p>
      </div>
    </el-dialog>
  </div>
</template>

<script>
  import {
    httpServer
  } from "@/components/httpServer/httpServer.js";
  import md5 from 'js-md5';
  import {
    videoPlayer
  } from 'vue-video-player';
  import 'video.js/dist/video-js.css'
  import IMessage from '@/components/message/iMessage.vue'
  // import html2canvas from "html2canvas";
  import {
    MessageBox
  } from "element-ui";
  export default {
    name: "Index",
    data() {
      return {
        timer: false,
        required: 1,
        tickNum: 0,
        prevTime: 0,
        isReady: false,
        ontakebtn: false,
        identifyFace: false,
        identifyFacePass: false,
        activeChapter: '',
        activeName: '',
        curTimes: 0,
        errMsg: '',
        errCount: 0,
        onPlay: false,
        media: {},
        options: {
          autoplay: false, // 如果true,浏览器准备好时开始回放。
          muted: false, // 默认情况下将会消除任何音频。
          loop: false, // 导致视频一结束就重新开始。
          preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频（如果浏览器支持）
          language: 'zh-CN',
          aspectRatio: '4:3', // 将播放器置于流畅模式，并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字（例如"16:9"或"4:3"）
          fluid: true, // 当true时，Video.js player将拥有流体大小。换句话说，它将按比例缩放以适应其容器。
          sources: [{
            src: 'https://media.ndjsxh.cn:18443/hls/20402/20402.m3u8',
            type: "application/x-mpegURL"
          }],
          poster: '',
          notSupportedMessage: '无法播放媒体源', // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
          playtimes: ""
        },
        heartbeat: 5,
        maxErrorCount: 20,
        collectBeat: 120,
        closeFace: true,
        chapter: 101,
        list: [],
      }
    },
    components: {
      videoPlayer,
      IMessage
    },
    filters: {
      useTime(val) {
        let timestr = ""
        let hour = parseInt(val / 3600);
        let min = parseInt(val / 60 % 60);
        let sec = parseInt(val % 60);
        if (hour < 10) hour = "0" + hour;
        if (min < 10) min = "0" + min;
        if (sec < 10) sec = "0" + sec;
        return hour + ":" + min + ":" + sec
      }
    },
    beforeDestroy() {
      this.stopTick()
      this.reportErr("play", 'destroy');
    },
    computed: {
      player() {
        return this.$refs.videoPlayer.player
      }
    },
    beforeDestroy() {
      this.destroyTimer()
      this.closeCamera("video")
    },
    created() {
      this.startTick()
      this.startMonitor();
      this.activeChapter = this.media.chapterName;
      this.activeName = this.media.name
    },
    methods: {
      photograph(ref) {
        let identify = (ref == "video2");
        let ctx = this.$refs["canvas"].getContext("2d");
        this.ontakebtn = true
        ctx.drawImage(this.$refs[ref], 0, 0, 240, 180);
        // 转base64格式、图片格式转换、图片质量压缩
        let imgBase64 = this.$refs["canvas"].toDataURL("image/jpeg", 1); // 由字节转换为KB 判断大小
        this.ontakebtn = false
        let str = imgBase64.replace("data:image/jpeg;base64,", "");
        let param = {
          id: this.media.id,
          ref,
          image: str
        }
        httpServer("course.collect", param).then(res => {
          let {
            msg,
            pause
          } = res.data
          this.errMsg = msg || '';
          if (msg) {
            this.errCount++
          } else {
            if (identify) {
              this.identifyPassAndPlay()
            }
            this.errCount = 0;
          }
          if (!identify && this.errCount > this.maxErrorCount) {
            this.doPause();
          }
        })
      },
      destroyTimer() {
        if (this.timer) clearInterval(this.timer);
      },
      goState(item) {
        if (item.name == this.activeChapter) {
          this.activeChapter = ""
        } else {
          this.activeChapter = item.name;
        }
      },
      goSubState(item, index) {
        this.$emit('loadMedia', item, index)
      },
      callCamera(ref) {
        // H5调用电脑摄像头API
        if (this.closeFace) {
          this.identifyFacePass = true;
          return;
        }
        navigator.mediaDevices
          .getUserMedia({
            video: true,
          })
          .then((success) => {
            // 摄像头开启成功
            this.$refs[ref].srcObject = success;
            // 实时拍照效果
            this.$refs[ref].play();
          })
          .catch((error) => {
            this.$message.error(
              "摄像头开启失败，请检查摄像头是否可用！或者打开摄影头"
            );
            console.error("摄像头开启失败，请检查摄像头是否可用！");
          });
      },
      closeCamera(ref) {
        if (!this.$refs[ref]) return;
        if (!this.$refs[ref].srcObject) return;
        let stream = this.$refs[ref].srcObject;
        let tracks = stream.getTracks();
        tracks.forEach((track) => {
          track.stop();
        });
        this.$refs[ref].srcObject = null;
      },
      startTick() {
        let tick = this.tryTick;
        this.destroyTimer();
        this.tickNum = 0;
        this.timer = setTimeout(tick, 1 * 1000);
      },
      stopTick() {
        if (this.timer) clearInterval(this.timer);
      },
      tryTick() {
        let that = this;
        try {
          that.tick()
        } catch (err) {
          that.reportErr("play", '' + err.message)
        }
        this.destroyTimer()
        this.timer = setTimeout(this.tryTick, 1 * 1000);
      },
      playerReadied(audio) {
        let that = this;
        let {
          position,
          duration
        } = this.media
        if (position > 5 && position < duration) {
          setTimeout(() => {
            this.setposition(position)
          }, 2000)
        }
        this.isReady = true
      },
      onPlayerTimeupdate(player) {
        let curTimes = player.cache_.currentTime;
        if (curTimes > 30 && curTimes > this.curTimes + 2) {
          console.log("return", curTimes, this.media.position)
          player.currentTime(this.curTimes);
          return;
        }
        this.curTimes = curTimes
      },
      setposition(position) {
        if (position > this.media.duration) position = this.media.duration;
        let player = this.$refs.videoPlayer.player;
        let res = player.currentTime(position);
        // player.play()
        this.curTimes = position;
        if (this.media.isFinish) return;
        if (this.media.position >= this.media.duration - 2 * this.heartbeat && !this.media.isFinish) {
          this.tick(true)
        }
      },
      onPlayerPause(event) {
        this.reportErr("play", 'pause');
        this.onPlay = false
      },
      onPlayerEnded(event) {
        this.reportErr("play", 'end');
        this.tick(true)
      },
      onClose() {
        this.reportErr("play", 'close')
        this.doPause()
        this.$emit("close")
        this.closeCamera("video")
      },
      doPause() {
        this.onPlay = false
        let myPlayer = this.$refs.videoPlayer.player;
        myPlayer && myPlayer.pause()
      },
      doPlay() {
        this.onPlay = true
        this.startTick();
        if (!this.$refs.videoPlayer || !this.$refs.videoPlayer.player) return;
        // if (!this.dialog) return this.doPause();
        let myPlayer = this.$refs.videoPlayer.player;
        myPlayer && myPlayer.play()
        this.tickNum = 0
      },
      onPlayerStart(player) {
        console.log("onPlayerStart")
        this.onPlay = true
        if (!this.identifyFacePass) {
          this.startIdentify()
        }
        this.reportErr("play", 'start');
        this.startTick();
      },
      startIdentify() {
        if (!this.closeFace) {
          this.identifyFace = true
          this.identifyFacePass = false
          this.closeCamera("video")
          this.callCamera("video2")
        }
        this.startTick()
      },
      identifyPassAndPlay() {
        this.identifyFacePass = true
        this.identifyFace = false;
        this.closeCamera("video2")
        this.callCamera("video")
        this.$message.successMsg("人脸认证通过", 2)
        this.doPlay()
      },
      reportErr(action, msg) {
        httpServer("course.report", {
          action,
          msg
        })
      },
      startMonitor() {
        let that = this
        document.addEventListener("visibilitychange", function() {
          // || document.hidden
          if (document.visibilityState == "hidden") {
            // that.doPause( )
            that.reportErr("play", 'hidden');
          } else {
            that.reportErr("play", 'show');
            // that.doPlay()
          }
        });
      },
      tick(force = false) {
        let media = this.media;
        this.tickNum++
        // 人脸认证期间
        if (!this.closeFace && !this.identifyFacePass) {
          if (this.onPlay) {
            this.doPause()
          }
          // 人脸认证
          if (this.tickNum % 3 == 1) {
            this.photograph("video2");
          }
          return;
        };
        // 未开始
        if (!force && !this.onPlay) {
          return;
        }

        // 已经完成
        if (this.media.isFinish) {
          console.log("finish")
          return;
        }
        // 每5秒一次心跳
        if (this.tickNum % this.heartbeat != 0) {
          return;
        }
        if (this.errCount >= this.maxErrorCount) {
          this.$message.errorMsg("人脸不在摄像头上", 5);
          this.destroyTimer()
          this.$emit("close");
          return;
        }

        let heartBeat = parseInt(this.tickNum / this.heartbeat);
        // 异常 10秒检查
        if (!this.closeFace) {
          if (this.errCount > 0) {
            this.photograph("video")
          } else if (heartBeat % this.collectBeat == 1) {
            this.photograph("video")
          }
        }
        // 主动暂停
        let myPlayer = this.$refs.videoPlayer.player;
        let curTimes = parseInt(myPlayer.currentTime());
        // 后退无心跳
        if (!force) {
          if (curTimes < this.media.position + this.heartbeat) {
            return;
          }
        }

        let isFinish = force ? 1 : 0
        if (curTimes >= media.duration) isFinish = 1;
        //  拉到后面
        if (!isFinish) {
          if (!this.onPlay) return;
        }
        // 强制完成
        let param = {
          id: media.id,
          position: curTimes,
          isFinish
        };
        httpServer("course.tick", param, true).then(res => {
          if (res.code == 200) {
            let {
              skip,
              position,
              pause,
              closed
            } = res.data
            if (pause || closed) {
              this.doPause();
              this.$emit("close")
              if (closed) {
                this.$message.errorMsg("课程关闭学习", 5);
              } else if (pause) {
                this.$message.errorMsg("多处同时播放视频", 5);
              }
              return
            }
            if (!skip) {
              setTimeout(() => {
                this.setposition(position)
              }, 2000);
            };

            Object.assign(param, res.data)
            this.$emit("update", param)
          }
        })
      }
    }
  }
</script>

<style>
  @import url("./index.css");

  .video-js {
    .vjs-control-bar {
      .vjs-icon-custombutton {
        font-family: VideoJS;
        font-weight: normal;
        font-style: normal;
      }

      .vjs-icon-custombutton:before {
        content: "\f108";
        font-size: 1.8em;
        line-height: 1.67;
      }
    }
  }

  .left-float {
    width: 240px;
    height: 180px;
    background-color: #8bbdf5;
    position: fixed;
    transition: bottom ease .9s;
    z-index: 0;
    left: 60px;
    top: 120px;
    text-align: center;
    border-radius: 5px;
  }

  .p-process {
    width: 100%;
    margin: 20px auto;
    height: 30px;
  }

  .media-footer {
    padding: 0px 30px;
    text-align: left;
    line-height: 40px !important;
    bottom: -10px;
  }

  .media-center {
    text-align: center;
    padding: 0px;
  }

  .media-time {
    font-size: 18px;
    vertical-align: center;
  }

  .media-select {
    white-space: nowrap;
    text-align: right;
    line-height: 40px !important;
    float: right;
    margin: 0px !important;
  }

  .bicon {
    font-size: 28px !important;
    padding: 4px !important;
  }

  .media-el-select {
    font-size: 28px !important;
    width: 80px;
    padding: -4px auto !important;
  }

  .media-name {
    width: 300px;
    margin-left: 16px;
    line-height: 24px;
    white-space: nowrap;
    overflow: hidden;
    align-items: center;
    text-overflow: ellipsis;
  }

  .media-process {
    width: 8px;
    height: 8px;
    align-items: center;
  }

  .vjs-tech {
    pointer-events: none;
  }
</style>
