<!--export default class CallState {-->
<!--static STATUS_IDLE = 0;-->
<!--static STATUS_OUTGOING = 1;-->
<!--static STATUS_INCOMING = 2;-->
<!--static STATUS_CONNECTING = 3;-->
<!--static STATUS_CONNECTED = 4;-->
<!--}-->
<template>
  <div
    class="flex-column flex-align-center flex-justify-center voip-container"
    style="width: 100%; height: 100%"
    ref="rootContainer"
  >
    <div v-if="sharedMiscState.isElectron" ref="notClickThroughArea">
      <ElectronWindowsControlButtonView
        style="position: absolute; top: 0; left: 0; width: 100%; height: 30px; background: white"
        :title="'野火会议'"
        :macos="!sharedMiscState.isElectronWindowsOrLinux"
      />
      <ScreenShareControlView v-if="session && session.screenSharing" type="conference" />
      <h1 style="display: none">Voip-Conference 运行在新的window，和主窗口数据是隔离的！！</h1>
    </div>
    <div v-if="endReason !== undefined && endReason === 4" @click="rejoinConference" class="rejoin-container">
      会议断开，点击重新加入
    </div>
    <!-- 头部 -->
    <div class="header" @dblclick="handleScreen">
      <div class="timeTik">调度时长 {{ duration }}</div>
      <div class="item time">{{ currentDatetime }}</div>
      <i class="item icon" :class="isFullscreen ? 'narrow' : 'fullscreen'" @click="handleScreen"></i>
      <i class="item icon close" @click="hangup"></i>
    </div>
    <!-- 音视频区域 -->
    <div
      class="main-slider-container"
      v-bind:style="{ display: session.screenSharing && sharedMiscState.isElectron ? 'none' : 'flex' }"
    >
      <div class="main">
        <header style="background: white; height: 20px; display: none; justify-content: space-between">
          <a href="#">
            <i
              class="icon-ion-information"
              style="padding: 0 10px"
              id="info-icon"
              v-bind:class="{ active: showConferenceSimpleInfoView }"
              @click="showConferenceSimpleInfoView = !showConferenceSimpleInfoView"
            />
          </a>
          <p style="flex: 1"></p>
          <p style="padding-right: 10px">{{ duration }}</p>
          <div>
            <a v-if="!audioOnly" href="#">
              <i
                class="icon-ion-grid"
                style="padding: 0 10px"
                id="grid-icon"
                v-bind:class="{ active: showChooseLayoutView }"
                @click="showChooseLayoutView = !showChooseLayoutView"
                >宫格布局</i
              >
            </a>
            <!--  TODO 条件显示，展示聊天界面，或者参与者列表界面时，才展示-->
            <a href="#" v-if="showSlider">
              <i
                :class="showSlider ? 'icon-ion-arrow-left-b' : 'icon-ion-arrow-right-b'"
                style="padding: 0 10px"
                @click="toggleSliderView"
              ></i>
            </a>
          </div>
        </header>
        <div
          v-if="showConferenceSimpleInfoView"
          v-click-outside="hideConferenceSimpleInfoView"
          style="position: absolute; left: 10px; top: 50px; z-index: 1000"
        >
          <ConferenceSimpleInfoView :session="session" />
        </div>
        <div
          v-if="showChooseLayoutView"
          v-click-outside="hideChooseLayoutView"
          style="position: absolute; right: 10px; top: 50px; z-index: 1000"
        >
          <ChooseConferenceLayoutView :current-layout="computedCurrentLayout" :session="session" />
        </div>
        <div
          style="
            position: absolute;
            left: 10px;
            bottom: 80px;
            width: 300px;
            max-height: 300px;
            overflow: hidden;
            background: transparent;
            z-index: 1000;
          "
        >
          <ConferenceConversationFloatingView :session="session" />
        </div>
        <div class="conference-main-content-container">
          <!--main-->
          <!--video-->
          <div v-if="!audioOnly" style="width: 100%; height: 100%">
            <i
              v-if="computedCurrentLayout === 0 && currentGridPageIndex > 0"
              style="position: absolute; top: 50%; left: 0; color: #c8cacc; z-index: 1000; font-size: 40px; padding: 0 10px"
              class="icon-ion-arrow-left-c"
              @click="prePage"
            ></i>
            <i
              v-if="computedCurrentLayout === 0 && currentGridPageIndex < gridPageCount - 1"
              style="position: absolute; top: 50%; right: 0; color: #c8cacc; z-index: 1000; font-size: 40px; padding: 0 10px"
              class="icon-ion-arrow-right-c"
              @click="nextPage"
            ></i>
            <!--                    宫格布局-->
            <section v-if="computedCurrentLayout === 0" class="content-container grid video">
              <!--participants include self-->
              <ConferenceParticipantVideoView
                v-for="participant in currentPageParticipants"
                :key="participant.uid + '-' + participant._isScreenSharing"
                :participant="participant"
                :session="session"
              >
              </ConferenceParticipantVideoView>
            </section>

            <!--                    演讲者布局-->
            <section v-else class="content-container focus video">
              <div
                :style="{
                  width: hideFocusLayoutParticipantListVideoView ? '100%' : 'calc(100% - 200px)',
                  height: '100%',
                  position: 'relative',
                }"
              >
                <video
                  v-if="
                    computedFocusVideoParticipant &&
                    !computedFocusVideoParticipant._isAudience &&
                    (!computedFocusVideoParticipant._isVideoMuted || computedFocusVideoParticipant._isScreenSharing) &&
                    computedFocusVideoParticipant._stream
                  "
                  v-bind:style="{ objectFit: computedFocusVideoParticipant._isScreenSharing ? 'contain' : 'fit' }"
                  style="width: 100%; height: 100%"
                  :srcObject.prop="
                    computedFocusVideoParticipant._screenShareStream
                      ? computedFocusVideoParticipant._screenShareStream
                      : computedFocusVideoParticipant._stream
                  "
                  :muted="computedFocusVideoParticipant.uid === selfUserInfo.uid"
                  playsInline
                  autoPlay
                />
                <div
                  @click="toggleParticipantListVideoView"
                  style="position: absolute; top: 50%; right: 0; color: #c8cacc; z-index: 1000; font-size: 40px"
                >
                  <i :class="hideFocusLayoutParticipantListVideoView ? 'icon-ion-arrow-left-b' : 'icon-ion-arrow-right-b'"></i>
                </div>
              </div>
              <div v-show="!hideFocusLayoutParticipantListVideoView" class="focus-mode-participant-list-container">
                <!--participants include self-->
                <ConferenceParticipantVideoView
                  v-for="participant in participantUserInfos"
                  :key="participant.uid + '-' + participant._isScreenSharing"
                  :participant="participant"
                  :session="session"
                >
                </ConferenceParticipantVideoView>
              </div>
            </section>
          </div>
          <!--audio-->
          <div v-else style="width: 100%; height: 100%">
            <div style="background: white; height: 50px; display: flex; justify-content: center; align-items: center">
              <div
                style="
                  background: #daeafe;
                  width: 300px;
                  height: 40px;
                  padding: 0 5px;
                  border-radius: 3px;
                  display: flex;
                  flex-direction: column;
                  justify-content: center;
                "
              >
                <p class="single-line">{{ "正在讲话: " + speakingUserName }}</p>
              </div>
            </div>
            <section class="content-container audio">
              <!--participants-->
              <div v-for="participant in participantUserInfos" :key="participant.uid" class="participant-audio-item">
                <video
                  v-if="audioOnly && participant._stream && !participant._isVideoMuted"
                  class="hidden-video"
                  :srcObject.prop="participant._stream"
                  :muted="participant.uid === selfUserInfo.uid"
                  playsInline
                  autoPlay
                />
                <!-- video 标签不能播放没有视频的流 -->
                <audio
                  v-else-if="audioOnly && participant._stream && participant._isVideoMuted"
                  class="hidden-video"
                  :srcObject.prop="participant._stream"
                  :ref="participant.uid + '-audio'"
                  :muted="participant.uid === selfUserInfo.uid"
                  playsInline
                  autoPlay
                />
                <div style="position: relative">
                  <img
                    class="avatar"
                    v-bind:class="{ highlight: participant._volume > 0 }"
                    :src="participant.portrait"
                    :alt="participant"
                  />
                  <i v-if="participant._isHost" class="indicator icon-ion-person" style="background: #fd802e"></i>
                  <i v-if="participant._isAudience" class="indicator icon-ion-ios-mic-off" style="color: red"></i>
                </div>
                <p class="single-line">{{ userName(participant) }}</p>
              </div>
            </section>
          </div>
          <!--actions-->
          <footer>
            <div class="duration-action-container">
              <p v-if="false">{{ duration }}</p>
              <div class="action-container">
                <div class="action">
                  <img
                    v-if="!session.audience && !session.audioMuted"
                    @click="muteAudio"
                    class="action-img"
                    src="@/assets/images/av_conference_audio.png"
                  />
                  <img v-else @click="muteAudio" class="action-img" src="@/assets/images/av_conference_audio_mute.png" />
                  <p>静音</p>
                </div>
                <div class="action" v-if="!session.screenSharing && callType === 'video'">
                  <img
                    v-if="!session.audience && !session.videoMuted"
                    @click="muteVideo"
                    class="action-img"
                    src="@/assets/images/av_conference_video.png"
                  />
                  <img v-else @click="muteVideo" class="action-img" src="@/assets/images/av_conference_video_mute.png" />
                  <p>视频</p>
                </div>
                <div class="action" v-if="false">
                  <img
                    v-if="!session.screenSharing"
                    @click="screenShare"
                    class="action-img"
                    src="@/assets/images/av_conference_screen_sharing.png"
                  />
                  <img
                    v-else
                    @click="screenShare"
                    class="action-img"
                    src="@/assets/images/av_conference_screen_sharing_hover.png"
                  />
                  <p class="single-line">共享屏幕</p>
                </div>
                <div class="action" v-if="false" @click="chat">
                  <i
                    class="icon-ion-ios-chatboxes"
                    style="width: 40px; height: 40px; font-size: 40px; color: black"
                    v-bind:style="{ color: showConversationView ? 'white' : 'black' }"
                  />
                  <p>聊天</p>
                </div>
                <div v-if="selfUserInfo.uid !== conferenceManager.conferenceInfo.owner" class="action">
                  <img
                    v-if="!conferenceManager.isHandUp"
                    @click="handup"
                    class="action-img"
                    src="@/assets/images/av_conference_handup.png"
                  />
                  <img v-else @click="handup" class="action-img" src="@/assets/images/av_conference_handup_hover.png" />
                  <p class="single-line">举手</p>
                </div>
                <div class="action" v-if="!isSingle">
                  <img
                    @click.stop="members"
                    class="action-img"
                    v-bind:style="{ filter: showConferenceManageView ? 'invert(100%)' : 'none' }"
                    src="@/assets/images/av_conference_members.png"
                  />
                  <p>管理</p>
                </div>
                <div class="action">
                  <img @click="hangup" class="action-img" src="@/assets/images/av_conference_end_call.png" />
                  <p>结束</p>
                </div>
              </div>
            </div>
          </footer>
        </div>
      </div>
      <!-- 右侧滑出区域 -->
      <div class="slider" v-if="!isSingle">
        <!-- 管理 -->
        <ConferenceManageView
          v-if="false"
          v-bind:class="{ active: showConferenceManageView }"
          :participants="participantUserInfos"
          :session="session"
          :all-members="allMembers"
        />
        <ConferenceMemberListView
          v-if="showConferenceManageView"
          :participants="participantUserInfos"
          :session="session"
          :all-members="allMembers"
          :member-list="memberList"
        >
        </ConferenceMemberListView>
        <ConversationView
          v-if="showConversationView && sharedMiscState.isElectron"
          class="conversation-view"
          style="height: 100%"
          :title="conferenceManager.conferenceInfo.conferenceTitle"
          :input-options="{ disableScreenShot: true, disableHistory: true, disableVoip: true, disableChannelMenu: true }"
        />
      </div>
    </div>
  </div>
