<template>
  <div class="row" style="margin: 0;">
    <div class="col-md-12 text-center" style="padding:8px;">
      <div style="font-size: 30px;">
        <span>指定动作检测</span>
        <span v-if="showIframe">({{ timeCount }})</span>
      </div>
      <div
        v-if="showIframe"
        class="text-center"
        style="color: red; font-size: 16px;"
      >
        (注意：请点击下方“开始比对”按钮并在60秒内完成指定动作检测,超时将退出考试)
      </div>
    </div>
    <div
      id="faceIdDiv"
      style="position: relative;
						 height:710px; background-color: #6e6f72 !important;
  					 background-image: radial-gradient(circle at 50% 0,#a9a9a9,#34363c);"
    >
      <div
        v-show="!showIframe"
        width="100%"
        height="200px"
        style="text-align: center;line-height:100px;margin-top:5px;"
      >
        <div style="color:white;font-weight: bold;font-size:20px;">
          {{ redoBtnMsg }}
        </div>
        <button
          v-if="redoBtnShow"
          type="button"
          class="qm-primary-button"
          :disabled="redoBtnDisabled"
          @click="startFaceVerify"
        >
          重试
        </button>
      </div>
      <webview
        v-show="showIframe"
        id="myFrame"
        :preload="electronDir + 'manipulateFaceID.js'"
        style="position:absolute;width:100%;height:710px;"
      ></webview>
      <!-- <iframe
        src="https://www.baidu.com"
      preload="./manipulateFaceID.js"
      allow="camera *"
      src="https://www.baidu.com"
      allowusermedia
      id="myFrame"
      scrolling="no"
      width="100%"
      height="710px"
      frameborder="0"
    ></iframe> -->
      <!-- <iframe
        v-show="showIframe"
        allow="camera *"
        allowusermedia
        id="myFrame"
        scrolling="no"
        width="100%"
        height="710px"
        frameborder="0"
      ></iframe> -->
    </div>
  </div>
</template>

<script>
import {
  FACEID_LINENESS_URL,
  VUE_APP_WK_SERVER_SOCKET,
} from "@/constants/constants.js";

export default {
  name: "FaceId",
  data() {
    return {
      showIframe: false,
      redoBtnShow: false,
      timeCount: 60,
      redoBtnMsg: "",
      electronDir: "",
    };
  },
  mounted() {
    this.examRecordId = this.$route.params.examRecordDataId;
    console.debug("startFaceVerify");
    window._hmt.push(["_trackEvent", "活体检测弹出框", "弹出框"]);
    this.logger({
      page: "活体检测弹出框",
      action: "弹出框",
    });
    // open websocket
    this.openWebsocketToStartFaceIDTest();

    this.startFaceVerify();
    if (typeof nodeRequire != "undefined") {
      // console.log(nodeRequire("electron").remote.app);
      const app = window.nodeRequire("electron").remote.app;
      this.electronDir = "file://" + app.getAppPath() + "/";
      console.log(this.electronDir);
    }
  },
  beforeDestroy() {
    this.logger({
      page: "活体检测弹出框",
      action: "关闭框",
    });
    try {
      if (this.ws && this.ws.readyState === 1) this.ws.close();
    } catch (e) {
      console.log("关闭ws异常。");
    }
    clearTimeout(this.faceIdTimeout);
    clearInterval(this.timeCountInterval);
    clearTimeout(this.iframeDomReadyTimeout);
  },
  methods: {
    showRedo(redoMsg) {
      this.showIframe = false;
      this.redoBtnDisabled = false;
      this.redoBtnShow = true;
      if (redoMsg) {
        this.redoBtnMsg = redoMsg;
      } else {
        this.redoBtnMsg = "系统繁忙，请手动点击重试";
        this.logger({
          page: "活体检测弹出框",
          action: "系统繁忙，请手动点击重试",
        });
      }
    },
    updateFaceVerifyMsg(errorMsg) {
      this.$http.get(
        "/api/ecs_oe_student/examFaceLivenessVerify/updateFaceLivenessVerify/" +
          this.$route.params.examRecordDataId,
        { params: { errorMsg: errorMsg } }
      );
    },
    async faceidLoadedButTimeouted() {
      this.closeWS();
      let shouldTimeout = true;

      if (!this.haveReceivedMsg) {
        await this.$http
          .get(
            "/api/ecs_oe_student/examFaceLivenessVerify/getFaceVerifyResult/" +
              this.faceVerifyId
          )
          .then(response => {
            window._hmt.push([
              "_trackEvent",
              "活体检测弹出框",
              "60秒超时非websocket处理结果",
            ]);
            this.logger({
              page: "活体检测弹出框",
              action: "60秒超时非websocket处理结果",
            });
            var receivedMsg = response.data;
            shouldTimeout = false;
            this.faceTestEnd(receivedMsg);
            clearTimeout(this.faceIdTimeout);
            clearInterval(this.timeCountInterval);
            this.$emit("closeFaceId");
            this.closeWS();
          })
          .catch(() => {
            shouldTimeout = true;
            window._hmt.push([
              "_trackEvent",
              "活体检测弹出框",
              "60秒超时非websocket处理结果--api失败",
            ]);
            this.logger({
              page: "活体检测弹出框",
              action: "60秒超时非websocket处理结果--api失败",
            });
          });
      }
      if (!shouldTimeout) return;

      let faceTestEndCalled = false;
      this.$http
        .get(
          "/api/ecs_oe_student/examFaceLivenessVerify/faceLivenessVerifyTimeOut/" +
            this.examRecordId
        )
        .then(response => {
          if (response.status == 200) {
            var receivedMsg = response.data;
            faceTestEndCalled = true;
            return this.faceTestEnd(receivedMsg);
          }
        })
        .finally(() => {
          // Chrome 63开始支持。但是vue-cli引入了p-finally，所以已经加在Promise对象中了
          clearInterval(this.timeCountInterval);
          if (!faceTestEndCalled) {
            this.$Message.error({
              content: "指定动作检测超时，系统退出，请重新登录",
              duration: 30,
              closable: true,
            });
            this.logger({
              page: "活体检测弹出框",
              action: "指定动作检测超时，系统退出，请重新登录",
            });
          }
          this.logout(
            "?LogoutReason=" +
              (faceTestEndCalled
                ? "活体检测超时"
                : "活体检测超时-faceLivenessVerifyTimeOut-调用失败")
          );
        });
    },
    openWebsocketToStartFaceIDTest() {
      this.closeWS();
      this.ws = new WebSocket(
        `${VUE_APP_WK_SERVER_SOCKET}?key=${this.$store.state.user.key}&token=${this.$store.state.user.token}`
      );
      this.ws.onopen = () => {
        window._hmt.push([
          "_trackEvent",
          "活体检测弹出框",
          "websocket的打开成功",
        ]);
        console.log("websocket已连接");
        this.logger({
          page: "活体检测弹出框",
          action: "websocket已连接",
        });
      };
      this.ws.onmessage = response => {
        console.log("faceid websocket response: ", response);
        if (response.data.indexOf("verifyResult") > -1) {
          this.haveReceivedMsg = true;
          var receivedMsgData = JSON.parse(response.data);
          var receivedMsg = JSON.parse(receivedMsgData.content.returnMsgJson);
          // 两个结束点。第二个结束点：从websocket得到消息。
          this.faceTestEnd(receivedMsg);
          clearTimeout(this.faceIdTimeout);
          clearInterval(this.timeCountInterval);
          this.$emit("closeFaceId");
          this.closeWS();

          window._hmt.push([
            "_trackEvent",
            "活体检测弹出框",
            "websocket得到消息",
          ]);
          this.logger({
            page: "活体检测弹出框",
            action: "websocket得到消息",
          });
        }
      };

      // 重复关闭不会报错
      this.ws.onclose = () => {
        console.log("websocket连接已关闭...");
        window._hmt.push([
          "_trackEvent",
          "活体检测弹出框",
          "websocket连接关闭",
        ]);
        this.logger({
          page: "活体检测弹出框",
          action: "websocket连接关闭",
        });
      };
      this.ws.onerror = () => {
        window._hmt.push([
          "_trackEvent",
          "活体检测弹出框",
          "websocket连接失败",
        ]);
        this.logger({
          page: "活体检测弹出框",
          action: "websocket连接失败",
        });
        this.wsReconnectCount = this.wsReconnectCount || 0;
        this.wsReconnectCount++;
        if (!this.haveReceivedMsg && this.wsReconnectCount < 10) {
          this.openWebsocketToStartFaceIDTest();
          window._hmt.push([
            "_trackEvent",
            "活体检测弹出框",
            "websocket重新连接" + this.wsReconnectCount,
          ]);
          this.logger({
            page: "活体检测弹出框",
            action: "websocket重新连接",
            detail: this.wsReconnectCount,
          });
        }
        // this.showRedo("websocket连接异常，请手动点击重试");
      };
    },
    closeWS() {
      try {
        if (this.ws && this.ws.readyState === 1) this.ws.close();
      } catch (e) {
        console.log("关闭ws异常。");
        window._hmt.push([
          "_trackEvent",
          "活体检测弹出框",
          "关闭websocket异常",
        ]);
        this.logger({
          page: "活体检测弹出框",
          action: "关闭websocket异常",
        });
      }
    },
    /**
     * 指定动作检测结束
     */
    faceTestEnd(receivedMsg) {
      this.logger({
        page: "活体检测弹出框",
        action: "指定动作检测结束",
        verifyCount: receivedMsg.verifyCount,
        verifyResult: receivedMsg.verifyResult,
      });
      if (receivedMsg.verifyCount == 1) {
        if (receivedMsg.verifyResult == "TIME_OUT") {
          this.$Message.error({
            content: "第一次指定动作检测超时，检测失败，系统退出，请重新登录",
            duration: 30,
            closable: true,
          });
          this.logout(
            "?LogoutReason=第一次活体检测超时，检测失败，系统退出，请重新登录"
          );
        } else if (receivedMsg.verifyResult == "VERIFY_FAILED") {
          this.$Message.error({
            content: "第一次指定动作检测失败，系统退出，请重新登录",
            duration: 30,
            closable: true,
          });
          this.logout("?LogoutReason=第一次活体检测失败，系统退出，请重新登录");
        } else if (receivedMsg.verifyResult == "NOT_ONESELF") {
          this.$Message.error({
            content: "指定动作检测不合格，结束考试",
            duration: 30,
            closable: true,
          });
          return this.faceTestUploadResult("FAILED");
        } else if (receivedMsg.verifyResult == "VERIFY_SUCCESS") {
          this.$Message.info({
            content: "指定动作检测成功，请继续完成考试",
            duration: 15,
            closable: true,
          });
          return this.faceTestUploadResult("SUCCESS");
        } else if (receivedMsg.verifyResult == "UNKNOWN") {
          this.$Message.error({
            content: "第一次指定动作检测异常(fid)，系统退出，请重新登录",
            duration: 30,
            closable: true,
          });
          this.logout(
            "?LogoutReason=第一次活体检测失败(faceid)，系统退出，请重新登录"
          );
        }
      } else if (receivedMsg.verifyCount >= 2) {
        if (receivedMsg.verifyResult == "VERIFY_SUCCESS") {
          this.$Message.info({
            content: "指定动作检测成功，请继续完成考试",
            duration: 15,
            closable: true,
          });
          return this.faceTestUploadResult("SUCCESS");
        } else {
          this.$Message.error({
            content: "指定动作检测不合格，结束考试",
            duration: 30,
            closable: true,
          });
          return this.faceTestUploadResult("FAILED");
        }
      }
    },
    /**
     * 指定动作检测结果返回后台处理
     */
    faceTestUploadResult(result) {
      return this.$http
        .get(
          "/api/ecs_oe_student/examFaceLivenessVerify/faceLivenessVerifyEnd/" +
            this.examRecordId +
            "?result=" +
            result
        )
        .then(() => {
          if (result != "SUCCESS") {
            this.logout("?LogoutReason=活体检测失败");
            window._hmt.push(["_trackEvent", "活体检测弹出框", "活体检测失败"]);
            this.logger({
              page: "活体检测弹出框",
              action: "活体检测失败",
            });
          } else {
            window._hmt.push(["_trackEvent", "活体检测弹出框", "活体检测成功"]);
            this.logger({
              page: "活体检测弹出框",
              action: "活体检测成功",
            });
          }
        })
        .catch(() => {
          this.$Message.error({
            content: "上传指定动作检测结果出错！",
            duration: 15,
            closable: true,
          });
          window._hmt.push([
            "_trackEvent",
            "活体检测弹出框",
            "上传指定动作检测结果出错！",
          ]);
          this.logger({
            page: "活体检测弹出框",
            action: "上传指定动作检测结果出错！",
          });
          console.log("上传指定动作检测结果出错！");
          this.logout("?LogoutReason=上传指定动作检测结果出错！");
        });
    },
    iframeLoadSuccess() {
      window._hmt.push([
        "_trackEvent",
        "活体检测弹出框",
        "FaceID页面iframe加载成功",
      ]);
      this.logger({
        page: "活体检测弹出框",
        action: "FaceID页面iframe加载成功",
      });

      clearInterval(this.timeCountInterval);
      this.timeCount = 60; //指定动作检测倒计时60秒
      this.timeCountInterval = setInterval(() => {
        --this.timeCount;
        if (this.timeCount === 0) {
          clearInterval(this.timeCountInterval);
        }
      }, 1000);

      // 两个结束点。第一个结束点：超时。先传后台，再根据后台信息进行处理。可能ws没有收到处理结果，会通过http接收一遍。
      //定时事件，如果1分钟内未完成指定动作检测，执行内部程序
      clearTimeout(this.faceIdTimeout); // 防止这个函数是否会因为别的原因被调两次
      this.faceIdTimeout = setTimeout(() => {
        this.faceidLoadedButTimeouted();
      }, 60000); //60000
    },
    async startFaceVerify() {
      this.redoBtnDisabled = true;
      this.redoBtnMsg = "正在进入指定动作检测...";

      let response = null;
      if (this.faceVerifyId) {
        try {
          response = await this.$http.get(
            "/api/ecs_oe_student/examFaceLivenessVerify/getFaceVerifyToken/" +
              this.faceVerifyId
          );
        } catch (error) {
          console.log(error);
          this.showRedo("网络异常，请手动点击重试");
          window._hmt.push([
            "_trackEvent",
            "活体检测弹出框",
            "网络异常-getFaceVerifyToken",
          ]);
          this.logger({
            page: "活体检测弹出框",
            action: "网络异常-getFaceVerifyToken",
          });
          return;
        }
      } else {
        try {
          response = await this.$http.get(
            "/api/ecs_oe_student/examFaceLivenessVerify/startFaceVerify/" +
              this.examRecordId
          );
          this.faceVerifyId = response.data.faceVerifyId;
        } catch (error) {
          console.log(error);
          this.$Message.error({
            content: "获取底照token失败，请重新登录！",
            duration: 15,
            closable: true,
          });
          this.logger({
            page: "活体检测弹出框",
            action: "获取底照token失败，请重新登录！",
          });
          this.logout("?LogoutReason=获取底照token失败，请重新登录！");
          return;
        }
      }

      if (!response.data.success) {
        console.log(response.data.errorMsg);
        this.$Message.error({
          content: "您上传的底照不适合做活体检测，请联系老师！",
          duration: 15,
          closable: true,
        });
        this.logger({
          page: "活体检测弹出框",
          action: "您上传的底照不适合做活体检测，请联系老师！",
        });
        this.logout("?LogoutReason=您上传的底照不适合做活体检测，请联系老师！");
        return;
      }

      this.showIframe = true;
      var iframe = document.getElementById("myFrame");
      try {
        iframe.src = FACEID_LINENESS_URL + response.data.faceLivenessToken;
      } catch (err) {
        console.error(err);
      }

      {
        // iframe 状态管理
        clearTimeout(this.iframeDomReadyTimeout);
        let iframeDomReady = false;
        iframe.addEventListener("did-start-loading", () => {
          console.log("loading faceid iframe");
          console.log(iframe);
        });

        iframe.addEventListener("did-fail-load", () => {
          console.log("failed loading faceid iframe", iframe.src);
          clearTimeout(this.iframeDomReadyTimeout);
          if (iframe.src.includes(FACEID_LINENESS_URL)) {
            this.showRedo("网络异常，请手动点击重试");
            window._hmt.push([
              "_trackEvent",
              "活体检测弹出框",
              "网络异常-加载失败",
            ]);
            this.logger({
              page: "活体检测弹出框",
              action: "网络异常-加载失败",
            });
          }
        });

        // 网络异常，最后的管理
        this.iframeDomReadyTimeout = setTimeout(() => {
          if (iframeDomReady === false) {
            clearTimeout(this.iframeDomReadyTimeout);
            this.showRedo("网络异常，请手动点击重试");
            window._hmt.push(["_trackEvent", "活体检测弹出框", "网络异常"]);
            this.logger({
              page: "活体检测弹出框",
              action: "网络异常",
            });
          }
        }, 30 * 1000);

        iframe.addEventListener("dom-ready", () => {
          iframeDomReady = true;
          console.log("faceid iframe dom ready");
          iframe.insertCSS(".copyright { display: none !important;}");
          // iframe.openDevTools();
        });

        iframe.addEventListener("ipc-message", event => {
          // console.log(event.channel);
          clearTimeout(this.iframeDomReadyTimeout);
          const iframeLoadMsg = event.channel;
          if (iframeLoadMsg.indexOf("error_message") > -1) {
            this.showRedo();
            this.updateFaceVerifyMsg(iframeLoadMsg);
          }
          if (iframeLoadMsg === "success") {
            this.iframeLoadSuccess();
            // iframe.send("faceid-footer", ".copyright");
          }
        });
      }
    },
  },
};
</script>
