const client = require(path.join(__dirname, 'client.js'))
const record = require(path.join(__dirname, 'record.js'))
// const liteav = require(path.join(__dirname, 'trtc.js'))

let liteav = require(path.join(__dirname, 'trtc_electron_sdk.js'))
 
const wim = require(path.join(__dirname, 'webim.js'))
const control = require(path.join(__dirname, 'control.js'))
const ntp = require(path.join(__dirname, '../', 'ntp.js'))
const log = require('electron-xlog')

module.exports = {
    debugMode: false,
    /** 机构信息 */
    sdkAppId: 0,
    appName: '',
    iconUrl: '',
    token: '',
    /** 课堂信息 */
    classId: 0,
    classInfo: '',
    userId: '',
    userSig: '',
    hbTimer: null,
    /** 状态 */
    slience: false,
    draw: false,
    speakerVolume: 0,
    recordInit: false,
    classExit: false,
    /** 回调 */
    listeners: {},
    /** 内容大屏 */
    curApp: null,
    iframe: null,
    subVideoComponent: null,
    cameraComponent: null,
    /** 视频信息 */
    videoPath: "",
    videoLength: 0,
    videoPos: 0,
    lastVideoPos: -1,
    videoCanvas: null,
    isPlaying: false,
    /** 设备状态 */
    cameraEnable: false,
    micEnable: false,
    shareEnable: false,
    roleEnable: false,  // 是否是上麦角色

    /** 导出接口 */
    setDebugMode: function (isDebugMode) { this.debugMode = isDebugMode; },
    isTeacher: function () { return this.userId == this.classInfo.teacher_id; },
    isAssistant: function() { return this.userId == this.classInfo.assistant_id; },
    getUserInfo: function(userId, callback){
        client.getUserInfo(userId, callback);
    },
    getUserNickName: function (userId, callback) {
        this.getUserInfo(userId, (ret, json) => {
            callback(ret ? json.nickname : userId);
        })
    },    
    setErrorEvent: function (listener) {
        this.listeners.onError = listener;
    },
    setUserEvent: function (listener) {
        this.listeners.onUserEvent = listener;
    },
    setClassEvent: function (listener) {
        this.listeners.onClassEvent = listener;
    },
    setUserVideoData: function (listener) {
        this.listeners.onVideoData = listener;
    },
    setStatisticsListener: function (listener) {
        this.listeners.onStatistics = listener;
    },
    setRecvMsgListener: function (listener) {
        this.listeners.onRecvMsg = listener;
    },
    syncInfo: function (companyId, classId, userId, userToken, callback) {
        client.init(this.debugMode);
        client.getCompanyInfo(companyId, (ret, json) => {
            if (ret) {
                this.sdkAppId = json.sdkappid;
                this.appName = json.name;
                this.iconUrl = json.icon;

                client.getUserSig(userId, userToken, (ret, json) => {
                    if (!ret) {
                        callback(ret, '获取用户信息失败: ' + json.error_code + "|" + json.error_msg);
                        return;
                    }
                    this.userId = userId;
                    this.token = client.getToken();
                    this.userSig = json.user_sig;
                    client.getClassInfo(classId, (ret, json) => {
                        if (!ret) {
                            callback(ret, '获取课堂信息失败: '+ json.error_code + "|" + this.getClientErrorMsg(json));
                            return;
                        }
                        this.classInfo = json;
                        callback(ret, json);
                    })

                    //record.init(this.sdkAppId, userId, json.user_sig);
                });
            } else {
                callback(ret, '获取机构信息失败: ' + json.error_code + "|"+ this.getClientErrorMsg(json));
            }
        })
    },
    load: function (sdkAppId, userId, userSig, classId, classInfo, token) {
        this.sdkAppId = sdkAppId;
        this.userId = userId;
        this.userSig = userSig;

        this.classId = classId;
        this.classInfo = classInfo;

        this.token = token;
    },
    init: function () { 
        liteav.init();
        wim.init((code, msg) => {
            if (code < 0)
                this.onErrorEvent(code, msg);
        });
        this.initData();
    },
    joinClass: function (classId, classToken, callback) {
        var self = this;
        this.log("[TAG-SAAS] joinClass->classId: " + classId + ", classToken: " + classToken);
        liteav.setOnError((code, msg, arg) => {
            this.onErrorEvent(code, msg);
        });
        wim.setMsgRecvListener((groupId, senderId, element, timeStamp) => {
            this.onMsgRecv(groupId, senderId, element, timeStamp);
        });
        wim.login(this.sdkAppId, this.userId, this.userSig, (code, msg) => {
            if (0 != code) {
                this.onErrorEvent(-101, "IM登录失败: " + code + "|" + msg);
                return;
            }
            this.enterIMGroup(this.classInfo.chat_group_id, (ret)=>{!ret && this.onErrorEvent(-105, "加入课堂聊天群组失败");});
            this.enterIMGroup(this.classInfo.cmd_group_id, (ret)=>{
                if (ret){
                    control.init(this.userId, g_data.role);
                    this.sendControlMsg(control.getComCtrlMsg("join"));
                }else{
                    this.onErrorEvent(-105, "加入课堂群组失败");
                }                
            });            
        })
        client.enterClass(classId, classToken, (ret, json) => {
            if (ret) {                    
                this.classId = classId;
                g_data.role = json.role;
                if (this.classInfo.class_type != 'live' || this.isTeacher()){  // 大型直播课非老师端不用进TRTC房间
                    this.enterAVRoom(classId, "", (elapse) => {
                        try{
                            log.info("[TAG-SAAS] joinClassCallback->trtc elapse: "+elapse);
                            callback(json, elapse);
                            liteav.setOnStatistics((upLoss, downLoss, appCpu, systemCpu, rtt, recvBytes, sentBytes) => {
                                if (self.listeners.hasOwnProperty("onStatistics")) {
                                    this.listeners.onStatistics(upLoss, downLoss, appCpu, systemCpu, rtt, recvBytes, sentBytes);
                                }
                            })
                            if ((this.isTeacher()||this.isAssistant()) && !this.recordInit && process.platform!='darwin'){    // 老师或学生进课堂自动初始化录制模块
                                setTimeout(()=>{
                                    record.init(this.sdkAppId, this.userId, this.userSig, (ret, json)=>{
                                        if (ret){
                                            this.recordInit = true;
                                        }
                                    });   
                                }, 500);                                                            
                            }     
                        }catch (err){
                            log.error("[TAG-SAAS] enterClassCallback->Exception:"+err);
                        }                            
                    });
                }else{
                    callback(json, 0);
                }
                
                //this.hbTimer = setInterval((classId) => {
                //    client.heartBeat(classId, (ret, json)=>{
                //        if (ret){
                //            this.classInfo.duration_time = json.duration_time;
                //        } else if (json.error_code == 10216) {
                //            this.onErrorEvent(-102, "Token过期, 请重新进入课堂!!");
                //        } else if (json.error_code == 10240) {
                //            this.onErrorEvent(-103, "课堂出现异常, 请重新进入课堂!!");
                //        }
                //    })                        
                //}, 30000, classId);
            } else {                    
                this.onErrorEvent(-100, "进入课堂失败: " + json.error_code + "|" + this.getClientErrorMsg(json));
            }
        });
    },
    startClass: function (callback) {
        client.startClass(this.classId, (ret, json) => {
            if (ret) {
                this.curApp.start();
                this.sendControlMsg(control.getStartClassMsg());
            }
            callback(ret, json);
        })
    },
    endClass: function (callback) {        
        client.endClass(this.classId, (ret, json) => {
            this.curApp.end();
            this.enableCamera(false, null, false);
            liteav.exitRoom();
            callback(ret, json);
        })
    },
    exitClass: function (callback) {
        if (this.hbTimer){
            clearInterval(this.hbTimer)
            this.hbTimer = null;
        }
        this.sendControlMsg(control.getComCtrlMsg("exit"), ()=>{
            client.quitClass(this.classId, (ret, json) => {
                this.log("[TAG-SAAS] exitClass->classId: " + this.classId);
                callback(ret, json);
            })
        });
        this.enableCamera(false, null, false);
        if (this.classInfo.class_type != 'live' && !this.isTeacher()){    // 大型直播课除老师外不用退房
            liteav.exitRoom();
        }        
        this.classExit = true;
    },
    getQuestionInfo: function(questionId, callback){
        client.queryQuestionInfo(this.classId, questionId, this.userId, callback);
    },
    cancelQuestion: function(questionId){
        client.cancelQuestion(this.classId, questionId, "cancel_question", (ret, json)=>{            
        })
    },
    /** 上报接口 */
    reportEvent: function (event, data = '') {
        client.reportEvent(this.classId, event, data);
    },
    updateStudentCheckin: function (classId, checkId, userid,callback) {
        client.udpateStudentCheckin(classId, checkId, userid,callback);
    },


    /** 控制接口 */
    enableMemberRight: function (objId, right, enable) {
        this.sendControlMsg(control.getSetRightMsg(enable, [right], [objId]));
    },
    slienceAll: function(enable){
        this.sendControlMsg(control.getComCtrlMsg(enable ? "mutemsg_all" : "unmutemsg_all"));
        client.reportEvent(this.classId, enable ? "all_silence" : "del_all_silence", this.userid);
    },
    /** IM接口 */
    sendCustomMessage: function (data, ext, callback=null) {
        wim.sendCustomMessage(this.classInfo.cmd_group_id, data, ext, callback);
    },
    sendTextMessage: function (text, callback=null) {
        if (this.slience) return false;
        wim.sendTextMessage(this.classInfo.chat_group_id, text, callback);
        return true;
    },
    /** 内容大屏 */
    initContentBoard: function (iframe) {
        try {
            var self = this;
            this.iframe = iframe;
            iframe.setAttribute('src', `../web-content/index.html#/${this.sdkAppId}/${this.classId}/${this.userId}/${this.userSig}/${this.token}?t=${Date.now()}`);
        } catch (err) {
            this.err("[TAG-SAAS] initContentBoard->fail: " + err);
        }
    },
    syncContentBoard: function(){
        this.classInfo.ratio = this.iframe.contentWindow.document.documentElement.scrollWidth + ":" + this.iframe.contentWindow.document.documentElement.scrollHeight;
        this.log("[TAG-SAAS] syncContentBoard->onload: " + JSON.stringify(this.classInfo));
        window.curApp = this.curApp = this.iframe.contentWindow.initApp(this.classInfo);
        if ('ing' == this.classInfo.class_status) {
            if (this.isTeacher()){
                this.curApp.start();
            }//else if (this.isAssistant()){
            //    this.curApp.setRole("master");
            //}            
        }
        if (this.classInfo.settings.layout == '2') {
            this.curApp.renderIM();
        }
        ntp.getNetworkTime('time1.cloud.tencent.com', 123, (err, date) => {
            if (null == err) {
                var boardSdk = this.iframe.contentWindow.boardSDK;
                var data = {
                    type: 1008,
                    avsdk_time: liteav.getTickCount(),
                    board_time: boardSdk.getBoardTimestamp(),
                    time_line: date
                }
                this.sendCustomMessage(JSON.stringify(data), 'TXConferenceExt');
                this.log("[TAG-SAAS] initContentBoard->report time: " + JSON.stringify(data));
            } else {
                this.log("[TAG-SAAS] initContentBoard->report time ntp err: " + err);
            }
        })
    },
    initWebRtcFrame: function(iframe, url){
        try{
            this.iframe = iframe;
            iframe.setAttribute('src', url);
            iframe.setAttribute('allow', "autoplay");
            log.info("[TAG-SAAS] initWebRtcFrame->url: "+url);
        }catch(err){
            this.error("[TAG-SAAS] initContentBoard->fail: " + err);
        }
    },
    addSystemMessage: function(timestamp, message){
        if (this.iframe && this.iframe.contentWindow && this.iframe.contentWindow.hasOwnProperty('clientUpdateSysMsg')) {
            this.iframe.contentWindow.clientUpdateSysMsg({
                content: message,
                time: timestamp
            })
        }
    },
    addClassDoc: function (doc) {
        var docUrl = doc.is_transcode ? doc.transcode_result : doc.doc_url;
        log.info("[TAG-SAAS] addClassDoc->url: " + docUrl);
        if (this.iframe && this.iframe.contentWindow) {
            this.log("[TAG-SAAS] addClassDoc->" + JSON.stringify(doc));
            //doc.transcode_result = "https://ppt2h5-1259648581.file.myqcloud.com/g1nai85amr7q4uu8hlmb/index.html";
            this.iframe.contentWindow.addFile(doc);
        }
        client.reportEvent(this.classId, "open_doc", docUrl);
    },
    /** 录制功能 */
    startRecord: function (name, hwnd, dstPath, x, y, width, height) {
        if (!this.recordInit){
            record.init(this.sdkAppId, this.userId, this.userSig, (ret, json)=>{
                if (ret){
                    this.recordInit = true;
                }
                record.startRecord(name, hwnd, dstPath, x, y, width, height, this.classId);
            });            
        }else{
            record.startRecord(name, hwnd, dstPath, x, y, width, height, this.classId);
        }
        //liteav.startRecord(title, 0, 0, 0, 0, 0, recPath);
    },
    setRecordStateListener: function(listener){
        record.setStateListener(listener);
    },
    stopRecord: function () {
        record.stopRecord();
        //liteav.stopRecord();
    },
    startPush: function(name, hwnd, dstUrl, x, y, width, height){
        if (!this.recordInit){
            record.init(this.sdkAppId, this.userId, this.userSig, (ret, json)=>{
                if (ret) this.recordInit = true;
                record.startPush(name, hwnd, dstUrl, x, y, width, height);
            });            
        }else{
            record.startPush(name, hwnd, dstUrl, x, y, width, height);
        }
    },
    stopPush: function(){
        record.stopPush();
    },
    /** 屏幕分享 */
    shareScreen: function () {
        log.info("[TAG-SAAS] shareScreen->enter");
        if (!this.roleEnable){    // 需要上麦
            this.enableRole(true);
        }
        this.shareEnable = true;
        var sources = liteav.getScreenCaptureSources(120, 70, 20, 20);
        liteav.selectScreenCaptureTarget(1, sources[0].sourceId, sources[0].sourceName, 0, 0, 0, 0, false, true);
        liteav.startScreenCapture();
        client.reportEvent(this.classId, "screen_share_open");
        //this.sendTextMessage('开启屏幕分享: ' + calcPlayAddr(true));
    },
    stopShareScreen: function () {
        log.info("[TAG-SAAS] stopShareScreen->enter");
        this.shareEnable = false;
        if (!this.cameraEnable && !this.micEnable && this.roleEnable){   // 需要下麦
            this.enableRole(false);
        }
        liteav.stopScreenCapture();
        client.reportEvent(this.classId, "screen_share_close");        
    },
    shareCrop: function(name, x, y, w, h){
        log.info("[TAG-SAAS] shareCrop->name: "+name+", x:"+x+", y:"+y+", w:"+w+", h:"+h);
        var sources = liteav.getScreenCaptureSources(120, 70, 20, 20);
        var shareWin = sources[0];
        for (var i=0; i<sources.length; i++){
            if (sources[i].sourceName.startsWith(name)){
                shareWin = sources[i];
                log.info("[TAG-SAAS] shareCrop->find window title: "+sources[i].sourceName);
                break;
            }
        }
        liteav.selectScreenCaptureTarget(0, shareWin.sourceId, shareWin.sourceName, x, y, w, h, false, false);
        liteav.startScreenCapture();
    },
    pushCrop: function(name, hwnd, x, y, w, h){
        if (!this.recordInit){
            record.init(this.sdkAppId, this.userId, this.userSig, (ret, json)=>{
                record.startRecord(name, hwnd, dstPath, x, y, width, height);
            });
            this.recordInit = true;
        }else{
            record.startRecord(name, hwnd, dstPath, x, y, width, height);
        }
    },
    /** 播片功能 */
    startPlayVideo: function (videoPath, callback) {
        this.log("[TAG-SAAS] startPlayVideo->path: " + videoPath);
        this.videoPath = videoPath;
        liteav.setVodPlayerStarted((length) => {
            this.isPlaying = true;
            this.videoPos = 0;
            this.videoLength = length;
            client.reportEvent(this.classId, "media_open", videoPath);
            if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('PLAYVIDEO', this.userId, true);
            this.setVideoLength(this.videoPos, this.videoLength);
        })
        liteav.setVodPlayerProgress((pos) => {
            this.videoPos = pos;
            this.setVideoLength(this.videoPos, this.videoLength);
        })
        liteav.setVodPlayerStoped(() => {
            this.isPlaying = false;
            if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('PLAYVIDEO', this.userId, false);
            this.videoPos = this.videoLength = 0;
            this.setVideoLength(this.videoPos, this.videoLength);
        })
        liteav.setVodPlayerError((err) => {
            this.isPlaying = false;
            if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('PLAYVIDEO', this.userId, false);
            this.videoPos = this.videoLength = 0;
            this.setVideoLength(this.videoPos, this.videoLength);
        })
        this.addVideoFrame(path.basename(videoPath));
        liteav.startVodPlay(videoPath, this.videoCanvas, false);
    },
    stopPlayVideo: function () {
        this.log("[TAG-SAAS] stopPlayVideo->enter");
        liteav.stopVodPlay();
        this.reportEvent("media_close");
        if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('PLAYVIDEO', this.userId, false);
    },
    pausePlayVideo: function () {
        this.log("[TAG-SAAS] pausePlayVideo->enter with play status: " + this.isPlaying);
        liteav.pauseVodPlay();
    },
    resumePlayVideo: function () {
        this.log("[TAG-SAAS] resumePlayVideo->enter with play status: " + this.isPlaying);
        if (this.videoLength > 0) {
            liteav.resumeVodPlay();
        } else {
            liteav.startVodPlay(this.videoPath, 0);
            liteav.seekVodPlay(this.videoPos);
        }
    },
    setVideoPos: function (pos) {
        this.videoPos = pos;
        liteav.seekVodPlay(pos);
    },
    /** 渲染相关 */
    setLocalViewMirror: function(mirror){
        liteav.setLocalViewMirror(mirror);
    },
    /** 角色切换 */
    enableRole: function(enable){
        this.roleEnable = enable;
        liteav.changeAnchorRole(enable);
    },
    /** 设备接口 */
    enableCamera: function (enable, view, report = true) {        
        if (enable && this.cameraEnable == enable){
            this.log("[TAG-SAAS] enableCamera->ignore repeat request: "+enable);
            return;
        }
        this.log("[TAG-SAAS] enableCamera->enable: " + enable+", report: "+report);
        if (enable && !this.roleEnable){    // 需要上麦
            this.enableRole(true);
        }else if (!enable && !this.micEnable && !this.shareEnable && this.roleEnable){   // 需要下麦
            this.enableRole(false);
        }
        try{
            if (this.classInfo.class_video_type == 'live'){
                if (enable) this.addCameraFrame(this.userId);
                view = this.cameraComponent.$content;//.getElementsByClassName('camera_div')[0];
            }
        }catch(err){
            this.log("[TAG-SAAS] enableCamera->error: "+err);
        }
        
        liteav.enableCamera(enable, view);
        this.cameraEnable = enable;
    },
    enableMic: function (enable, report = true) {
        if (this.micEnable == enable){
            this.log("[TAG-SAAS] enableMic->ignore repeat request: "+enable);
            return;
        }
        this.log("[TAG-SAAS] enableMic->enable: " + enable);
        if (enable && !this.roleEnable){    // 需要上麦
            this.enableRole(true);
        }else if (!enable && !this.cameraEnable && !this.shareEnable && this.roleEnable){   // 需要下麦
            this.enableRole(false);
        }
        this.micEnable = enable;
        
        liteav.enableMic(enable);
    },
    enableChat: function(enable, report = true){
        this.slience = !enable;
        //client.reportEvent(this.classId, this.slience ? "slience" : "del_slience");
    },
    enableDraw: function(enable, report = true){
        this.draw = enable;
        this.curApp.setRole(enable ? "master" : "student");
        //client.reportEvent(this.classId, this.draw ? "enable_draw" : "disable_draw");
    },
    querySpeakerList: function () {
        var speakers = liteav.getSpeakerDevicesList();
        var curSpeaker = liteav.getCurrentSpeakerDevice();
        this.setCurDevice(speakers, curSpeaker);
        this.log("[TAG-SAAS] querySpeakerList->" + JSON.stringify(speakers));
        return speakers;
    },
    queryMicList: function (enable) {
        var curMic = { deviceName: '<未选择>', id: 0 };
        var mics = liteav.getMicDevicesList();
        if (enable) {
            curMic = liteav.getCurrentMicDevice();
        } else {
            mics.push(curMic);
        }
        this.log("[TAG-SAAS] queryMicList->" + JSON.stringify(mics));
        this.setCurDevice(mics, curMic);
        return mics;
    },
    queryCameraList: function (enable) {
        var curCamera = { deviceName: '<未选择>', id: 0 };
        var cameras = liteav.getCameraDevicesList();
        if (enable) {
            curCamera = liteav.getCurrentCameraDevice();
        } else {
            cameras.push(curCamera);
        }
        this.setCurDevice(cameras, curCamera);
        this.log("[TAG-SAAS] queryCameraList->" + JSON.stringify(cameras));
        return cameras;
    },
    setCurrentSpeaker: function (devId) {
        liteav.setCurrentSpeakerDevice(devId);
    },
    setCurrentMic: function (devId) {
        this.log("[TAG-SAAS] setCurrentMic->name:"+devId);
        liteav.setCurrentMicDevice(devId);
    },
    setCurrentCamera: function (devId) {
        this.log("[TAG-SAAS] setCurrentCamera->name:"+devId);
        liteav.setCurrentCameraDevice(devId);
    },
    startSpeakerTest: function (file) {
        liteav.startSpeakerDeviceTest(file);
    },
    stopSpeakerTest: function () {
        liteav.stopSpeakerDeviceTest();
    },
    getSpeakerVolume: function () {
        return liteav.getCurrentSpeakerVolume();
    },
    setSpeakerVolume: function (volume) {
        liteav.setCurrentSpeakerVolume(volume);
    },
    setBeautyStyle: function (mode, beauty, white, ruddiness) {
        liteav.setBeautyStyle(mode, beauty, white, ruddiness);
    },

    /** 测试 */
    log: function (msg) {
        log.info(msg);
    },
    err: function (msg) {
        log.error(msg);
    },




    /** 内部接口 */
    onErrorEvent: function (code, msg) {
        if (code == -1301 || code == -1314 || code == -1315 || code == -1316) {  // camera error
            this.cameraEnable = false;
        } else if (code == -1302) {
            this.micEnable = false;
        }
        if (this.listeners.hasOwnProperty("onError")) {
            this.listeners.onError(code, msg);
        }
    },
    onMsgRecv: function (groupId, senderId, element, timeStamp) {
        if (this.classExit){
            log.warn("[TAG-SAAS] onMsgRecv->ignored with class exit");
            return;
        }
        try {
            var content = element.getContent();
            if (this.classInfo && groupId == this.classInfo.chat_group_id) {
                if (element.getType() == webim.MSG_ELEMENT_TYPE.TEXT) {
                    log.info('[TAG-SAAS] onMsgRecv->text:' + content.getText() + "/" + senderId + "/" + timeStamp);
                    this.getUserInfo(senderId, (ret, json) => {
                        var nickname = (json.nickname && json.nickname.length>0) ? json.nickname : senderId;
                        var iconUrl = (json.avatar && json.avatar.length>0) ? json.avatar : 'https://main.qcloudimg.com/raw/45e1cd2f9378299a093a6ffa3bb8b9fb.svg'
                        if (this.listeners.hasOwnProperty("onRecvMsg")) {
                            log.info('[TAG-SAAS] onMsgRecv->notify msg');
                            this.listeners.onRecvMsg(senderId, nickname, timeStamp, content.getText(), iconUrl);
                        }                        
                        if (this.iframe && this.iframe.contentWindow && this.iframe.contentWindow.hasOwnProperty('clientUpdateIMmsg')) {
                            this.iframe.contentWindow.clientUpdateIMmsg({
                                isSend: senderId == this.userId,
                                send: senderId,
                                sendNick: nickname,
                                content: content.getText(),
                                time: timeStamp,
                                iconUrl: iconUrl
                            })
                        }
                    })
                }
            } else if (this.classInfo && groupId == this.classInfo.cmd_group_id) {
                if (element.getType() == webim.MSG_ELEMENT_TYPE.CUSTOM) {//自定义消息
                    if (content.getExt() == "TXWhiteBoardExt" && senderId != this.userId) {
                        //log.info("[TAG-SAAS] onMsgNotify->WhiteBoard:" + content.getData() + "/" + senderId);
                        if (this.iframe && this.iframe.contentWindow) {
                            this.iframe.contentWindow.addBoardData(content.getData());
                        }
                    } else if (content.getExt() == "CTRL") {//收到控制类消息
                        log.info("[TAG-SAAS] onMsgNotify->CTRL: " + content.getData() + "/" + senderId);
                        this.processControlMsg(JSON.parse(content.getData()), senderId, timeStamp);
                    } else if (content.getExt() == "CONTAINER") {//收到容器类消息
                        log.info("[TAG-SAAS] onMsgNotify->CONTAINER: " + content.getData() + "/" + senderId);
                        this.curApp.container.notify(content.getData());
                    }
                }
            }
        } catch (err) {
            log.error("[TAG-SAAS] onMsgRecv->error: " + err);
        }
    },
    onVideoData: function (id, type, width, height, timestamp, rotation, data) {
        if (this.listeners.hasOwnProperty("onVideoData")) {
            this.listeners.onVideoData(id, type, width, height, timestamp, rotation, data);
        }
    },
    sendControlMsg: function (msg, callback=null) {
        this.sendCustomMessage(msg, 'CTRL', callback);
    },
    enterIMGroup: function (groupId, callback) {
        this.log("[TAG-SAAS] enterIMGroup->groupId: " + groupId);
        wim.joinGroup(groupId, (errCode, errInfo) => {
            if (errCode == 10015 || errCode == 10010) {
                wim.createGroup(groupId, (errCode, errInfo) => {
                    if (errCode == 0) {
                        if (null != callback) callback(true);
                    } else if (errCode == 10013) {
                        this.log("[TAG-CLASS] enterIMGroup->Already Group Member");
                        if (null != callback) callback(true);
                    } else {
                        this.onErrorEvent(-101, "进入群组失败: " + errCode + ", " + errInfo);
                        if (null != callback) callback(false);
                    }
                })
            } else {
                if (null != callback) callback(0 == errCode || 10013 == errCode);
            }
        })
    },
    enterAVRoom: function (roomId, privateMapKey, callback) {
        var resolution = getResolution(this.classInfo.settings.resolution);
        this.log("[TAG-SAAS] enterAVRoom->roomId: " + roomId + ", " + JSON.stringify(this.classInfo.settings));

        liteav.setOnEnterRoom((elapse) => {
            callback(elapse);
        });
        liteav.setVideoEncoderParam(resolution, this.classInfo.settings.fps, this.classInfo.settings.bitrate);
        liteav.enterRoom(this.sdkAppId, parseInt(roomId), privateMapKey, this.userId, this.userSig);

        liteav.setOnUserEnter((id) => {
            if (id.length < 1) id = this.userId;
            if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('JOIN', id, true);
        });
        liteav.setOnUserExit((id) => {
            if (id.length < 1) id = this.userId;
            if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('JOIN', id, false);
        });
        liteav.setOnUserVideoAvailable((id, enable) => {
            if (id.length < 1) id = this.userId;
            if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('CAMERA', id, enable);
        });
        liteav.setOnUserAudioAvailable((id, enable) => {
            if (id.length < 1) id = this.userId;
            if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('MIC', id, enable);
        });
        liteav.setOnUserSubStreamAvailable((id, enable) => {
            if (id.length < 1) id = this.userId;
            if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('SCREEN', id, enable);
        });
        liteav.setOnFirstVideoFrame((id, streamType, width, height) => {
            if (id.length < 1) id = this.userId;
            if (this.listeners.hasOwnProperty("onUserEvent")) {
                this.listeners.onUserEvent('FIRSTFRAME', id, streamType==2);
            }
        });
        liteav.setVolumeCallback((id, volume) => {
            if (id.length < 1) id = this.userId;
            if (this.listeners.hasOwnProperty("onUserEvent")) this.listeners.onUserEvent('VOLUME', id, volume);
        });
        liteav.setLocalVideoCallback((id, type, width, height, timestamp, rotation, data) => {
            if (id.length < 1) id = this.userId;
            if (this.listeners.hasOwnProperty("onVideoData")) {
                this.listeners.onVideoData(id, type, width, height, timestamp, rotation, data);
            }
        });        
        liteav.setOnDeviceChange((devId, devType, devState) => {
            log.info("[TAG-SAAS] onDeviceChange->devId: "+devId+", devType: "+devType+", devState: "+devState);
            // 注意这是临时逻辑(状态相反) -- start
            process.platform=='darwin' && (devState=1-devState);
            // 注意这是临时逻辑(状态相反) -- end
            if (1 == devState){
                if (2 == devType){  // 摄像头
                    var curCameraDev = liteav.getCurrentCameraDevice();
                    log.info("[TAG-SAAS] onDeviceChange->remove camera: "+devId+"/"+curCameraDev.deviceName);
                    if (curCameraDev.deviceName == devId || curCameraDev.deviceId == devId){
                        this.cameraEnable = false;
                        if (this.listeners.hasOwnProperty("onError")) {
                            this.listeners.onError(-104, "摄像头被移除!");
                        }
                    }
                }else if (0 == devType){    // 麦克风
                    var curMicDev = liteav.getCurrentMicDevice();
                    log.info("[TAG-SAAS] onDeviceChange->remove mic: "+devId+"/"+curMicDev.deviceName);
                    if (curMicDev.deviceName == devId || curMicDev.deviceId == devId){
                        this.enableMic(false);
                        if (this.listeners.hasOwnProperty("onError")) {
                            this.listeners.onError(-105, "麦克风被移除!");
                        }
                    }
                }
            }
        });

        if (process.platform != 'darwin') {
            liteav.enableAudioVolumeEvaluation(500);
        } else {
            liteav.enableAudioVolumeEvaluation(500, 5);
        }
        this.speakerVolume = liteav.getCurrentSpeakerVolume();
        this.log("[TAG-SAAS] enterAVRoom->speakerVolume: " + this.speakerVolume);
    },
    initData: function () {

    },
    setCurDevice: function (list, cur) {
        if (list.length > 1) {
            for (var i = 0; i < list.length; i++) {
                if (list[i].deviceName == cur.deviceName) {
                    var tmp = list[i];
                    list[i] = list[0];
                    list[0] = tmp;
                    return;
                }
            }
        }
    },
    processControlMsg(msg, sender, timeStamp) {
        var req = msg.data;
        if (req.action == "on" || req.action == "off" || req.action == "on_all" || req.action == "off_all") {
            if (this.isTeacher()) return;       // 老师忽略
            var enable = (req.action == "on" || req.action == "on_all");
            if (req.action == "on_all" || req.action == "off_all" || req.objectId.indexOf(this.userId) != -1) {   // 处理发给自己的信令
                if (req.rights.indexOf("camera") != -1) {
                    if (this.listeners.hasOwnProperty("onClassEvent")) {
                        if (enable){
                            this.listeners.onClassEvent('OpenCamera', sender, timeStamp);
                        }else{
                            this.listeners.onClassEvent('CloseCamera', sender, timeStamp);
                        }                        
                    }
                }
                if (req.rights.indexOf("mic") != -1) {
                    if (this.listeners.hasOwnProperty("onClassEvent")) {
                        if (enable){
                            this.listeners.onClassEvent('OpenMic', sender, timeStamp);
                        }else{
                            this.listeners.onClassEvent('CloseMic', sender, timeStamp);
                        }                        
                    }
                }
                if (req.rights.indexOf("draw") != -1) {
                    if (this.listeners.hasOwnProperty("onClassEvent")) {
                        if (enable){
                            this.listeners.onClassEvent('EnableDraw', sender, timeStamp);
                        }else{
                            this.listeners.onClassEvent('DisableDraw', sender, timeStamp);
                        }                        
                    }
                }
            }
        } else if (req.action == "change_member_permission"){   // 下发权限列表
            if (this.listeners.hasOwnProperty("onClassEvent")){
                this.listeners.onClassEvent('Permissions', sender, req.objectId);
            }
        } else if (req.action == "mutemsg") {      // 禁言
            if (req.objectId.indexOf(this.userId) != -1) {   // 处理发给自己的信令
                this.enableChat(false);
                if (this.listeners.hasOwnProperty("onClassEvent")){
                    this.listeners.onClassEvent('Silence', sender, req.objectId);
                }
            }
        } else if (req.action == "unmutemsg") {    // 解除禁言
            if (req.objectId.indexOf(this.userId) != -1) {   // 处理发给自己的信令
                this.enableChat(true);
                if (this.listeners.hasOwnProperty("onClassEvent")){
                    this.listeners.onClassEvent('UnSilence', sender, req.objectId);
                }
            }
        } else if (req.action == "mutemsg_all"){    // 全体禁言
            if ((this.isTeacher() || this.isAssistant()) && this.classInfo.settings.layout == '2'){
                if (this.iframe && this.iframe.contentWindow && this.iframe.contentWindow.hasOwnProperty('clientUpdateSysMsg')) {
                    this.iframe.contentWindow.clientSetState("enable_all_silence", true)
                }                
            }
            if (this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('SilenceAll', sender, timeStamp);
            }
        } else if (req.action == "unmutemsg_all"){    // 全体禁言
            if ((this.isTeacher() || this.isAssistant()) && this.classInfo.settings.layout == '2'){
                if (this.iframe && this.iframe.contentWindow && this.iframe.contentWindow.hasOwnProperty('clientUpdateSysMsg')) {
                    this.iframe.contentWindow.clientSetState("enable_all_silence", false)
                }                
            }
            if (this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('UnSilenceAll', sender, timeStamp);
            }
        } else if (req.action == "kickoff") {
            if (req.objectId.indexOf(this.userId) != -1) {   // 处理发给自己的信令
                if (this.listeners.hasOwnProperty("onClassEvent")) {
                    this.listeners.onClassEvent('KickOff', sender, timeStamp);
                }
            }
        } else if (req.action == "hand_up") {
            if (sender != this.userId && this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('HandUp', req.objectId, timeStamp);
            }
        } else if (req.action == "hand_down") {
            if (sender != this.userId && this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('HandDown', req.objectId, timeStamp);
            }
        } else if (req.action == "class_begin") {
            if (sender != this.userId && this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('StartClass', sender, timeStamp);
            }
        } else if (req.action == "class_end") {
            if (sender != this.userId && this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('EndClass', sender, timeStamp);
            }
        } else if (req.action == "join") {
            if (sender != this.userId && this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('Join', sender, timeStamp);
            }
        } else if (req.action == "exit") {
            if (sender != this.userId && this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('Exit', sender, timeStamp);
            }
        }else if (req.action == "show_check_in"){  // 签到显示
            if (this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('Show_Check_In', sender,req.check_id);
            }

        }else if (req.action == "hide_check_in"){  // 签到隐藏
            if (this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('Hide_Check_In', sender, req.check_id);
            }
        }else if(req.action == "show_question"){    // 答题
            if (this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('NewQuestion', sender, req);
            }
        }else if(req.action == "cancel_question"){  // 关闭答题
            if (this.listeners.hasOwnProperty("onClassEvent")) {
                this.listeners.onClassEvent('CancelQuestion', sender, req);
            }
        }
    },
    addCameraFrame: function(name){
        var component = this.curApp.container.getNodes().find((item) => {
            this.log("[TAG-SAAS] addCameraFrame->" + component + "/" + item.type);
            return item.type === 'cameraCtn'
        })

        if (null != this.cameraComponent) {
        } else if (typeof (component) != 'undefined') {
            //component.remove();
            this.cameraComponent = component;
        }else{
            this.cameraComponent = this.curApp.container.createNode({
                title: name,
                width: '320px',
                height: '240px',
                left: '10px',
                top: '10px',
                background: '#000',
                type: 'cameraCtn',
                present: true
            })
        }        
        this.cameraComponent.on('close', ()=>{
            this.log("[TAG-SAAS] cameraComponent->close");
            this.cameraComponent.remove();
            this.cameraComponent = null;
        })
        this.cameraComponent.on('remove', ()=>{
            this.log("[TAG-SAAS] cameraComponent->remove");
            this.cameraComponent = null;
        })
        //this.cameraComponent.$content.innerHTML = '<div class="camera_div" style="width:100%; height:100%;background-color:black;"></div>';
    },
    addVideoFrame: function (name) {
        // 移除已存在的视频组件
        var component = this.curApp.container.getNodes().find((item) => {
            this.log("[TAG-SAAS] addVideoFrame->" + component + "/" + item.type);
        })
        if (null != this.subVideoComponent) {
            this.subVideoComponent.remove();
            this.subVideoComponent = null;
        } else if (typeof (component) != 'undefined') {
            component.remove();
        }
        this.subVideoComponent = this.curApp.container.createNode({
            title: name,
            width: '400px',
            height: '300px',
            left: '10px',
            top: '10px',
            background: '#fff',
            type: 'videoCtn',
            present: true
        })
        this.subVideoComponent.on('close', () => {
            this.videoCanvas = null;
            this.subVideoComponent.remove();
            this.subVideoComponent = null;
        })
        this.subVideoComponent.on('remove', (e, data) => {
            this.log("[TAG-SAAS] video component removed");
            this.videoCanvas = null;
            this.stopPlayVideo();
            this.subVideoComponent = null;
        })
        if (this.isTeacher()) {
            this.subVideoComponent.$content.innerHTML =
                '<div class="canvas_video" style="width:100%; height:100%;background-color:black;"></div>'
                + '<div class="video_control">'
                + '<input type="range" class="video_seek" value="0" /><br /><img class="video_play" src="../img/video_pause.png" /><span id="video_pos" class="video_info">00:00/00:00</span>'
                + '</div>';
            this.setVideoStyle(this.subVideoComponent.$content);
        } else {
            this.subVideoComponent.$content.innerHTML = '<canvas class="canvas_video" style="width:100%; height:100%;background-color:black;"></canvas>';
        }
        this.videoCanvas = this.subVideoComponent.$content.getElementsByClassName('canvas_video')[0];
        this.log("[TAG-SAAS] addVideoFrame->finish add sub video");
    },
    setVideoStyle: function (componentDom) {
        var videoControl = componentDom.getElementsByClassName('video_control')[0];
        videoControl.style.position = 'relative';
        videoControl.style.top = '-50px';
        videoControl.style.width = "100%";
        videoControl.style.height = "50px";
        videoControl.style.backgroundColor = "#000";
        videoControl.style.textAlign = 'left';
        videoControl.style.opacity = "0";

        componentDom.addEventListener('mouseenter', function () {
            videoControl.style.opacity = "1";
        })
        componentDom.addEventListener('mouseleave', function () {
            videoControl.style.opacity = "0";
        })

        var videoBtn = componentDom.getElementsByClassName('video_play')[0];
        videoBtn.style.position = 'relative';
        videoBtn.style.verticalAlign = 'top';
        videoBtn.style.top = '-5px';
        videoBtn.style.marginLeft = '30px';
        videoBtn.style.marginRight = '30px';
        var self = this;
        videoBtn.onclick = function () {
            if (self.isPlaying) {
                self.pausePlayVideo();
                videoBtn.setAttribute("src", "../img/video_play.png");
                self.log("[TAG-SAAS] onclick->change icon play");
            } else {
                self.resumePlayVideo();
                videoBtn.setAttribute("src", "../img/video_pause.png");
                self.log("[TAG-SAAS] onclick->change icon pause");
            }
            self.isPlaying = !self.isPlaying;
        }

        var videoInfo = componentDom.getElementsByClassName('video_info')[0];
        videoInfo.style.position = 'relative';
        videoInfo.style.top = '-1px';
        videoInfo.style.marginLeft = "5px";
        videoInfo.style.marginRight = "5px";
        videoInfo.style.marginTop = "5px";
        videoInfo.style.color = "#FFF";
        videoInfo.style.fontSize = "12px";

        var videoSeek = componentDom.getElementsByClassName('video_seek')[0];
        videoSeek.style.width = "calc(100% + 20px)";
        videoSeek.style.verticalAlign = "top";
        videoSeek.style.left = "-10px";
        videoSeek.style.right = "-10px";
    },
    getTimeInfo: function (seconds) {
        var mins = Math.floor(seconds / 60);
        var secs = Math.floor(seconds % 60);
        return (mins > 9 ? mins : '0' + mins) + ":" + (secs > 9 ? secs : '0' + secs);
    },
    setVideoLength(pos, total) {
        this.log("[TAG-SAAS] setVideoLength->" + pos + "/" + total);
        try {
            if (pos == 0 && total == 0) {
                this.isPlaying = false;
                var videoBtn = this.subVideoComponent.$content.getElementsByClassName('video_play')[0];
                videoBtn.setAttribute("src", "../img/video_play.png");
                this.log("[TAG-SAAS] setVideoLength->change icon play");
            } else if (pos == 0 && total != 0) {
                this.isPlaying = true;
                var videoBtn = this.subVideoComponent.$content.getElementsByClassName('video_play')[0];
                videoBtn.setAttribute("src", "../img/video_pause.png");
                this.log("[TAG-SAAS] setVideoLength->change icon pause");
            }
            if (pos != 0 && pos != total && Math.abs(pos - this.lastVideoPos) < 1000)
                return;
            this.lastVideoPos = pos;
            var videoSeek = this.subVideoComponent.$content.getElementsByClassName('video_seek')[0];
            var videoInfo = this.subVideoComponent.$content.getElementsByClassName('video_info')[0];
            var posSecs = pos / 1000;
            var totalSecs = total / 1000;
            videoInfo.textContent = this.getTimeInfo(posSecs) + " / " + this.getTimeInfo(totalSecs);
            if (0 == videoSeek.max) {
                videoSeek.min = 0;
                videoSeek.max = totalSecs;
                videoSeek.addEventListener('input', () => {
                    log.info("[TAG-SAAS] setVideoPos: " + (videoSeek.value));
                    videoSeek.style.background = 'linear-gradient(to right, #0A818C ' + (this.value * 100 / this.max) + '%, #737882 1%, #737882)';
                    this.setVideoPos(videoSeek.value * 1000);
                });
            } else if (videoSeek.max != totalSecs) {
                videoSeek.max = totalSecs;
            }
            videoSeek.value = posSecs;
            videoSeek.style.background = 'linear-gradient(to right, #0A818C ' + (videoSeek.value * 100 / videoSeek.max) + '%, #737882 1%, #737882)';
        } catch (err) {
            log.error("[TAG-SAAS] setVideoLength->err: " + err);
        }
    },
    getVideoCanvas: function () {
        if (null == this.videoCanvas) {
            var component = this.curApp.container.getNodes().find((item) => {
                return item.type == 'videoCtn' && item.action != 'remove';
            });
            if (component) {
                component.show();
                component.$content.innerHTML =
                    '<canvas class="canvas_video" style="width:100%; height:100%;background-color:black;"></canvas>'
                this.videoCanvas = component.$content.getElementsByClassName('canvas_video')[0];
                component.on("remove", (e, data) => {
                    this.log("[TAG-SAAS] getVideoCanvas");
                    this.videoCanvas = null;
                })
            }
        }
        return this.videoCanvas;
    },

    startRemoteView: function (id, view) {
        liteav.startRemoteView(id, view);
    },
    stopRemoteView: function (id) {
        liteav.stopRemoteView(id);
    },
    startRemoteSubStreamView: function(id, view){
        liteav.startRemoteSubStreamView(id, view);
    },
    stopRemoteSubStreamView: function(id){
        liteav.stopRemoteSubStreamView(id);
    },

    enableCustomRender: function(open,callback){
        liteav.enableCustomRender(open, (userid, streamType, videoframe) => {
            if(userid.length<1)
                userid=g_data.userId
            callback(userid, streamType,videoframe.width,videoframe.height,videoframe.timestamp,videoframe.rotation,videoframe.data)

        })
    },

    getClientErrorMsg: function(json){
        if (json.error_code == 10227){
            return '课堂人数已达到上限';
        }else if(json.error_code == 10240){
            return '课堂不存在';
        }else if(json.error_code == 10270){
            return '机构码不存在';
        }else if(json.error_code == 10215){
            return '用户不存在';
        }else if (json.error_code == '10300'){
            return '最多只能提前一个小时进入课堂';
        }else{
            return json.error_msg;
        }
    }

    
}

/** 外部事件监听(liteav不支持子方法) */
function onRemoteVideoData(id, type, width, height, timestamp, rotation, data) {
    module.exports.onVideoData(id, type, width, height, timestamp, rotation, data);
}