</template>

<script>
import avenginekit from "../../../wfc/av/internal/engine.min";
import CallSessionCallback from "../../../wfc/av/engine/callSessionCallback";
import CallState from "../../../wfc/av/engine/callState";
import ClickOutside from "vue-click-outside";
import localStorageEmitter from "../../../ipc/localStorageEmitter";
import { currentWindow, isElectron } from "@/platform";
import ScreenOrWindowPicker from "../ScreenOrWindowPicker";
import CallEndReason from "../../../wfc/av/engine/callEndReason";
import ScreenShareControlView from "../ScreenShareControlView";
import avenginekitproxy from "../../../wfc/av/engine/avenginekitproxy";
import ElectronWindowsControlButtonView from "../../common/ElectronWindowsControlButtonView";
import store from "@/store.js";
import VideoType from "../../../wfc/av/engine/videoType";
import IpcEventType from "@/wfc/ipcEventType";
import ConferenceParticipantVideoView from "./ConferenceParticipantVideoView";
import ConversationView from "../../main/conversation/ConversationView";
import ConferenceSimpleInfoView from "./ConferenceSimpleInfoView";
import ChooseConferenceLayoutView from "./ChooseConferenceLayoutView";
import ConferenceConversationFloatingView from "./ConferenceConversationFloatingView";
import conferenceManager from "./conferenceManager";
import ConferenceManageView from "./ConferenceManageView";
import ConferenceMemberListView from "@/ui/voip/conference/ConferenceMemberListView.vue";
import wfc from "../../../wfc/client/wfc";
import LocalStorageIpcEventType from "../../../ipc/localStorageIpcEventType";
import UserInfo from "../../../wfc/model/userInfo";
import ConversationType from "../../../wfc/model/conversationType";
import Conversation from "../../../wfc/model/conversation";
import ConversationInfo from "../../../wfc/model/conversationInfo";
import ChatRoomInfo from "../../../wfc/model/chatRoomInfo";
import screenfull from "screenfull";
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
import weekday from "dayjs/plugin/weekday";
dayjs.locale("zh-cn"); // 本地化
dayjs.extend(weekday); // 第一天为周一

