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

<script>
import * as faceapi from "face-api.js";
import { FACE_API_MODEL_PATH } from "@/constants/constants";
import { mapGetters as globalMapGetters } from "vuex";
import { createNamespacedHelpers } from "vuex";
const { mapState } = createNamespacedHelpers("examingHomeModule");
import { isThisMachineOwnByStudent } from "@/utils/util";

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 !== null) 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 !== null)
    return __cache4TensorFlowWebPackStatus;

  try {
    __cache4TensorFlowWebPackStatus = faceapi.tf.ENV.get("WEBGL_PACK");
  } finally {
    if (__cache4TensorFlowWebPackStatus !== true) {
      __cache4TensorFlowWebPackStatus = false;
    }
  }
  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");

let routeId = null;
let __inputSize = 128;
let __isDoingFaceLiveness = false;
let disableFaceTracking = false;

async function detectTest() {
  const inputSizeList = [128, 160, 224, 320, 416, 512, 608];
  const succRate = [0, 0, 0, 0, 0, 0, 0];
  const detectTimes = 6;

  try {
    const detectStartTime = performance.now();
    const videoEl = document.getElementById("video");
    const options = new faceapi.TinyFaceDetectorOptions({
      inputSize: 128,
      scoreThreshold: 0.5,
    });
    const result = await Promise.race([
      faceapi.detectAllFaces(videoEl, options),
      new Promise(resolve => setTimeout(resolve, 10 * 1000)),
    ]);
    const detectEndTime = performance.now();
    if (
      !result ||
      !result.length ||
      detectStartTime - detectEndTime > 2 * 1000
    ) {
      disableFaceTracking = true;
      window._hmt.push([
        "_trackEvent",
        "正在考试页面",
        "启动检测耗时过长：停止实时",
      ]);
      return;
    }
  } catch (error) {
    console.log(error);
    disableFaceTracking = true;
    window._hmt.push(["_trackEvent", "正在考试页面", "启动检测错误：停止实时"]);
    return;
  }

  for (let idx = 0; idx < inputSizeList.length; idx++) {
    for (let n = 0; n < detectTimes; n++) {
      await new Promise(resolve => setTimeout(resolve, 3 * 1000));
      if (__isDoingFaceLiveness) {
        console.log("正在活检，暂停实时人脸");
        await new Promise(resolve => setTimeout(resolve, 120 * 1000));
      }
      const inputSize = inputSizeList[idx];
      const videoEl = document.getElementById("video");
      try {
        const detectStartTime = performance.now();
        const options = new faceapi.TinyFaceDetectorOptions({
          inputSize: inputSize,
          scoreThreshold: 0.5,
        });
        // const result = await faceapi.detectAllFaces(videoEl, options);
        // console.log(result);
        if (!routeId) {
          return;
        }
        const result = await Promise.race([
          faceapi.detectAllFaces(videoEl, options),
          new Promise(resolve => setTimeout(resolve, 10 * 1000)),
        ]);
        const detectEndTime = performance.now();
        if (detectStartTime - detectEndTime > 0.2 * 1000) {
          disableFaceTracking = true;
          window._hmt.push([
            "_trackEvent",
            "正在考试页面",
            "单次检测耗时过长：停止实时",
          ]);
          return;
        }

        if (result && result.length >= 1) {
          console.log(`inputSize: ${inputSize} ${result.length}`);
          succRate[idx]++;
        } else {
          console.log(`inputSize: ${inputSize} 检测失败`);
        }
      } catch (error) {
        console.log(error);
        console.log(`inputSize: ${inputSize} 检测失败-异常`);
      }
    }

    if (succRate[idx] === detectTimes) {
      console.log(`inputSize: ${inputSizeList[idx]} 提前选中`);
      break;
    }
  }

  console.log({ succRate });
  const max = Math.max(...succRate);

  const idx = succRate.indexOf(max);

  __inputSize = inputSizeList[idx];
  window.__inputSize = __inputSize;
  console.log("最好的 inputSize 为: " + __inputSize);
  window._hmt.push([
    "_trackEvent",
    "正在考试页面",
    "最好的 inputSize 为: " + __inputSize,
  ]);

  return __inputSize;
}

