<template>
  <div></div>
</template>

<script>
import * as faceapi from "face-api.js";
import { FACE_API_MODEL_PATH } from "@/constants/constants";

window.faceapi = faceapi;

const os = (function() {
  const ua = navigator.userAgent.toLowerCase();
  return {
    isWin2K: /windows nt 5.0/.test(ua),
    isXP: /windows nt 5.1/.test(ua),
    isVista: /windows nt 6.0/.test(ua),
    isWin7: /windows nt 6.1/.test(ua),
    isWin8: /windows nt 6.2/.test(ua),
    isWin81: /windows nt 6.3/.test(ua),
    isWin10: /windows nt 10.0/.test(ua),
  };
})();

let __cache4WebglAvailable = null;
function webgl_available() {
  if (__cache4WebglAvailable) return __cache4WebglAvailable;

  var canvas = document.createElement("canvas");
  var gl = canvas.getContext("webgl");
  __cache4WebglAvailable = gl && gl instanceof WebGLRenderingContext;
  return __cache4WebglAvailable;
}

let __cache4TensorFlowWebPackStatus = null;
function tensorFlowWebPackStatus() {
  if (__cache4TensorFlowWebPackStatus) return __cache4TensorFlowWebPackStatus;

  __cache4TensorFlowWebPackStatus = faceapi.tf.ENV.get("WEBGL_PACK");
  return __cache4TensorFlowWebPackStatus;
}

function getCPUModel() {
  if (typeof nodeRequire != "undefined") {
    var os = window.nodeRequire("os");
    const cpus = os.cpus();
    if (cpus.length > 0) {
      return cpus[0].model;
    }
  }
  return "null";
}

// if (os.isWin7) alert("是win7");

// if (os.isWin10) alert("是win10");

function getFaceDetectorOptions() {
  let inputSize = 320;
  if (os.isWin7) {
    inputSize = 256; // 在win7上无bug，速度快，效果较好
  } else if (os.isWin10) {
    inputSize = 320; // 在win10上，效果较好
  }
  window.____hideMe =
    window.____hideMe ||
    new faceapi.TinyFaceDetectorOptions({
      inputSize: inputSize,
      scoreThreshold: 0.5,
    });
  return window.____hideMe;
  // return new faceapi.SsdMobilenetv1Options({ minConfidence: 0.8 });
  // return new faceapi.MtcnnOptions({ minFaceSize: 200, scaleFactor: 0.8 });
}

const detectTimeArray = [];
export default {
  name: "FaceTracking",
  async created() {
    await faceapi.nets.tinyFaceDetector.load(FACE_API_MODEL_PATH);
    // faceapi.nets.faceRecognitionNet.load(modelsPath);
    await faceapi.loadFaceLandmarkModel(FACE_API_MODEL_PATH);
    faceapi.tf.ENV.set("WEBGL_PACK", false);
  },
  async mounted() {
    let trackStarted = false;
    const that = this;

    async function trackHead() {
      const video = document.getElementById("video");
      if (
        video &&
        video.readyState === 4 &&
        faceapi.nets.tinyFaceDetector.params
      ) {
        trackStarted = true;
      } else {
        return;
      }
      console.log("start tracking ... ");
      await that.detectFaces();
    }
    this.trackHeadInterval = setInterval(() => {
      if (trackStarted) {
        clearInterval(this.trackHeadInterval);
      } else {
        trackHead();
      }
    }, 1000);
  },
  beforeDestroy() {
    clearInterval(this.trackHeadInterval);
    clearTimeout(this.warningTimeout);
    clearTimeout(this.detectFacesTimeout);
  },
  methods: {
    async detectFaces() {
      this.singleTimeUsage = this.singleTimeUsage || 0;
      this.multipleTimeUsage = this.multipleTimeUsage || 0;
      if (
        this.singleTimeUsage > 10 * 1000 ||
        this.multipleTimeUsage > 3 * 1000
      ) {
        window._hmt.push([
          "_trackEvent",
          "正在考试页面",
          "关闭实时人脸检测，因为耗时过长",
        ]);
        return;
      }

      const detectStartTime = performance.now();
      const videoEl = document.getElementById("video");
      // this.___vWidth =
      //   this.___vWidth ||
      //   document.getElementById("video-container").clientWidth;

      const options = getFaceDetectorOptions();
      let result;

      try {
        result = await faceapi
          // .detectSingleFace(videoEl, options)
          .detectAllFaces(videoEl, options);
      } catch (e) {
        window._hmt.push(["_trackEvent", "正在考试页面", "实时人脸检测失败"]);
        throw e;
      }
      // console.log(result);

      const detectEndTime = performance.now();
      // console.log("WebGL: ", faceapi.tf.ENV.get("WEBGL_PACK"));
      console.log(
        "WebGL: ",
        webgl_available(),
        " WEBGL_PACK: ",
        tensorFlowWebPackStatus(),
        " single detect time: ",
        detectEndTime - detectStartTime,
        " result: ",
        result.length
      );
      this.singleTimeUsage = detectEndTime - detectStartTime;

      if (detectTimeArray.length < 11) {
        // 仅捕获一部分检测次数
        detectTimeArray.push(detectEndTime - detectStartTime);
      }
      if (detectTimeArray.length === 11) {
        detectTimeArray.shift();
        const avg =
          detectTimeArray.reduce((a, b) => a + b, 0) / detectTimeArray.length;
        const roundAvg = Math.round(avg / 100) * 100;
        window._hmt.push([
          "_trackEvent",
          "正在考试页面",
          "实时人脸检测平均时长",
          roundAvg + "ms",
        ]);
        console.log(detectTimeArray);
        detectTimeArray.push(0, 0); // 避免再次达到push条件和上传条件

        const roundAvg100 = Math.round(avg / 100) * 100;
        const osType = os.isWin7 ? "win7" : os.isWin10 ? "win10" : "other";
        const stats = `webgl: ${webgl_available()}; tf_backend: ${faceapi.tf.getBackend()}; os: ${osType}; cpu: ${getCPUModel()}`;
        window._hmt.push([
          "_trackEvent",
          "正在考试页面",
          "实时人脸检测统计" + roundAvg100 + "ms",
          stats,
        ]);

        this.multipleTimeUsage = roundAvg;
      }
      // init this.showWaringTime
      this.showWaringTime = this.showWaringTime || Date.now();

      if (result.length >= 2 && Date.now() - this.showWaringTime > 20 * 1000) {
        this.showWaringTime = Date.now();
        this.$Message.warning({
          content: "请独立完成考试",
          duration: 5,
          closable: true,
        });
      }

      if (result.length === 0 && Date.now() - this.showWaringTime > 20 * 1000) {
        this.showWaringTime = Date.now();
        this.$Message.warning({
          content: "请调整坐姿，诚信考试",
          duration: 5,
          closable: true,
        });
      }

      if (
        (!result || result.length !== 1) &&
        !videoEl.classList.contains("video-warning")
      ) {
        videoEl.classList.add("video-warning");
        this.warningTimeout = setTimeout(function() {
          videoEl.classList.remove("video-warning");
        }, 3000);
      }

      this.detectFacesTimeout = setTimeout(() => this.detectFaces(), 10 * 1000);
    },
  },
};
</script>

<style>
@keyframes warning-people {
  0% {
    /* border: solid 5px white; */
    box-shadow: 0 0 20px white;
  }
  100% {
    /* border: solid 5px red; */
    box-shadow: 0 0 20px gold;
  }
}

.video-warning {
  animation: warning-people 3s infinite;
}
</style>