export default {
  name: "Conference",
  props: {
    memberList: {
      type: Array,
      default() {
        return [];
      },
    },
    allMembers: {
      type: Array,
      default() {
        return [];
      },
    },
    isSingle: Boolean,
    callType: String,
  },
  data() {
    return {
      voipProxy: avenginekitproxy,
      conferenceLimit: 3600, // 会议时长上限 单位秒 默认一小时（3600秒）
      connectingLimit: 60, // 拨号时长上限 单位秒 默认 1分钟（60秒）
      timer: null,
      timeTik: 0,
      timeTikFormat: "",
      diallingTimer: null,
      diallingTime: 0,
      currentDatetime: dayjs().format("YYYY-MM-DD dddd HH:mm:ss"),
      isFullscreen: false,
      session: null,
      audioOnly: false,
      status: 1,
      selfUserInfo: null,
      participantUserInfos: [],

      startTimestamp: 0,
      currentTimestamp: 0,

      showSlider: true,
      showConferenceManageView: true,
      showConversationView: false,
      sharedMiscState: store.state.misc,
      videoInputDeviceIndex: 0,

      refreshUserInfoInternal: 0,

      endReason: undefined,

      conferenceManager: conferenceManager,

      // -1，默认布局，也就是宫格布局；0, 宫格视图；1，演讲者视图
      currentLayout: -1,

      // 宫格视图
      currentGridPageIndex: 0,
      participantCountPerGridPage: 9,

      // 演讲者视图
      speakingVideoParticipant: null,
      hideFocusLayoutParticipantListVideoView: false,

      showConferenceSimpleInfoView: false,
      showChooseLayoutView: false,
      dialling: false, // 是否拨打电话中
      calling: false, // 是否通话中
    };
  },
  components: {
    ConferenceManageView,
    ConferenceConversationFloatingView,
    ChooseConferenceLayoutView,
    ConferenceSimpleInfoView,
    ConferenceParticipantVideoView,
    ScreenShareControlView,
    ElectronWindowsControlButtonView,
    ConversationView,
    ConferenceMemberListView,
  },
  methods: {
    setTimer() {
      this.timer = setTimeout(() => {
        this.timeTik++;
        this.currentDatetime = dayjs().format("YYYY-MM-DD dddd HH:mm:ss");
        this.setTimer();
      }, 1000);
    },
    /**
     * * 多人会议发起后给每个参会人计时
     */
    setAllMembersTimer() {
      this.allMembersTimer = setInterval(() => {
        this.memberList.forEach((member) => {
          const hasKey = Object.prototype.hasOwnProperty.call(member, "timer");
          const beKickOut = member.beKickOut;
          if (!hasKey || beKickOut || member.state === "CONNECTED") {
            member.timer = 0;
          } else {
            // console.log("现在的人员状态是。。", member.state);
            if (member.timer < this.connectingLimit && member.state === "CONNECTING") {
              member.timer++;
              member.timeout = false;
            } else {
              // console.log("进入了超时判断", member.state);
              if (member.state !== "DISCONNECTED") {
                member.timer = 0;
                member.timeout = true;
                member.state = "DISCONNECTED";
              }
            }
          }
        });
        this.$emit("update:memberList", JSON.parse(JSON.stringify(this.memberList)));
        // console.log("%c memberList", "background: #00cc00; color: #fff; border-radius: 3px;padding:2px 5px", this.memberList);
      }, 1000);
    },
    // 用来解决 iOS 上，不能自动播放问题
    autoPlay() {
      if (isElectron()) {
        return;
      }
      console.log("auto play");
      if (!this.autoPlayInterval) {
        this.autoPlayInterval = setInterval(() => {
          try {
            let videos = document.getElementsByTagName("video");
            let allPlaying = true;
            for (const video of videos) {
              if (video.paused) {
                allPlaying = false;
                break;
              }
            }
            // participantUserInfos 不包含自己
            if (allPlaying && videos.length === this.participantUserInfos.filter((p) => !p._isAudience).length + 1) {
              clearInterval(this.autoPlayInterval);
              this.autoPlayInterval = 0;
              console.log("auto play, allPlaying", videos.length);
              return;
            }

            for (const video of videos) {
              if (video.paused) {
                video.play();
              }
            }
          } catch (e) {
            // do nothing
          }
        }, 100);
      }
    },
    setupSessionCallback() {
      let sessionCallback = new CallSessionCallback();

      sessionCallback.didChangeState = (state) => {
        console.log("didChangeState", state);
        this.status = state;
        if (state === CallState.STATUS_CONNECTED) {
          // 比如没有摄像头，但发起视频通话时，会自动 muteVideo
          this.selfUserInfo._isVideoMuted = this.session.videoMuted;
          if (this.startTimestamp === 0) {
            this.startTimestamp = new Date().getTime();
            this.timer = setInterval(() => {
              this.currentTimestamp = new Date().getTime();
            }, 1000);
          }
        } else if (state === CallState.STATUS_IDLE) {
          if (this.timer) {
            clearInterval(this.timer);
          }
        }
      };

      sessionCallback.onInitial = (session, selfUserInfo) => {
        //this.session.rotateAng = 90;

        selfUserInfo._isHost = session.host === selfUserInfo.uid;
        selfUserInfo._isAudience = session.audience;
        selfUserInfo._isVideoMuted = session.videoMuted;
        selfUserInfo._isAudioMuted = session.audioMuted;
        selfUserInfo._volume = 0;
        // 先添加属性，在赋值，才能 reactive
        this.selfUserInfo = selfUserInfo;
        this.participantUserInfos = [selfUserInfo];

        console.log("oninitial", selfUserInfo._isAudience);
        // pls refer to: https://vuejs.org/v2/guide/reactivity.html
        this.$set(this.selfUserInfo, "_stream", null);
        this.$set(this.selfUserInfo, "_screenShareStream", null);
        this.$set(this.selfUserInfo, "_isScreenSharing", false);
        this.participantUserInfos.forEach((p) => this.$set(p, "_stream", null));

        this.session = session;
        document.title = session.title;

        conferenceManager.getConferenceInfo(session.callId);
      };

      sessionCallback.didCreateLocalVideoTrack = (stream, screenShare) => {
        console.log("didCreateLocalVideoTrack", screenShare);
        if (screenShare) {
          this.selfUserInfo._screenShareStream = stream;
        } else {
          this.selfUserInfo._stream = stream;
          this.selfUserInfo._screenShareStream = null;
          this.selfUserInfo._isVideoMuted = false;
        }
        this.selfUserInfo._isScreenSharing = screenShare;
        this.autoPlay();
      };

      sessionCallback.didRotateLocalVideoTrack = (stream) => {
        console.log("didRotateLocalVideoTrack", stream.getAudioTracks());
        this.selfUserInfo._stream = stream;
        this.selfUserInfo._stream.timestamp = new Date().getTime();
      };

      sessionCallback.didScreenShareEnded = () => {
        console.log("didScreenShareEnded", this.session.videoMuted, this.session.audioMuted);
        if (isElectron()) {
          currentWindow.setIgnoreMouseEvents(false);
        }
        this.selfUserInfo._isScreenSharing = false;
        this.selfUserInfo._isVideoMuted = this.session.videoMuted;
      };

      sessionCallback.didCreateLocalVideoTrackError = () => {
        // TODO
        // 没有摄像头或者麦克风，加入会议时，会回调到此处，自己断会显示自己的头像，其他端会显示黑屏
        // 可以进行相关提示
      };

      sessionCallback.didReceiveRemoteVideoTrack = (userId, stream, screenSharing) => {
        let p;
        console.log("didReceiveRemoteVideoTrack", userId, stream, screenSharing);
        // let index = -1;
        for (let i = 0; i < this.participantUserInfos.length; i++) {
          p = this.participantUserInfos[i];
          if (p.uid === userId && p._isScreenSharing === screenSharing) {
            // index = i;
            p._stream = stream;
            p._stream.timestamp = new Date().getTime();
            break;
          }
        }
        // if (index > -1) {
        //     this.$set(this.participantUserInfos, index, p);
        // }
        this.autoPlay();
      };

      sessionCallback.didRemoveRemoteVideoTrack = (userId) => {
        console.log("didRemoveRemoteVideoTrack", userId);
      };

      /**
       * * 新参与者加入
       * @param userId
       * @param screenSharing
       */
      sessionCallback.didParticipantJoined = (userId, screenSharing) => {
        console.log("didParticipantJoined", userId, screenSharing);
        let userInfo = wfc.getUserInfo(userId);
        let subscriber = this.session.getSubscriber(userId, screenSharing);
        userInfo._stream = subscriber.stream;
        userInfo._isAudience = subscriber.audience;
        userInfo._isHost = this.session.host === userId;
        userInfo._isVideoMuted = subscriber.videoMuted;
        userInfo._isAudioMuted = subscriber.audioMuted;
        userInfo._volume = 0;
        userInfo._isScreenSharing = screenSharing;
        // 动态添加的属性不是 reactive 的，故直接创建个新的对象
        // 其实这个问题很奇怪，只有发起会议，第一次进入该会议的时候，其他端加入，参与者列表会不刷新；重新进入等，都一切正常
        this.participantUserInfos.push(Object.assign(new UserInfo(), userInfo));
        console.log("members ls", this.memberList);
        this.memberList.forEach((x) => {
          if (x.tencentUserId === userId) {
            x.state = "CONNECTED";
          }
        });
        this.$emit("update:memberList", [...this.memberList]);
        this.$emit("participantJoined", userId);
        console.log("joined", userInfo, subscriber.audience, this.participantUserInfos.length);
      };

      sessionCallback.didParticipantLeft = (userId, endReason, screenSharing) => {
        console.log(
          "didParticipantLeft",
          userId,
          endReason,
          screenSharing,
          JSON.stringify(this.participantUserInfos),
          this.participantUserInfos.length
        );
        //this.participantUserInfos = this.participantUserInfos.filter(p => p.uid !== userId && p._isScreenSharing !== screenSharing);
        this.participantUserInfos = this.participantUserInfos.filter((p) => {
          return !(p.uid === userId && p._isScreenSharing === screenSharing);
        });
        const memberList = [...this.memberList];
        memberList.forEach((item) => {
          if (item.tencentUserId === userId) {
            item.state = "DISCONNECTED";
            item.beKickOut = true;
          }
        });
        this.$emit("update:memberList", memberList);
        this.$emit("participantLeft", userId);
        //fixme 上面可能会没有触发重新计算 focusVideoParticipant
        console.log("didParticipantLeft d", userId, endReason, screenSharing, this.participantUserInfos.length);
      };

      sessionCallback.didCallEndWithReason = (reason) => {
        console.log("callEndWithReason", reason);
        conferenceManager.addHistory(
          conferenceManager.conferenceInfo,
          new Date().getTime() - conferenceManager.conferenceInfo.startTime * 1000
        );
        // 可以根据reason，进行一些提示
        // alert('会议已结束');

        this.endReason = reason;
        if (reason === CallEndReason.REASON_MediaError) {
          return;
        }
        if (reason === CallEndReason.RoomNotExist) {
          console.log("join conference failed", reason, this.session);
          let obj = { reason: reason, session: this.session };
          localStorageEmitter.send(LocalStorageIpcEventType.joinConferenceFailed, obj);
        }
        this.session.closeVoipWindow();
        this.session = null;
      };

      sessionCallback.onRequestChangeMode = (audience) => {
        console.log("onRequestChangeMode", audience);
        if (audience) {
          this.session.switchAudience(true);
          return;
        }
        this.$alert({
          content: "主持人邀请你参与互动",
          cancelCallback: () => {
            // do nothing
          },
          confirmCallback: () => {
            this.session.switchAudience(false);
          },
        });
      };

      sessionCallback.didChangeType = (userId, audience, screenSharing) => {
        console.log("didChangeType", userId, audience, screenSharing);
        this.participantUserInfos.forEach((u) => {
          if (u.uid === userId && u._isScreenSharing === screenSharing) {
            u._isAudience = audience;
            if (audience) {
              u._stream = null;
            }
            if (this.speakingVideoParticipant && this.speakingVideoParticipant.uid === u.uid) {
              this.speakingVideoParticipant = null;
            }
          }
        });
      };

      sessionCallback.didReportAudioVolume = (userId, volume) => {
        let userInfo;
        if (userId === this.selfUserInfo.uid) {
          this.selfUserInfo._volume = volume;
          userInfo = this.selfUserInfo;
        } else {
          this.participantUserInfos.forEach((u) => {
            if (u.uid === userId && u._isScreenSharing === false) {
              u._volume = volume;
              userInfo = u;
            }
          });
        }
        if (this.currentLayout === 0) {
          return;
        }

        if (!userInfo) {
          return;
        }

        if (userInfo._isVideoMuted) {
          return;
        }

        if (this.conferenceFocusUser) {
          return this.conferenceFocusUser;
        }

        if (!this.speakingVideoParticipant) {
          this.speakingVideoParticipant = userInfo;
        } else if (userInfo._volume > this.speakingVideoParticipant._volume) {
          this.speakingVideoParticipant = userInfo;
        }
      };

      sessionCallback.didMuteStateChanged = (participants) => {
        console.log("conference", "didMuteStateChanged", participants);
        participants.forEach((p) => {
          // 自己
          if (p === this.selfUserInfo.uid) {
            console.log("conference", "didMuteStateChanged self", this.session.videoMuted);
            this.selfUserInfo._isVideoMuted = this.session.videoMuted;
            return;
          }
          let s = this.session.getSubscriber(p);
          if (!s) {
            return;
          }
          console.log("conference", "didMuteStateChanged", p, s.videoMuted, s.audioMuted);
          this.participantUserInfos.forEach((u) => {
            if (u.uid === p && u._isScreenSharing === false) {
              let subscriber = this.session.getSubscriber(p);
              u._isVideoMuted = subscriber.videoMuted;
              u._isAudioMuted = subscriber.audioMuted;
              if (this.speakingVideoParticipant && this.speakingVideoParticipant.uid === u.uid) {
                this.speakingVideoParticipant = null;
              }
            }
          });
        });
      };

      sessionCallback.didMediaLostPacket = (media, lostPacket, screenSharing) => {
        console.log("didMediaLostPacket", media, lostPacket, screenSharing);
        if (lostPacket > 6) {
          console.log("您的网络不好");
        }
      };

      sessionCallback.didUserMediaLostPacket = (userId, media, lostPacket, uplink, screenSharing) => {
        console.log("didUserMediaLostPacket", userId, media, lostPacket, uplink, screenSharing);
        //如果uplink ture对方网络不好，false您的网络不好
        //接收方丢包超过10为网络不好
        if (lostPacket > 10) {
          if (uplink) {
            let userInfos = this.participantUserInfos.filter((u) => u.uid === userId && u._isScreenSharing === screenSharing);
            if (userInfos && userInfos.length > 0) {
              console.log(userInfos[0].displayName, "网络不好");
            }
          } else {
            console.log("您的网络不好");
          }
        }
      };

      if (isElectron()) {
        avenginekit.setup(sessionCallback);
      } else {
        avenginekit.sessionCallback = sessionCallback;
      }
    },
    handleScreen() {
      if (screenfull.isEnabled) {
        // console.log("screen",this.$refs.container)
        screenfull.toggle(this.$refs.rootContainer);
        this.isFullscreen = !this.isFullscreen;
        if (!this.isFullscreen) {
          this.$refs.rootContainer.style.top = "0";
          this.$refs.rootContainer.style.left = "50%";
          this.$refs.rootContainer.style.transform = "translate(-50%, 0)";
        }
      }
    },

    hangup() {
      if (this.allMembersTimer) {
        clearInterval(this.allMembersTimer);
        this.allMembersTimer = null;
      }
      const memberList = this.memberList.map((x) => {
        return {
          ...x,
          state: "CONNECTING",
          action: "",
          timer: 0,
          timeout: false,
          beKickOut: false,
          hangup: false,
        };
      });
      this.$emit("update:memberList", memberList);
      this.$emit("hangup");
      this.session.leaveConference(true);
      conferenceManager.addHistory(
        conferenceManager.conferenceInfo,
        new Date().getTime() - conferenceManager.conferenceInfo.startTime * 1000
      );
    },

    muteAudio() {
      let enable = this.session.audioMuted ? true : false;
      if (enable && !conferenceManager.isOwner() && !conferenceManager.conferenceInfo.allowSwitchMode) {
        this.requestUnmute(true);
        return;
      }
      this._muteAudio(enable);
    },

    async _muteAudio(enable) {
      let result = await this.session.setAudioEnabled(enable);
      if (!result) {
        return;
      }
      this.selfUserInfo._isAudioMuted = !enable;

      console.log("muteAudio", this.selfUserInfo._isAudioMuted, this.session.audience);
      if (enable) {
        if (this.session.audience) {
          await this.session.switchAudience(false);
          this.selfUserInfo._isAudience = false;
        }
      } else {
        if (this.session.videoMuted && !this.session.audience) {
          await this.session.switchAudience(true);
          this.selfUserInfo._isAudience = true;
        }
      }
    },
    muteVideo() {
      let enable = this.session.videoMuted ? true : false;
      if (enable && !conferenceManager.isOwner() && !conferenceManager.conferenceInfo.allowSwitchMode) {
        this.requestUnmute(false);
        return;
      }
      this._muteVideo(enable);
    },

    async _muteVideo(enable) {
      let result = await this.session.setVideoEnabled(enable);
      if (!result) {
        return;
      }
      this.selfUserInfo._isVideoMuted = !enable;

      console.log("muteVideo result", this.selfUserInfo._isVideoMuted, this.session.audience);
      if (enable) {
        if (this.session.audience) {
          await this.session.switchAudience(false);
          this.selfUserInfo._isAudience = false;
        }
      } else {
        if (this.session.audioMuted && !this.session.audience) {
          await this.session.switchAudience(true);
          this.selfUserInfo._isAudience = true;
        }
      }
    },

    requestUnmute() {
      this.$alert({
        content: "主持人不允许解除静音，您可以向主持人申请解除静音",
        confirmText: "申请",
        cancelCallback: () => {
          // do nothing
        },
        confirmCallback: () => {
          conferenceManager.applyUnmute(false);
        },
      });
    },

    down2voice() {
      this.session.downgrade2Voice();
    },

    members() {
      this.showConferenceManageView = !this.showConferenceManageView;
      this.toggleSliderView();
    },

    chat() {
      if (isElectron()) {
        this.showConversationView = !this.showConversationView;
        this.toggleSliderView();
      } else {
        let conversation = new Conversation(ConversationType.ChatRoom, this.session.callId, 0);
        let chatroomInfo = new ChatRoomInfo();
        chatroomInfo.chatRoomId = this.session.callId;
        chatroomInfo.title = this.session.title;
        conversation._target = chatroomInfo;
        conversation._target._displayName = chatroomInfo.title;
        let conversationInfo = new ConversationInfo();
        conversationInfo.conversation = conversation;
        store.setCurrentConversationInfo(conversationInfo);
        this.$router.replace("/home");
      }
    },

    hideParticipantList() {
      this.showConferenceManageView && (this.showConferenceManageView = false);
      this.toggleSliderView();
    },

    toggleSliderView() {
      if (!this.showSlider) {
        // if (isElectron()) {
        //   let size = currentWindow.getSize();
        //   currentWindow.setSize(size[0] + 350, size[1], false);
        // } else {
        //   window.resizeTo(window.innerWidth + 360, window.outerHeight);
        // }
        this.$refs.rootContainer.style.setProperty("--slider-width", "350px");
      } else {
        // if (isElectron()) {
        //   let size = currentWindow.getSize();
        //   this.$refs.rootContainer.style.setProperty("--slider-width", "0px");
        //   currentWindow.setSize(size[0] - 350, size[1], false);
        // } else {
        this.$refs.rootContainer.style.setProperty("--slider-width", "0px");
        window.resizeTo(window.innerWidth - 350, window.outerHeight);
        // }

        this.showConferenceManageView = false;
        this.showConversationView = false;
      }
      this.showSlider = !this.showSlider;
    },

    async screenShare() {
      // if (true) {
      //     navigator.mediaDevices.enumerateDevices().then(deviceInfos => {
      //         // test input
      //         for (const deviceInfo of deviceInfos) {
      //             if (this.testCount % 2 === 0) {
      //                    //仅仅是为测试了，生成不能这么写死
      //                 if (deviceInfo.label === "外置麦克风 (Built-in)") {
      //                     console.log('audioInput 外置');
      //                     this.session.setAudioInputDeviceId(deviceInfo.deviceId);
      //                     break;
      //                 }
      //             } else {
      //                 if (deviceInfo.label === "MacBook Pro麦克风 (Built-in)") {
      //                     console.log('audioInput 内置');
      //                     this.session.setAudioInputDeviceId(deviceInfo.deviceId);
      //                     break;
      //                 }
      //             }
      //         }
      //
      //         // test output
      //         // for (const deviceInfo of deviceInfos) {
      //         //     if (this.testCount % 2 === 0) {
      //         //         if (deviceInfo.label === "外置耳机 (Built-in)") {
      //         //             console.log('audioOut 外置');
      //         //             this.setAudioOutputDeviceId(deviceInfo.deviceId)
      //         //             break;
      //         //         }
      //         //     } else {
      //         //         if (deviceInfo.label === "MacBook Pro扬声器 (Built-in)") {
      //         //             console.log('audioOutput 内置');
      //         //             this.setAudioOutputDeviceId(deviceInfo.deviceId)
      //         //             break;
      //         //         }
      //         //     }
      //         // }
      //
      //     }).catch(err => {
      //         console.log()
      //     })
      //     this.testCount++;
      //     return;
      // }

      if (this.session.screenSharing) {
        this.session.stopScreenShare();
        console.log("stopScreenShare", this.session.videoMuted, this.session.audioMuted);
        if (this.session.videoMuted && this.session.audioMuted) {
          this.session.switchAudience(true);
        }
        // currentWindow.setIgnoreMouseEvents(false)
      } else {
        if (isElectron()) {
          let beforeClose = (event) => {
            // What a gamble... 50% chance to cancel closing
            if (!event.params) {
              return;
            }
            if (event.params.source) {
              let source = event.params.source;
              let desktopShareOptions = {
                sourceId: source.id,
                minWidth: 1280,
                maxWidth: 1280,
                minHeight: 720,
                maxHeight: 720,
              };

              if (this.session.audience) {
                this.session
                  .switchAudience(false)
                  .then(() => {
                    this.session.startScreenShare(desktopShareOptions);
                  })
                  .catch((err) => {
                    console.error(err);
                  });
              } else {
                this.session.startScreenShare(desktopShareOptions);
              }
              avenginekitproxy.emitToMain(IpcEventType.START_SCREEN_SHARE, {});
            }
          };
          this.$modal.show(
            ScreenOrWindowPicker,
            {},
            {
              width: 800,
              height: 600,
              name: "screen-window-picker-modal",
              clickToClose: false,
            },
            {
              // 'before-open': beforeOpen,
              "before-close": beforeClose,
              // 'closed': closed,
            }
          );
        } else {
          if (this.session.audience) {
            await this.session.switchAudience(false);
          }
          this.session.startScreenShare({
            frameRate: 30,
          });
        }
      }
    },

    // 设置音频输出设备
    setAudioOutputDeviceId(deviceId) {
      let audioEls = this.$el.getElementsByTagName("audio");
      for (const audioEl of audioEls) {
        audioEl.setSinkId(deviceId);
      }
      let videoEls = this.$el.getElementsByTagName("video");
      for (const videoEl of videoEls) {
        videoEl.setSinkId(deviceId);
      }
    },

    handup() {
      conferenceManager.handUp(!conferenceManager.isHandUp);
    },

    userName(user) {
      let name = "";
      if (user.groupAlias) {
        name = user.groupAlias;
      } else if (user.friendAlias) {
        name = user.friendAlias;
      } else if (user.displayName) {
        name = user.displayName;
      } else {
        name = user.name;
      }
      return name;
    },

    timestampFormat(timestamp) {
      timestamp = ~~(timestamp / 1000);
      let str = "";
      let hour = ~~(timestamp / 3600);
      str = hour > 0 ? (hour < 10 ? "0" : "") + hour + ":" : "";
      let min = ~~((timestamp % 3600) / 60);
      str += (min < 10 ? "0" : "") + min + ":";
      let sec = ~~(timestamp % 60);
      str += (sec < 10 ? "0" : "") + sec;
      return str;
    },

    // TODO 删除，可以直接监听用户信息变化了
    refreshUserInfos() {
      let toRefreshUsers = [];
      this.participantUserInfos.forEach((pu) => {
        if (!pu.updateDt) {
          toRefreshUsers.push(pu.uid);
        }
      });

      if (toRefreshUsers.length > 0) {
        console.log("to refreshUsers", toRefreshUsers);
        let userInfos = wfc.getUserInfos(toRefreshUsers, "");
        userInfos.forEach((u) => {
          let index = this.participantUserInfos.findIndex((p) => p.uid === u.uid);
          if (u.updateDt && index > -1) {
            let ou = this.participantUserInfos[index];
            u._stream = ou._stream;
            u._isAudience = ou._isAudience;
            u._isHost = ou._isHost;
            u._isVideoMuted = ou._isVideoMuted;
            u._isAudioMuted = ou._isAudioMuted;
            u._volume = ou._volume;
            // FYI: https://v2.vuejs.org/v2/guide/reactivity#Change-Detection-Caveats
            this.participantUserInfos.splice(index, 1, u);
          }
        });
      }
    },

    rejoinConference() {
      avenginekit.joinConference({
        callId: this.session.callId,
        pin: this.session.pin,
        host: this.session.host,
        tile: this.session.title,
        desc: this.session.desc,
        audioOnly: this.session.audioOnly,
        audience: this.session.audience,
        advance: this.session.advance,
        muteVideo: this.session.videoMuted,
        muteAudio: this.session.audioMuted,
        extra: this.session.extra,
        callExtra: this.session.callExtra,
        selfUserInfo: this.selfUserInfo,
      });
      this.endReason = undefined;
    },

    prePage() {
      this.currentGridPageIndex--;
      if (this.currentGridPageIndex < 0) {
        this.currentGridPageIndex = Math.ceil(this.participantUserInfos.length / this.participantCountPerGridPage) - 1;
      }
    },
    nextPage() {
      if (this.participantUserInfos.length / this.participantCountPerGridPage > this.currentGridPageIndex + 1) {
        this.currentGridPageIndex++;
      } else {
        this.currentGridPageIndex = 0;
      }
    },

    updateCountPerPage(count) {
      this.participantCountPerGridPage = count;
    },

    setCurrentLayout(layout) {
      if (this.currentLayout === layout) {
        return;
      }
      // 演讲者布局
      if (layout === 1) {
        // 演讲者布局，切换为小流，然后焦点用户切换为大流
        this.participantUserInfos.forEach((u) => {
          if (u.uid !== this.selfUserInfo.uid && !u._isAudience && !u._isVideoMuted) {
            this.session.setParticipantVideoType(u.uid, u._isScreenSharing, VideoType.SMALL_STREAM);
          }
        });
      } else {
        //宫格布局， 当前页切换为大流，未显示的，取消订阅，由 currentPageParticipants 副作用触发
        this.currentGridPageIndex = 0;
        conferenceManager.currentFocusUser = null;
      }
      this.currentLayout = layout;
      this.showChooseLayoutView = false;
    },
    toggleParticipantListVideoView() {
      this.hideFocusLayoutParticipantListVideoView = !this.hideFocusLayoutParticipantListVideoView;
    },

    hideConferenceSimpleInfoView(event) {
      if (event.target.id === "info-icon") {
        return;
      }
      this.showConferenceSimpleInfoView = false;
    },

    hideChooseLayoutView(event) {
      if (event.target.id === "grid-icon") {
        return;
      }
      this.showChooseLayoutView = false;
    },
  },
  computed: {
    duration() {
      if (this.currentTimestamp <= 0) {
        return "00:00";
      }
      let escapeMillis = this.currentTimestamp - this.startTimestamp;
      return this.timestampFormat(escapeMillis);
    },

    speakingUserName() {
      let maxVolume = this.selfUserInfo._volume;
      let speakingUserInfo = this.selfUserInfo;
      this.participantUserInfos.forEach((u) => {
        if (u._volume > maxVolume) {
          speakingUserInfo = u;
          maxVolume = u._volume;
        }
      });
      if (!maxVolume) {
        return "";
      }

      return this.userName(speakingUserInfo);
    },

    currentPageParticipants() {
      if (this.currentLayout === 1) {
        return [];
      }

      let focusUser = this.conferenceFocusUser;
      // sort not in place，避免副作用
      let sortedParticipantUserInfos = [...this.participantUserInfos].sort((o1, o2) => {
        if (focusUser) {
          if (o1.uid === focusUser.uid && o1._isScreenSharing === focusUser._isScreenSharing) {
            return -1;
          }
          if (o2.uid === focusUser.uid && o2._isScreenSharing === focusUser._isScreenSharing) {
            return 1;
          }
        }

        if (o1._isAudience && !o2._isAudience) {
          return 1;
        } else if (!o1._isAudience && o2._isAudience) {
          return -1;
        } else if (o1._isAudience && o2._isAudience) {
          return o1.uid.localeCompare(o2.uid);
        } else {
          if (o1._isScreenSharing && !o2._isScreenSharing) {
            return -1;
          }
          if (!o1._isScreenSharing && o2._isScreenSharing) {
            return 1;
          }
          if (!o1._isVideoMuted && o2._isVideoMuted) {
            return -1;
          }
          if (o1._isVideoMuted && !o2._isVideoMuted) {
            return 1;
          }
          return o1.uid.localeCompare(o2.uid);
        }
      });

      let start = this.currentGridPageIndex * this.participantCountPerGridPage;
      let end =
        start + this.participantCountPerGridPage > sortedParticipantUserInfos.length
          ? sortedParticipantUserInfos.length
          : start + this.participantCountPerGridPage;
      // side effect
      // TODO 优化
      // 相邻页切换时，不能理解取消订阅，可能还切换回去，那样的话，就会有一小段时间，不显示视频流
      for (let i = 0; i < sortedParticipantUserInfos.length; i++) {
        let u = sortedParticipantUserInfos[i];
        if (u.uid === this.selfUserInfo.uid || u._isAudience || u._isVideoMuted) {
          continue;
        }
        if (i >= start && i < end) {
          console.log("set video type big", u.uid);
          this.session.setParticipantVideoType(u.uid, u._isScreenSharing, VideoType.BIG_STREAM);
        } else {
          console.log("set video type none", u.uid);
          this.session.setParticipantVideoType(u.uid, u._isScreenSharing, VideoType.NONE);
        }
      }
      // side effect
      return sortedParticipantUserInfos.slice(start, end);
    },

    gridPageCount() {
      return Math.ceil(this.participantUserInfos.length / this.participantCountPerGridPage);
    },

    conferenceFocusUser() {
      let focus = conferenceManager.conferenceInfo.focus;
      if (!focus) {
        return null;
      }
      let focusUser = this.participantUserInfos.find((u) => u.uid === focus && u._isScreenSharing === true);
      if (!focusUser) {
        focusUser = this.participantUserInfos.find((u) => u.uid === focus);
      }
      return focusUser;
    },
    conferenceLocalFocusUser() {
      return conferenceManager.localFocusUser;
    },

    // 以用户手动选择的为准
    computedCurrentLayout() {
      if (this.currentLayout === -1 && this.conferenceFocusUser) {
        return 1;
      }
      return this.currentLayout;
    },

    // TODO 可以缓存到 conferenceManager 里面
    computedFocusVideoParticipant() {
      if (this.currentLayout === 0) {
        console.log("computedSpeakingParticipant null");
        return null;
      }
      let sp;
      if (this.conferenceFocusUser && !this.conferenceFocusUser._isVideoMuted) {
        sp = this.conferenceFocusUser;
      } else if (this.conferenceLocalFocusUser && !this.conferenceLocalFocusUser._isVideoMuted) {
        sp = this.conferenceLocalFocusUser;
      } else if (this.speakingVideoParticipant) {
        sp = this.speakingVideoParticipant;
      } else {
        sp = this.participantUserInfos.find((u) => !u._isAudience && !u._isVideoMuted && u._isScreenSharing === true);
        if (!sp) {
          sp = this.participantUserInfos.find((u) => !u._isAudience && !u._isVideoMuted);
        }
      }

      if (conferenceManager.currentFocusUser) {
        this.session.setParticipantVideoType(
          conferenceManager.currentFocusUser.uid,
          conferenceManager.currentFocusUser._isScreenSharing,
          VideoType.SMALL_STREAM
        );
      }
      if (sp) {
        conferenceManager.currentFocusUser = sp;
        this.session.setParticipantVideoType(
          conferenceManager.currentFocusUser.uid,
          conferenceManager.currentFocusUser._isScreenSharing,
          VideoType.BIG_STREAM
        );
      } else {
        if (this.session.screenSharing) {
          sp = this.selfUserInfo;
        }
      }
      console.log("computedSpeakingParticipant", sp);
      return sp;
    },
  },
  watch: {
    participantUserInfos: {
      deep: true,
      handler() {
        let audioOnly = true;
        // console.log('participantUserInfos', this.session.screenSharing);
        if (this.session.screenSharing) {
          audioOnly = false;
        } else {
          for (let i = 0; i < this.participantUserInfos.length; i++) {
            let u = this.participantUserInfos[i];
            if (!u._isAudience && !u._isVideoMuted) {
              audioOnly = false;
              break;
            }
          }
        }
        this.audioOnly = audioOnly;

        // mute self audio
        let ref = this.$refs[this.selfUserInfo.uid + "-audio"];
        if (ref && ref.length > 0) {
          this.$refs[this.selfUserInfo.uid + "-audio"][0].muted = true;
        }
        if (this.audioOnly) {
          return;
        }
        // 宫格布局
        if (this.currentLayout === 0) {
          let start = this.currentGridPageIndex * this.participantCountPerGridPage;
          let end =
            start + this.participantCountPerGridPage > this.participantUserInfos.length
              ? this.participantUserInfos.length
              : start + this.participantCountPerGridPage;
          let count = end - start;
          let width = "100%";
          let height = "100%";
          if (count <= 1) {
            width = "100%";
            height = "100%";
          } else if (count <= 4) {
            width = "50%";
            height = "45%";
          } else if (count <= 9) {
            width = "33%";
            height = "33%";
          } else {
            // max 16
            width = "25%";
            height = "25%";
          }
          if (this.$refs.rootContainer) {
            this.$refs.rootContainer.style.setProperty("--participant-video-item-width", width);
            this.$refs.rootContainer.style.setProperty("--participant-video-item-height", height);
          }
        }
      },
    },
    currentPageParticipants: {
      deep: true,
      handler() {
        if (this.audioOnly) {
          return;
        }
        if (this.currentLayout === 0) {
          let count = this.currentPageParticipants.length;
          let width = "100%";
          let height = "100%";
          if (count <= 1) {
            width = "100%";
            height = "100%";
          } else if (count <= 4) {
            width = "50%";
            height = "45%";
          } else if (count <= 9) {
            width = "33%";
            height = "33%";
          } else {
            // max 16
            width = "25%";
            height = "25%";
          }
          if (this.$refs.rootContainer) {
            this.$refs.rootContainer.style.setProperty("--participant-video-item-width", width);
            this.$refs.rootContainer.style.setProperty("--participant-video-item-height", height);
          }
        }
      },
    },
    /**
     * * 监听已调度时长，超过限度conferenceLimit（一小时）就自动挂断
     * @param val
     */
    timeTik(val) {
      if (val && val > this.conferenceLimit) {
        this.hangup();
      }
    },
    /**
     * * 监听参会人状态，如果每个人都断联了就自动挂断
     */
    memberList: {
      handler(value) {
        const offlineMembers = [...value].filter((x) => x.state === "DISCONNECTED").length;
        console.log("watching status", this.status);
        console.log("offline members", offlineMembers);
        if (
          offlineMembers >= value.length /* && process.env.VUE_APP_ENV !== "development"*/ &&
          (this.status === CallState.STATUS_CONNECTED || this.status === CallState.STATUS_CONNECTING)
        ) {
          this.hangup();
        }
      },
      deep: true,
      immediate: true,
    },
  },
  directives: {
    ClickOutside,
  },
  created() {
    document.title = "在线会议";
    conferenceManager.setVueInstance(this);
    this.refreshUserInfoInternal = setInterval(() => {
      this.refreshUserInfos();
    }, 3 * 1000);

    this.$eventBus.$on("muteVideo", (mute) => {
      if (this.session.videoMuted !== mute) {
        let enable = this.session.videoMuted ? true : false;
        this._muteVideo(enable);
      }
    });
    this.$eventBus.$on("muteAudio", (mute) => {
      if (this.session.audioMuted !== mute) {
        let enable = this.session.audioMuted ? true : false;
        this._muteAudio(enable);
      }
    });
    this.setTimer();
    // 分别对所有参会人计时
    this.setAllMembersTimer();
  },
  mounted() {
    this.setupSessionCallback();

    if (isElectron()) {
      //
      // this.$on('stop-screen-share', () => {
      //     this.session.stopScreenShare();
      //     this.$forceUpdate();
      // })
      window.addEventListener("mousemove", (event) => {
        if (!this.session || !this.session.screenSharing) {
          return;
        }
        if (event.target.id === "main-content-container") {
          currentWindow.setIgnoreMouseEvents(true, { forward: true });
        } else {
          currentWindow.setIgnoreMouseEvents(false);
        }
      });
      window.addEventListener("mouseleave", () => {
        currentWindow.setIgnoreMouseEvents(false);
      });
      this.$refs.rootContainer.style.setProperty("--conference-container-margin-top", "30px");
    } else {
      this.$refs.rootContainer.style.setProperty("--conference-container-margin-top", "0px");
    }
  },
  destroyed() {
    // reset
    this.$set(this.selfUserInfo, "_stream", null);
    this.participantUserInfos.forEach((m) => this.$set(m, "_stream", null));
    clearInterval(this.refreshUserInfoInternal);
    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }
    this.$eventBus.$off("muteVideo");
    this.$eventBus.$off("muteAudio");
    // this.conferenceManager.destroy(); // 换成定制的参会人选择了。这个没用了反而会报错。
  },
};
</script>