function getFaceDetectorOptions() {
  return new faceapi.TinyFaceDetectorOptions({
    inputSize: __inputSize || 128,
    scoreThreshold: 0.5,
  });

  // return new faceapi.SsdMobilenetv1Options({ minConfidence: 0.8 });
  // return new faceapi.MtcnnOptions({ minFaceSize: 200, scaleFactor: 0.8 });
}

const detectTimeArray = [];
export default {
  name: "FaceTracking",
  computed: {
    ...globalMapGetters(["isEpcc"]),
    ...mapState(["isDoingFaceLiveness"]),
  },
  watch: {
    isDoingFaceLiveness: function(val) {
      __isDoingFaceLiveness = val;
    },
  },
  async created() {
    routeId = "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 detectTest();

      await that.detectFaces();
    }
    if (!this.isEpcc) {
      // EPCC 关闭人脸实时检测
      this.trackHeadInterval = setInterval(() => {
        if (trackStarted) {
          clearInterval(this.trackHeadInterval);
        } else {
          trackHead();
        }
      }, 1000);
    }
  },
  beforeDestroy() {
    routeId = null;
    clearInterval(this.trackHeadInterval);
    clearTimeout(this.warningTimeout);
    clearTimeout(this.detectFacesTimeout);
  },
  methods: {
    async detectFaces() {
      this.singleTimeUsage = this.singleTimeUsage || 0;
      this.multipleTimeUsage = this.multipleTimeUsage || 0;
      if (
        disableFaceTracking ||
        this.singleTimeUsage > 10 * 1000 ||
        this.multipleTimeUsage > 0.5 * 1000
      ) {
        console.log("关闭实时人脸检测，因为耗时过长", this.multipleTimeUsage);
        window._hmt.push([
          "_trackEvent",
          "正在考试页面",
          "关闭实时人脸检测，因为耗时过长",
        ]);
        return;
      }
      if (this.isDoingFaceLiveness) {
        console.log("正在活检，暂停实时人脸");
        this.detectFacesTimeout = setTimeout(
          () => this.detectFaces(),
          10 * 1000
        );
        return;
      }

      const videoEl = document.getElementById("video");
      // var canvas = document.createElement("canvas");
      // canvas.width = 133;
      // canvas.height = 100;

      // var context = canvas.getContext("2d");
      // context.drawImage(videoEl, 0, 0, 133, 100);
      const detectStartTime = performance.now();
      // 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 < 6) {
        // 仅捕获一部分检测次数
        detectTimeArray.push(detectEndTime - detectStartTime);
      }
      if (detectTimeArray.length === 6) {
        detectTimeArray.shift();
        const avg =
          detectTimeArray.reduce((a, b) => a + b, 0) / detectTimeArray.length;
        const roundAvg = Math.round(avg / 100) * 100;
        window._hmt.push([
          "_trackEvent",
          "正在考试页面",
          "实时人脸检测平均时长" +
            (isThisMachineOwnByStudent() ? "学生电脑" : "学习中心电脑"),
          roundAvg + "ms",
        ]);
        console.log(detectTimeArray);
        detectTimeArray.push(0, 0); // 避免再次达到push条件和上传条件

        // FIXME: 上线初期停止统计此类信息，过于零散
        // 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,
        });
        this.failTimes = this.failTimes || 1;
        this.failTimes++;
      }

      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(async () => {
        if (this.failTimes > 5) {
          this.$Message.warning({
            content: "请保持正确坐姿，确保脸部在摄像头内，背景无强光。",
            duration: 10,
            closable: true,
          });
          this.failTimes = 1;
          await detectTest();
        }
        if (!routeId) return;
        await this.detectFaces();
      }, 60 * 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>