<style lang="scss" scoped>
.voip-container {
  background: #00000000 !important;
  position: relative;
  --conference-container-margin-top: 41px;
  --slider-width: 350px;
  --main-width: 100%;
}

a {
  color: gray;
}

i:hover {
  color: #1f64e4;
}

i.active {
  color: #3f64e4;
}

.header {
  width: 100%;
  height: 41px;
  background: #0e70ff;
  border-radius: 4px 4px 0px 0px;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;
  padding: 0 16px;
  position: relative;
  .item {
    margin-left: 16px;
  }
  .time {
    font-size: 14px;
    font-weight: 400;
    color: #ffffff;
  }
  .timeTik {
    position: absolute;
    top: 50%;
    left: 20px;
    transform: translate(0, -50%);
    font-size: 14px;
    font-weight: 400;
    color: #ffffff;
  }
  .icon {
    display: block;
    width: 16px;
    height: 16px;
    cursor: pointer;
    &.fullscreen {
      background: url("~@/assets/images/conversation/全屏.png") no-repeat;
      background-size: 100% 100%;
    }
    &.narrow {
      background: url("~@/assets/images/conversation/全屏.png") no-repeat;
      background-size: 100% 100%;
    }
    &.close {
      background: url("~@/assets/images/conversation/关闭.png") no-repeat;
      background-size: 100% 100%;
    }
  }
}

.main-slider-container {
  width: 100%;
  //margin-top: var(--conference-container-margin-top);
  height: calc(100% - var(--conference-container-margin-top));
  display: flex;
  flex-direction: row;
}

.main-slider-container .main {
  width: 100%;
  height: 100%;
  &-showSlider {
    width: calc(100% - var(--slider-width));
  }
}

.main-slider-container .slider {
  width: var(--slider-width);
  height: 100%;
  overflow: auto;
  background: white;
}

.conference-main-content-container {
  width: 100%;
  height: 100%;
  position: relative;
  /*flex: 1;*/
  /*flex-direction: column;*/
  /*justify-content: space-between;*/
  /*align-items: center;*/
}

.content-container {
  width: 100%;
  height: 100%;
  position: relative;
  display: flex;
  flex: 1;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  align-content: center;
}

.main-slider-container .grid {
  flex-direction: row;
}

.main-slider-container .focus {
  --participant-video-item-width: 200px;
  --participant-video-item-height: 100px;
  flex-direction: column;
}

.content-container.video {
  background: black;
  object-fit: contain;
}

.focus-mode-participant-list-container {
  /*position: absolute;*/
  /*top: 0;*/
  /*right: 0;*/
  width: 200px;
  height: 100%;
  overflow: auto;
}

.content-container.audio {
  background: white;
  height: calc(100% - 50px);
  overflow: auto;
  padding: 10px 0 50px 0;
}

.participant-audio-item {
  display: flex;
  flex-direction: column;
  padding: 20px 40px;
  justify-content: center;
  align-items: center;
}

.hidden-video {
  height: 0;
}

.participant-audio-item .indicator {
  width: 18px;
  height: 18px;
  position: absolute;
  left: 50%;
  color: white;
  text-align: center;
  vertical-align: center;
  border-radius: 9px;
  bottom: 0;
  background: #d6d6d6;
  transform: translateX(-50%) translateY(25%);
}

.participant-audio-item p {
  padding-top: 8px;
}

.conference-main-content-container:hover footer {
  width: 100%;
  display: block;
  position: absolute;
  left: 0;
  bottom: 0;
  background: gray;
}

footer {
  /*height: 100px;*/
  display: none;
}

.duration-action-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.duration-action-container p {
  color: white;
  padding: 0 5px 0 0;
}

.action-container {
  /*width: 100%;*/
  display: flex;
  justify-content: center;
}

.action-container .action {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  font-size: 12px;
  color: white;
  padding: 0 25px 0 25px;
}

.avatar {
  width: 90px;
  height: 90px;
  border-radius: 45px;
}

.avatar.highlight {
  border: 2px solid #1fca6a;
}

.action-img {
  width: 40px;
  height: 40px;
}

.video.me {
  -webkit-transform: scaleX(-1);
  transform: scaleX(-1);
}

.rejoin-container {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: #e0e0e0e0;
  text-align: center;
  justify-content: center;
  color: red;
}

.icon-ion-grid:after {
  padding-left: 5px;
  content: "\f13f";
}
</style>
