package cn.tee3.manager.service;

import android.Manifest;
import android.app.Activity;
import android.graphics.Bitmap;
import android.text.TextUtils;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;
import com.tbruyelle.rxpermissions2.Permission;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import cn.tee3.avd.AVDEngine;
import cn.tee3.avd.ErrorCode;
import cn.tee3.avd.MAudio;
import cn.tee3.avd.MScreen;
import cn.tee3.avd.MUserManager;
import cn.tee3.avd.MVideo;
import cn.tee3.avd.MWhiteboard;
import cn.tee3.avd.Room;
import cn.tee3.avd.Tlog;
import cn.tee3.avd.User;
import cn.tee3.avd.VideoDevice;
import cn.tee3.avd.VideoOptions;
import cn.tee3.avd.WhiteboardView;
import cn.tee3.manager.bean.RxBusMsgObj;
import cn.tee3.manager.util.RxBus;
import cn.tee3.manager.util.StringUtils;
import cn.tee3.manager.view.T3VideoView;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import tee3.webrtc.RendererCommon;

/**
 * 会议管理类：音视频处理
 */
public class MeetingManager implements IBaseService {

    private static final String TAG = MeetingManager.class.getSimpleName();
    private MVideo mVideo;
    private MScreen mScreen;
    private MAudio mAudio;
    private MUserManager userManager;
    private MWhiteboard mWhiteboard;
    private MVideo.CameraType defaultCamera = MVideo.CameraType.front;
    private CameraCallback cameraCallback;
    private UsbCameraCallback usbCameraCallback;
    private MicrophoneCallback microphoneCallback;
    private SpeakerCallback speakerCallback;
    private Disposable disposable;
    private List<T3VideoView> videoViewList = new ArrayList<>();

    private boolean isCameraBusying;
    private boolean isAutoMode;
    private boolean isMeetingInit;
    private static final int MAX_VIDEO_SUB_NUM = 20;    // 订阅视频最大阈值
    private boolean hasVideoFree;                       // 是否有视频未订阅（超过订阅视频最大阈值置位）

    private String initErrorMsg = "MeetingManager 尚未初始化，请调用init()初始化！";
    private static final String tag = "MeetingManager";
    private SetupCallback setupCallback;
    private Activity activity;
    private int maxBitrate;
    private int minBitrate;
    private boolean isEnableReleaseVideoView = true;
    private boolean enableAutoSub = true;
    private BindType bindType = BindType.DEFAULT;
    private List<User> autoSubUserList = new ArrayList<>();
    public HashMap<String, Boolean> deviceMap = new HashMap<>();
    public enum BindType {
        DEFAULT,
        BIND_USER,
        BIND_DEVICE
    }

    private MeetingManager() {
    }

    public static MeetingManager getInstance() {
        return Builder.INSTANCE;
    }

    private static class Builder {
        private static final MeetingManager INSTANCE = new MeetingManager();
    }

    /**
     * 初始化
     * @return
     */
    @Override
    public String init() {
        Room room = RoomManager.getInstance().getRoom();
        if (room == null) {
            initErrorMsg = "room尚未初始化，请先获取房间，如已获取房间则检查房间是否获取成功！";
            Log.w(tag, initErrorMsg);
            return initErrorMsg;
        }
        mVideo = MVideo.getVideo(room);
        mScreen = MScreen.getScreen(room);
        mAudio = MAudio.getAudio(room);
        userManager = MUserManager.getUserManager(room);
//        mWhiteboard = MWhiteboard.getWhiteboard(room);
        if (mVideo == null || mAudio == null || userManager == null) {
            initErrorMsg = "MeetingManager 初始化失败，传入的room异常";
            Log.w(tag, initErrorMsg);
            LogHelper.getInstance().appendLog("┣  ○  cn.tee3.manager :: MeetingManager.init() -> 失败，获取 mVideo、mAudio 为空")
                    .printLog();
        }
        LogHelper.getInstance().appendLog("┗  ●  cn.tee3.manager :: MeetingManager.init() -> 成功")
                .printLog()
                .logBufferClear();

        isMeetingInit = true;
        RxBus.getInstance().post(new RxBusMsgObj(RxBusMsgObj.MsgTag.Meeting_initComplete));

        return IBaseService.SUCCESS;
    }

    public void addVideoView(T3VideoView videoView) {
        Tlog.d(TAG, "addVideoView(): " + videoView.toString());
        if (videoView == null) {
            return;
        }
        if (!videoView.isUpdatedRender()) {
            videoView.updateRender();
        }
        if (!videoViewList.contains(videoView)) {
            videoViewList.add(videoView);

            Tlog.d(TAG, "addVideoView() -> add: " + videoViewList.toString());
            displayVideoRefresh();
        }
        Tlog.d(TAG, "addVideoView() -> out");
    }

    public void removeVideoView(T3VideoView videoView) {
        if (videoView == null) {
            return;
        }
        if (videoView.hasVideo()) {
            detachRender(videoView);
        }
        videoViewList.remove(videoView);
    }

    public void removeVideoList(List<T3VideoView> videoList) {
        for (T3VideoView videoView : videoList) {
            removeVideoView(videoView);
        }
    }

    public void resetVideoList(List<T3VideoView> videoList) {
        if (videoList == null) {
            return;
        }
        if (videoList.size() == 0) {
            return;
        }
        for (T3VideoView videoView : videoList) {
            if (!videoView.isUpdatedRender()) {
                videoView.updateRender();
            }
        }
        this.videoViewList.clear();
        this.videoViewList.addAll(videoList);
        displayVideoRefresh();
    }

    /**
     * 设置是否对传入的 VideoView 列表做释放操作
     * @param enable
     * @return
     */
    public MeetingManager enableReleaseVideoViewList(boolean enable) {
        this.isEnableReleaseVideoView = enable;
        return this;
    }

    /**
     * 设置是否优先使用 GPU Texture 进行编码，默认优先使用 Texture
     * @param enableTexture
     * @return
     */
    public MeetingManager enableTextureCodec(boolean enableTexture) {
        MVideo.enableTexture(enableTexture);
        return this;
    }

    /**
     * 设置是否优先使用 Camera2 接口
     * @param enable
     * @return
     */
    public MeetingManager enableCamera2API(boolean enable) {
        MVideo.enableCamera2API(enable);
        return this;
    }

    public boolean isEnableTextureCodec() {
        return MVideo.isEnableTexture();
    }

    /**
     * 是否已打开摄像头
     * @return
     */
    public boolean isOpenCamera() {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return false;
        }
        return mVideo.ispublishedLocalCamera();
    }

    /**
     * 是否打开已麦克风
     * @return
     */
    public boolean isOpenMicrophone() {
        if (mAudio == null) {
            Log.w(tag, "MeetingManager 尚未初始化，请调用init()初始化！");
            return false;
        }
        return mAudio.isOpenMicrophone();
    }

    /**
     * 是否已开启外放功能
     * @return
     */
    public boolean isHandFree() {
        if (mAudio == null) {
            Log.w(tag, "MeetingManager 尚未初始化，请调用init()初始化！");
            return false;
        }
        return mAudio.isHandFree();
    }

    /**
     * 当前使用的摄像头类型是否是前置摄像头
     * @return
     */
    public boolean isFrontCamera() {
        if (mVideo == null) {
            Log.w(tag, "MeetingManager 尚未初始化，请调用init()初始化！");
            return false;
        }
        return mVideo.getCurrentCameraType() == MVideo.CameraType.front;
    }

    /**
     * 打开/关闭摄像头
     */
    public void openCloseCamera(Activity activity) {
        if (RequestPermissions.permissionHelper(activity).isGranted(Manifest.permission.CAMERA)) {
            openCloseCamera();
        } else {
            RequestPermissions.permissionHelper(activity)
                    .request(new RequestPermissions.PermissionRequestCallback() {
                                 @Override
                                 public void onRequestCallback(Permission permission) { }

                                 @Override
                                 public void onGranted(String code) {
                                     if (code.equals(Manifest.permission.CAMERA)) {
                                         openCloseCamera();
                                     }
                                 }
                                 @Override
                                 public void onRefused(String code) {
                                 }
                                 @Override
                                 public void onNoLongerAsk(String code) {
                                 }
                             },
                            Manifest.permission.CAMERA
                    );
        }
    }

    /**
     * 打开/关闭摄像头
     */
    public void openCloseCamera() {
        if (mVideo == null || isCameraBusying) {
            return;
        }
        int ret;
        if (mVideo.ispublishedLocalCamera()) {
            ret = mVideo.unpublishLocalCamera();
            LogHelper.getInstance().appendLog("取消发布本地摄像头 -> " + ((ret == ErrorCode.AVD_OK) ? "成功" : "失败:" + ret))
                    .printLog();
        } else {
            ret = mVideo.publishLocalCamera(defaultCamera);
            LogHelper.getInstance().appendLog("发布本地摄像头 -> " + ((ret == ErrorCode.AVD_OK) ? "成功" : "失败:" + ret))
                    .printLog();
            if (ret != ErrorCode.AVD_OK && cameraCallback != null) {
                cameraCallback.error(ret);
            }
        }
        isCameraBusying = ret == 0;
    }

    /**
     * 打开/关闭摄像头
     * @param type 摄像头类型(前置或后置)
     */
    public void openCloseCamera(MVideo.CameraType type) {
        if (mVideo == null || isCameraBusying) {
            Log.w(tag, initErrorMsg);
            return;
        }
        int ret;
        if (mVideo.ispublishedLocalCamera()) {
            ret = mVideo.unpublishLocalCamera();
            LogHelper.getInstance().appendLog("取消发布本地摄像头")
                    .printLog();
        } else {
            ret = mVideo.publishLocalCamera(type);
            LogHelper.getInstance().appendLog("发布本地摄像头 -> type=" + type.name())
                    .printLog();
        }
        isCameraBusying = ret == 0;
    }

    /**
     * 打开摄像头
     * @param type 摄像头类型(前置或后置)
     */
    public void openCamera(MVideo.CameraType type) {
        if (mVideo == null || isCameraBusying) {
            Log.w(tag, initErrorMsg);
            return;
        }
        if (!mVideo.ispublishedLocalCamera()) {
            int ret = mVideo.publishLocalCamera(type);
            isCameraBusying = ret == 0;
            LogHelper.getInstance().appendLog("发布本地摄像头")
                    .printLog();
        }
    }

    /**
     * 关闭摄像头
     */
    public void closeCamera() {
        if (mVideo == null  || isCameraBusying) {
            Log.w(tag, initErrorMsg);
            return;
        }
        if (mVideo.ispublishedLocalCamera()) {
            int ret = mVideo.unpublishLocalCamera();
            isCameraBusying = ret == 0;
            LogHelper.getInstance().appendLog("取消发布本地摄像头")
                    .printLog();
        }
    }

    /**
     * 打开/关闭麦克风
     */
    public void openCloseMicrophone(Activity activity) {
        if (RequestPermissions.permissionHelper(activity).isGranted(Manifest.permission.RECORD_AUDIO)) {
            openCloseMicrophone();
        } else {
            RequestPermissions.permissionHelper(activity)
                    .request(new RequestPermissions.PermissionRequestCallback() {
                                 @Override
                                 public void onRequestCallback(Permission permission) {
                                 }

                                 @Override
                                 public void onGranted(String code) {
                                     if (code.equals(Manifest.permission.RECORD_AUDIO)) {
                                         openCloseMicrophone();
                                     }
                                 }
                                 @Override
                                 public void onRefused(String code) {
                                 }
                                 @Override
                                 public void onNoLongerAsk(String code) {
                                 }
                             },
                            Manifest.permission.RECORD_AUDIO
                    );
        }
    }

    /**
     * 打开/关闭麦克风
     */
    public void openCloseMicrophone() {
        if (mAudio == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        int result;
        if (mAudio.isOpenMicrophone()) {
            result = mAudio.closeMicrophone();
            LogHelper.getInstance().appendLog("关闭麦克风")
                    .printLog();
        } else {
            result = mAudio.openMicrophone();
            LogHelper.getInstance().appendLog("打开麦克风")
                    .printLog();
        }

        if (result != ErrorCode.AVD_OK) {
            microphoneCallback.error(result);
            LogHelper.getInstance().appendLog("○  " + (mAudio.isOpenMicrophone() ? "关闭" : "打开") + "麦克风失败")
                    .printLog();
        }
    }

    /**
     * 打开麦克风
     */
    public void openMicrophone() {
        if (mAudio == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        int result = mAudio.openMicrophone();
        LogHelper.getInstance().appendLog("打开麦克风")
                .printLog();
        if (result != ErrorCode.AVD_OK) {
            microphoneCallback.error(result);
            LogHelper.getInstance().appendLog("○  打开麦克风失败")
                    .printLog();
        }
    }

    /**
     * 关闭麦克风
     */
    public void closeMicrophone() {
        if (mAudio == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        int result = mAudio.closeMicrophone();
        LogHelper.getInstance().appendLog("关闭麦克风")
                .printLog();
        if (result != ErrorCode.AVD_OK) {
            microphoneCallback.error(result);
            LogHelper.getInstance().appendLog("○  关闭麦克风失败")
                    .printLog();
        }
    }

    public void openCloseSpeaker() {
        if (mAudio == null) {
            Log.w(tag, initErrorMsg);
            if (speakerCallback != null) {
                speakerCallback.error(initErrorMsg);
            }
            return;
        }
        mAudio.setHandFree(!mAudio.isHandFree());
        LogHelper.getInstance().appendLog("设置免提 -> " + !mAudio.isHandFree())
                .printLog();
        if (speakerCallback != null) {
            speakerCallback.success(mAudio.isHandFree());
        }
    }

    public void openSpeaker() {
        if (mAudio == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        mAudio.setHandFree(true);
        LogHelper.getInstance().appendLog("打开免提")
                .printLog();
    }

    public void closeSpeaker() {
        if (mAudio == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        mAudio.setHandFree(false);
        LogHelper.getInstance().appendLog("关闭免提")
                .printLog();
    }

    /**
     * 前后摄像头切换
     */
    public void frontBackCameraSwitch() {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        defaultCamera = (defaultCamera == MVideo.CameraType.front) ? MVideo.CameraType.back : MVideo.CameraType.front;
        mVideo.switchToLocalCamera();
    }

    /**
     * 设置视频容器
     * @param videoList
     * @return
     */
    public MeetingManager setVideoRenders(List<T3VideoView> videoList) {
        this.videoViewList.addAll(videoList);
        return this;
    }

    public List<T3VideoView> getVideoViewList() {
        return videoViewList;
    }

    /**
     * 启动自动模式
     * @param callback
     * @return
     */
    public MeetingManager setup4Auto(SetupCallback callback) {
        this.setupCallback = callback;
        RxBusMesHandlerInit();
        subscribeAllVideo();
        if (AVDManager.getInstance().getSetting().isOpenCamera()) {
            if (activity != null) {
                openCloseCamera(activity);
            } else {
                openCloseCamera();
            }
        }
        if (AVDManager.getInstance().getSetting().isOpenMIC()) {
            if (activity != null) {
                openCloseMicrophone(activity);
            } else {
                openCloseMicrophone();
            }
        }
        if (AVDManager.getInstance().getSetting().isOpenSpeaker()) {
            openSpeaker();
        } else {
            closeSpeaker();
        }

        RxBus.getInstance().post(new RxBusMsgObj(RxBusMsgObj.MsgTag.Meeting_setupMeeting));

        LogHelper.getInstance().appendLog("cn.tee3.manager :: MeetingManager.setup4Auto()")
                .printLog();

        if (setupCallback != null) {
            setupCallback.success("");
        }
        return this;
    }

    /**
     * 启动自动模式
     * @param activity 加入会议打开相机和麦克风时需要上下文来获取动态权限
     * @param callback
     * @return
     */
    public MeetingManager setup4Auto(Activity activity, SetupCallback callback) {
        if (activity != null) {
            this.activity = activity;
        }
        return setup4Auto(callback);
    }

    /**
     * 启动手动模式
     * @return
     */
    public MeetingManager setup4Customize() {
        isAutoMode = false;
        if (AVDManager.getInstance().isAVDSDKInit()) {
            RxBus.getInstance().post(new RxBusMsgObj(RxBusMsgObj.MsgTag.Meeting_setupMeeting));
        }
        return this;
    }

    public boolean isMeetingInit() {
        return isMeetingInit;
    }

    /**
     * 设置摄像头 打开/关闭 事件通知
     * @param callback
     * @return
     */
    public MeetingManager setCameraCallback(CameraCallback callback) {
        this.cameraCallback = callback;
        return this;
    }

    /**
     * 设置摄像头 打开/关闭 事件通知
     * @param callback
     * @return
     */
    public MeetingManager setMicrophoneCallback(MicrophoneCallback callback) {
        this.microphoneCallback = callback;
        return this;
    }

    /**
     * 设置听筒和扬声器切换 事件通知
     * @param callback
     * @return
     */
    public MeetingManager setSpeakerCallback(SpeakerCallback callback) {
        this.speakerCallback = callback;
        return this;
    }

    /**
     * 设置摄像头 打开/关闭 事件通知
     * @param usbCameraCallback
     * @return
     */
    public MeetingManager setUsbCameraCallback(UsbCameraCallback usbCameraCallback) {
        this.usbCameraCallback = usbCameraCallback;
        return this;
    }

    private void RxBusMesHandlerInit() {
        disposable = RxBus.getInstance().toObservable(RxBusMsgObj.class).subscribe(new Consumer<RxBusMsgObj>() {
            @Override
            public void accept(RxBusMsgObj rxBusMsgObj) {
                int result = 0;
                String videoId = null;
                User user = null;

                Object msg = rxBusMsgObj.getMsgBody();
                if (msg instanceof CallbackParams) {
                    CallbackParams callbackParams = (CallbackParams) msg;
                    result = callbackParams.result;
                    videoId = callbackParams.fromId;
                } else if (msg instanceof MVideo.Camera) {
                    videoId = ((MVideo.Camera) msg).getId();
                } else if (msg instanceof User) {
                    user = (User) msg;
                } else if (msg instanceof Integer) {
                    result = (int) msg;
                } else if (msg instanceof MScreen.ScreenWindow) {
                    videoId = ((MScreen.ScreenWindow)msg).getId();
                }

                switch (rxBusMsgObj.getMsgTag()) {
                    case Meeting_onPublishLocalCameraNotify:
                        isCameraBusying = false;
                        if (minBitrate > 0 && maxBitrate > 0) {
                            mVideo.setVideoBitrate(videoId, minBitrate, maxBitrate);
                        }
                        if (mVideo.getCamera(videoId).getCameraType() == MVideo.CameraType.unknow) {
                            if (usbCameraCallback != null) {
                                if (result == ErrorCode.AVD_OK) {
                                    usbCameraCallback.success(true);
                                } else {
                                    usbCameraCallback.error(result);
                                }
                            }
                        } else {
                            if (cameraCallback != null) {
                                if (result == ErrorCode.AVD_OK) {
                                    cameraCallback.success(true);
                                } else {
                                    cameraCallback.error(result);
                                }
                            }
                        }
                        if (result == 1020) {
                            Tlog.w(tag, "发布本地视频失败 code：" + result);
                            openCloseCamera();  // TODO: 2020/11/4 远端开摄像头会失败，此时需要上面自己来开启摄像头。
                            return;
                        }
                        attachRender(videoId);
                        break;
                    case Meeting_onUnpublishLocalResult:
                        isCameraBusying = false;
                        detachRender(videoId);
                        if (mVideo.getCamera(videoId).getCameraType() == MVideo.CameraType.unknow) {
                            if (usbCameraCallback != null) {
                                usbCameraCallback.success(false);
                            }
                        } else {
                            if (cameraCallback != null) {
                                cameraCallback.success(false);
                            }
                        }
                        break;
                    case Meeting_onPublishCameraNotify:
                        Tlog.w("MeetingManager", "发布视频：userName = " + userManager.getOwnerName(videoId) + ", userId= " + userManager.getOwnerId(videoId) + ", videoId= " + videoId);
                        if (mVideo.getSubscribedCameras().size() >= MeetingManager.MAX_VIDEO_SUB_NUM) {
                            hasVideoFree = true;
                            return;
                        }
                        synchronized (autoSubUserList) {
                            if (enableAutoSub || autoSubUserList.contains(userManager.getUser(userManager.getOwnerId(videoId)))) {
                                mVideo.subscribe(videoId);
//                                mVideo.subscribeWithVideoQuality(videoId, VideoOptions.VideoQuality.quality_normal);
                            }
                        }
                        break;
                    case Meeting_onPublishScreenNotify:
                        mVideo.setVideoBitrate(videoId, minBitrate, maxBitrate);
                        synchronized (autoSubUserList) {
                            if (enableAutoSub || autoSubUserList.contains(userManager.getUser(userManager.getOwnerId(videoId)))) {
                                mScreen.subscribe(videoId);
                            }
                        }
                        break;
                    case Meeting_onSubscribeResult:
                    case Meeting_onSubscribeScreenResult:
                        attachRender(videoId);
                        break;
                    case Meeting_onUnpublishCameraNotify:
                        if (mVideo.isSelfDevice(videoId)) {
                            return;
                        }
                        mVideo.unsubscribe(videoId);

                        detachRender(videoId);
                        if (mVideo.getPublishedCameras().size() < MeetingManager.MAX_VIDEO_SUB_NUM && hasVideoFree) {
                            hasVideoFree = false;
                            subscribeAllVideo();
                        }
                        break;
                    case Meeting_onUnpublishScreenNotify:
                        mScreen.unsubscribe(videoId);
                        detachRender(videoId);
                        if (mVideo.getPublishedCameras().size() < MeetingManager.MAX_VIDEO_SUB_NUM && hasVideoFree) {
                            hasVideoFree = false;
                            subscribeAllVideo();
                        }
                        break;
                    case Meeting_onUnsubscribeResult:
                        break;
                    case Meeting_onUserLeaveNotify:
//                        userLeaveRefresh(user);
                        break;
                    case Meeting_onOpenMicrophone:
                        if (microphoneCallback != null) {
                            if (result == ErrorCode.AVD_OK) {
                                microphoneCallback.success(true);
                            } else {
                                microphoneCallback.error(result);
                            }
                        }
                        break;
                    case Meeting_onCloseMicrophone:
                        if (microphoneCallback != null) {
                            if (result == ErrorCode.AVD_OK) {
                                microphoneCallback.success(false);
                            } else {
                                microphoneCallback.error(result);
                            }
                        }
                        break;
                }
            }
        });
    }

    /**
     * 获取 user 发布的视频列表（可以是摄像头 DeviceType.CAMERA 和共享屏幕 DeviceType.SCREEN 之一，或不区分两者 VideoParams.DeviceType.UNKNOWN）
     * @param userId 用户名
     * @param type 视频类型
     * @return
     */
    public List<VideoDevice> getPublishedVideosByUserId(String userId, VideoParams.DeviceType type) {
        List<VideoDevice> deviceList = new ArrayList<>();
        if (TextUtils.isEmpty(userId) || !isMeetingInit) {
            return deviceList;
        }

        if (type == VideoParams.DeviceType.UNKNOWN || type == VideoParams.DeviceType.CAMERA) {
            List<MVideo.Camera> cameras = mVideo.getPublishedCameras();
            for (MVideo.Camera camera : cameras) {
                if (userManager.getOwnerId(camera.getId()).equals(userId)) {
                    deviceList.add(camera);
                }
            }
        }

        if (type == VideoParams.DeviceType.UNKNOWN || type == VideoParams.DeviceType.SCREEN) {
            List<MScreen.ScreenWindow> screens = mScreen.getPublishedScreens();
            for (MScreen.ScreenWindow screen : screens) {
                if (userManager.getOwnerId(screen.getId()).equals(userId)) {
                    deviceList.add(screen);
                }
            }
        }
        return deviceList;
    }

    /**
     * 获取已订阅 user 发布的视频列表（可以是摄像头 DeviceType.CAMERA 和共享屏幕 DeviceType.SCREEN 之一，或不区分两者 VideoParams.DeviceType.UNKNOWN）
     * @param userId 用户名
     * @param type 视频类型
     * @return
     */
    public List<VideoDevice> getSubscribedVideosByUserId(String userId, VideoParams.DeviceType type) {
        List<VideoDevice> deviceList = new ArrayList<>();
        if (TextUtils.isEmpty(userId) || !isMeetingInit) {
            return deviceList;
        }
        boolean isSelf = userManager.isSelfUser(userId);
        if (type == VideoParams.DeviceType.UNKNOWN || type == VideoParams.DeviceType.CAMERA) {
            if (isSelf) {
                for (MVideo.Camera camera : mVideo.getPublishedCameras()) {
                    if (userManager.getOwnerId(camera.getId()).equals(userManager.getSelfUserId())) {
                        deviceList.add(camera);
                    }
                }
            } else {
                for (MVideo.Camera camera : mVideo.getSubscribedCameras()) {
                    if (userManager.getOwnerId(camera.getId()).equals(userId)) {
                        deviceList.add(camera);
                    }
                }
            }

        }

        if (type == VideoParams.DeviceType.UNKNOWN || type == VideoParams.DeviceType.SCREEN) {
            if (isSelf) {
                for (MScreen.ScreenWindow screen : mScreen.getPublishedScreens()) {
                    if (userManager.getOwnerId(screen.getId()).equals(userManager.getSelfUserId())) {
                        deviceList.add(screen);
                    }
                }
            } else {
                for (MScreen.ScreenWindow screen : mScreen.getSubscribedScreens()) {
                    if (userManager.getOwnerId(screen.getId()).equals(userId)) {
                        deviceList.add(screen);
                    }
                }
            }
        }
        return deviceList;
    }

    /**
     * 通过设备Id来获取空闲的 videoView 列表
     * @param deviceId 设备Id号
     * @return
     */
    public List<T3VideoView> getFreeRender(String deviceId) {
        if (!isMeetingInit || !StringUtils.isNotEmpty(deviceId) && !mVideo.isCameraSubscribed(deviceId)) {
            return new ArrayList<>();
        }
        VideoParams.DeviceType deviceType = VideoParams.DeviceType.UNKNOWN;
        List<T3VideoView> freeVideoViewList = new ArrayList<>();
        String userId = userManager.getOwnerId(deviceId);
        List<VideoDevice> cameraList = getSubscribedVideosByUserId(userId, VideoParams.DeviceType.UNKNOWN);
        for (VideoDevice device : cameraList) {
            if (deviceId.equals(device.getId())) {
                if (device instanceof MVideo.Camera) {
                    deviceType = VideoParams.DeviceType.CAMERA;
                    break;
                } else if (device instanceof MScreen.ScreenWindow) {
                    deviceType = VideoParams.DeviceType.SCREEN;
                    break;
                }
            }
        }


//        Toast.makeText(AVDEngine.instance().getContext(), "deviceType= " + deviceType.name(), Toast.LENGTH_SHORT).show();
        if (deviceType == VideoParams.DeviceType.UNKNOWN) {
            return freeVideoViewList;
        }

        for (T3VideoView videoView : videoViewList) {
            if (videoView.hasBindUser() && videoView.getBindUserId().equals(userId)) {
                if (videoView.getVideoParams().getDeviceType() == deviceType) {
                    freeVideoViewList.add(videoView);
                }
            }
        }

        if (bindType == BindType.BIND_USER || freeVideoViewList.size() > 0) {
            return freeVideoViewList;
        }

        for (T3VideoView videoView : videoViewList) {
            if (!videoView.hasVideo() && !videoView.hasBindUser()) {
                freeVideoViewList.add(videoView);
                return freeVideoViewList;
            }
        }

        return freeVideoViewList;
    }

    /**
     * 通过设备Id来获取空闲的Render
     * @param inDeviceId 设备Id号
     * @return
     */
    public List<T3VideoView> getFreeRenderList4BindDevice(String inDeviceId) {
        List<T3VideoView> freeVideoViewList = new ArrayList<>();
        if (TextUtils.isEmpty(inDeviceId)) {
            return freeVideoViewList;
        }
        for (T3VideoView videoView : videoViewList) {
            String deviceId = videoView.getVideoParams().getDeviceId();
            if (!TextUtils.isEmpty(deviceId) && deviceId.equals(inDeviceId)) {
                freeVideoViewList.add(videoView);
            }
        }
        return freeVideoViewList;
    }

    /**
     * 渲染视频
     * @param videoView 视频容器
     * @param videoId 视频id
     */
    public void attachRender(T3VideoView videoView, String videoId) {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        if (videoView.hasVideo()) {
            detachRender(videoView);
        }
        if (deviceMap.containsKey(videoId)) {
            if (deviceMap.get(videoId)) {
                mVideo.attachRender(videoId, videoView.getRender());
            } else {
                mScreen.attachRender(videoId, videoView.getRender());
            }
            videoView.setVideo(videoId);
        }
    }

    public void attachRender(String videoId) {
        if (TextUtils.isEmpty(videoId)) {
            return;
        }
        for (T3VideoView videoView : videoViewList) {
            if (videoId.equals(videoView.getVideoId()) && !videoView.hasBindUser()) {
                return;
            }
        }
        List<T3VideoView> freeVideoViewList = new ArrayList<>();
        switch (bindType) {
            case DEFAULT:
            case BIND_USER:
                freeVideoViewList = getFreeRender(videoId);
                break;
            case BIND_DEVICE:
                freeVideoViewList = getFreeRenderList4BindDevice(videoId);
                break;
        }

//        Tlog.w("qqq", "freeList-> " + freeVideoViewList.toString());
        for (T3VideoView videoView : freeVideoViewList) {
            if (videoView == null) {
                Tlog.w(TAG, "videoView is null");
                continue;
            }else if (videoView.getRender() == null) {
                Tlog.w(TAG, "render is null");
                continue;
            } else if (videoView.getRender().viewRenderer == null) {
                Tlog.w(TAG, "surfaceViewRender is null");
                continue;
            }

            if (videoView.hasVideo()) {
                if (videoView.getVideoId().equals(videoId)) {
                    continue;
                }
                detachRender(videoView);
            }
            if (deviceMap.containsKey(videoId)) {
                if (deviceMap.get(videoId)) {
                    mVideo.attachRender(videoId, videoView.getRender());
                } else {
                    mScreen.attachRender(videoId, videoView.getRender());
                }
                videoView.setVideo(videoId);
            }
        }
        Tlog.d(TAG, "attachRender(): " + videoId + ", videoList-> " + videoViewList.toString());
    }

    /**
     * 取消渲染
     * @param videoView 视频id
     */
    public void detachRender(T3VideoView videoView) {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        if (!videoView.hasVideo()) {
            return;
        }
        String videoId = videoView.getVideoId();
        if (deviceMap.containsKey(videoId)) {
            if (deviceMap.get(videoId)) {
                mVideo.detachRender(videoView.getRender());
            } else {
                mScreen.detachRender(videoView.getRender());
            }
        } else {
            mVideo.detachRender(videoView.getRender());
        }
        videoView.setVideo(null);
    }

    public void detachRenderNoClearVideoParam(T3VideoView videoView) {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        if (!videoView.hasVideo()) {
            return;
        }
        if (deviceMap.containsKey(videoView.getVideoId())) {
            if (deviceMap.get(videoView.getVideoId())) {
                mVideo.detachRender(videoView.getRender());
            } else {
                mScreen.detachRender(videoView.getRender());
            }
            videoView.setVideo(null);
        }
    }

    /**
     * 取消渲染
     * @param videoId 设备id
     */
    public void detachRender(String videoId) {
        if (!StringUtils.isNotEmpty(videoId)) {
            return;
        }

        for (T3VideoView videoView : videoViewList) {
            if (videoId.equals(videoView.getVideoId())) {
                detachRender(videoView);
            }
        }
        deviceMap.remove(videoId);
    }

    /**
     * 交换两个视频窗口正在渲染的视频
     * @param videoView_01 视频窗口01
     * @param videoView_02 视频窗口02
     */
    public void swapVideoView(T3VideoView videoView_01, T3VideoView videoView_02) {
        Tlog.d(TAG, "swapVideoView(): " + videoView_01.toString() + " && " + videoView_02.toString());
        VideoParams params01 = videoView_01.getVideoParams();
        VideoParams params02 = videoView_02.getVideoParams();

        VideoParams videoParams_01 = new VideoParams(params01.getUserId(), params01.getUserName(), params01.getDeviceId(), params01.getDeviceType());
        VideoParams videoParams_02 = new VideoParams(params02.getUserId(), params02.getUserName(), params02.getDeviceId(), params02.getDeviceType());

        videoView_01.setVideoParams(videoParams_02);
        videoView_02.setVideoParams(videoParams_01);

        String videoId_01 = videoView_01.getVideoId();
        String videoId_02 = videoView_02.getVideoId();

        if (videoView_01.hasVideo()) {
            detachRender(videoView_01);
        }

        if (videoView_02.hasVideo()) {
            detachRender(videoView_02);
        }

        RendererCommon.ScalingType scalingType_01 = videoView_01.getScalingType();
        RendererCommon.ScalingType scalingType_02 = videoView_02.getScalingType();
        videoView_01.setScalingType(scalingType_02);
        videoView_02.setScalingType(scalingType_01);

        if (StringUtils.isNotEmpty(videoId_02)) {
            attachRender(videoView_01, videoId_02);
        }

        if (StringUtils.isNotEmpty(videoId_01)) {
            attachRender(videoView_02, videoId_01);
        }
    }

    public void bindVideoChangedUpdate() {
        switch (bindType) {
            case BIND_USER:

                break;
            case BIND_DEVICE:
                for (T3VideoView videoView : videoViewList) {
                    if (videoView.hasBindDevice() && !videoView.getBindDeviceId().equals(videoView.getVideoId())) {
                        detachRender(videoView);
                    }
                    if (videoView.hasBindDevice() && TextUtils.isEmpty(videoView.getVideoId())) {
                        attachRender(videoView, videoView.getBindDeviceId());
                    }
                }
                break;
        }
    }

    /**
     * 订阅共享屏幕
     * @param screenId 共享屏幕id
     */
    public void subscribeScreen(String screenId) {
        if (mScreen == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        mScreen.subscribe(screenId);
    }

    /**
     * 订阅视频
     * @param videoId 视频id
     */
    public void subscribeVideo(String videoId) {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        if (!mVideo.isCameraSubscribed(videoId)) {
            mVideo.subscribe(videoId);
        }
    }

    /**
     * 取消订阅视频
     * @param videoId 视频id
     */
    public void unSubscribeVideo(String videoId) {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        mVideo.unsubscribe(videoId);
    }

    /**
     * 订阅所有已发布的视频
     */
    public void subscribeAllVideo() {
        if (!isEnableAutoSub()) {
            return;
        }
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        List<MVideo.Camera> cameras = mVideo.getPublishedCameras();
        for (MVideo.Camera camera : cameras) {
            mVideo.subscribe(camera.getId());
//            mVideo.subscribeWithVideoQuality(camera.getId(), VideoOptions.VideoQuality.quality_normal);
        }

        List<MScreen.ScreenWindow> screens = mScreen.getPublishedScreens();
        for (MScreen.ScreenWindow screen : screens) {
            mScreen.subscribe(screen.getId());
        }
    }

    /**
     * 订阅所有已发布的视频
     */
    public void subscribeAllVideo(List<User> userList) {
        if (userList.size() == 0) {
            return;
        }
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }

        List<MVideo.Camera> cameras = mVideo.getPublishedCameras();
        for (MVideo.Camera camera : cameras) {
            for (User user : userList) {
                if (userManager.getOwnerId(camera.getId()).equals(user.getUserId())) {
                    mVideo.subscribe(camera.getId());
                }
            }
        }

        List<MScreen.ScreenWindow> screens = mScreen.getPublishedScreens();
        for (MScreen.ScreenWindow screen : screens) {
            for (User user : userList) {
                if (userManager.getOwnerId(screen.getId()).equals(user.getUserId())) {
                    mScreen.subscribe(screen.getId());
                }
            }
        }
    }

    /**
     * 取消订阅所有已发布的视频
     */
    public void unsubscribeAllVideo() {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        List<MVideo.Camera> cameras = mVideo.getPublishedCameras();
        for (MVideo.Camera camera : cameras) {
            mVideo.unsubscribe(camera.getId());
        }

        List<MScreen.ScreenWindow> screens = mScreen.getPublishedScreens();
        for (MScreen.ScreenWindow screen : screens) {
            mScreen.unsubscribe(screen.getId());
        }

        for (T3VideoView videoView : videoViewList) {
            if (videoView.hasVideo()) {
                detachRender(videoView);
            }
        }
    }

    public void unsubAndDetachAllVideo() {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }

        if (videoViewList == null) {
            return;
        }

        List<MVideo.Camera> cameras = mVideo.getPublishedCameras();
        for (MVideo.Camera camera : cameras) {
            if (userManager.isSelfDevice(camera.getId())) {
                mVideo.unpublishLocalCamera();
            } else {
                mVideo.unsubscribe(camera.getId());
            }
        }
    }

    public void resetAllVideo() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    boolean isPublished = mVideo.ispublishedLocalCamera();
                    unsubAndDetachAllVideo();
                    Thread.sleep(500);
                    subscribeAllVideo();
                    if (isPublished) {
                        Thread.sleep(500);
                        mVideo.publishLocalCamera(defaultCamera);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).run();
    }

    /**
     * 从订阅的视频从查找出未渲染视频，并把此视频渲染到视频容器中
     */
    public void displayVideoRefresh() {
        Tlog.d(TAG, "displayVideoRefresh() -> in");
        for (T3VideoView videoView : videoViewList) {
            Tlog.d(TAG, "displayVideoRefresh() -> videoView: " + videoView.toString());
            Tlog.d(TAG, "displayVideoRefresh() -> videoView.hasBindUser() ? " + videoView.hasBindUser() + ", userManager.getOwnerId(videoView.getVideoId()) = " + userManager.getOwnerId(videoView.getVideoId())
                    +", videoView.getVideoParams().getUserId() = " + videoView.getVideoParams().getUserId());
            if (videoView.hasBindUser() && userManager.getOwnerId(videoView.getVideoId()).equals(videoView.getVideoParams().getUserId())) {
                continue;
            }
            String freeDevice = getFreeDevice(videoView);
            Tlog.d(TAG, "displayVideoRefresh() -> freeDevice: " + freeDevice);
            if (!TextUtils.isEmpty(freeDevice)) {
                attachRender(videoView, freeDevice);
            }
        }
    }

    /**
     * 从订阅的视频从查找出未渲染视频，并把此视频渲染到视频容器中
     */
    public void resubscribeAllVideo() {
        if (isMeetingInit && mVideo != null) {
            for (MVideo.Camera camera : mVideo.getPublishedCameras()) {
                if (!userManager.isSelfDevice(camera.getId())) {
                    mVideo.unsubscribe(camera.getId());
                }
            }
        }
    }

    public T3VideoView findVideoViewBound2Device(String videoId) {
        if (TextUtils.isEmpty(videoId)) {
            return null;
        }
        for (T3VideoView videoView : videoViewList) {
            if (videoView.hasBindUser() && userManager.getOwnerId(videoId).equals(videoView.getVideoParams().getUserId())) {
                return videoView;
            }
        }
        return null;
    }

    /**
     * 从已订阅的视频列表中查找出第一个尚未渲染的camera视频
     * @param videoView 视频容器
     * @return 获取到的Camera
     */
    public String getFreeDevice(T3VideoView videoView) {
        Tlog.d(TAG, "getFreeDevice() -> videoView: " + videoView.toString());
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return null;
        }
        List<MVideo.Camera> cameras = mVideo.getSubscribedCameras();
        if (mVideo.ispublishedLocalCamera()) {
            MVideo.Camera localCamera = mVideo.getCamera(mVideo.getCurrentCameraId());
            cameras.add(localCamera);
        }

        List<MScreen.ScreenWindow> screens = mScreen.getPublishedScreens();
        if (cameras.size() == 0 && screens.size() == 0) {
            return null;
        }
        Tlog.d(TAG, "getFreeDevice() -> bindType: " + bindType.name());
        switch (bindType) {
            case DEFAULT:
                String freeDevice = getFreeDeviceDefaultType(cameras, screens, videoView);
                Tlog.d(TAG, "getFreeDevice() -> freeDevice: " + freeDevice);
                if (!TextUtils.isEmpty(freeDevice)) {
                    Tlog.d(TAG, "getFreeDevice() -> return: " + freeDevice);
                    return freeDevice;
                }
                break;
            case BIND_USER:
                if (videoView.hasBindUser()) {
                    String userId = videoView.getVideoParams().getUserId();
                    if (videoView.getVideoParams().getDeviceType() == VideoParams.DeviceType.CAMERA) {
                        for (MVideo.Camera camera : cameras) {
                            if (userId.equals(userManager.getOwnerId(camera.getId()))) {
                                return camera.getId();
                            }
                        }
                    } else if (videoView.getVideoParams().getDeviceType() == VideoParams.DeviceType.SCREEN) {
                        for (MScreen.ScreenWindow screen : screens) {
                            if (userId.equals(userManager.getOwnerId(screen.getId()))) {
                                return screen.getId();
                            }
                        }
                    }
                }
                break;
            case BIND_DEVICE:
                if (videoView.hasBindDevice()) {
                    String deviceId = videoView.getVideoParams().getDeviceId();
                    if (videoView.getVideoParams().getDeviceType() == VideoParams.DeviceType.CAMERA) {
                        for (MVideo.Camera camera : cameras) {
                            if (deviceId.equals(camera.getId())) {
                                return camera.getId();
                            }
                        }
                    } else if (videoView.getVideoParams().getDeviceType() == VideoParams.DeviceType.SCREEN) {
                        for (MScreen.ScreenWindow screen : screens) {
                            if (deviceId.equals(screen.getId())) {
                                return screen.getId();
                            }
                        }
                    }
                }
                break;
        }
        return null;
    }

    /**
     * 从已订阅的视频列表中查找出第一个尚未渲染的camera视频
     * @param videoView 视频容器
     * @return 获取到的Camera
     */
    public String getFreeDeviceDefaultType(List<MVideo.Camera> cameras, List<MScreen.ScreenWindow> screens, T3VideoView videoView) {
        Tlog.d(TAG, "getFreeDeviceDefaultType() -> in");
        for (MVideo.Camera camera : cameras) {
            Tlog.d(TAG, "cameraId: " + camera.getId());
        }
        for (MScreen.ScreenWindow screen : screens) {
            Tlog.d(TAG, "screen: " + screen.getId());
        }

        if (videoView.hasBindUser()) {
            Tlog.d(TAG, "getFreeDeviceDefaultType() -> 11111111");
            if (videoView.getVideoParams().getDeviceType() == VideoParams.DeviceType.CAMERA) {
                Tlog.d(TAG, "getFreeDeviceDefaultType() -> 2222222222");
                for (MVideo.Camera camera : cameras) {

                    Tlog.d(TAG, "videoView.getVideoParams().getUserId(): " + videoView.getVideoParams().getUserId() + ", userManager.getOwnerId(camera.getId()) : " + userManager.getOwnerId(camera.getId()));
                    if (videoView.getVideoParams().getUserId().equals(userManager.getOwnerId(camera.getId()))) {
                        Tlog.d(TAG, "getFreeDeviceDefaultType() -> 4444444444");
                        return camera.getId();
                    }
                }
            } else if (videoView.getVideoParams().getDeviceType() == VideoParams.DeviceType.SCREEN) {
                Tlog.d(TAG, "getFreeDeviceDefaultType() -> 33333333333");
                for (MScreen.ScreenWindow screen : screens) {
                    Tlog.d(TAG, "videoView.getVideoParams().getUserId(): " + videoView.getVideoParams().getUserId() + ", userManager.getOwnerId(screen.getId()) : " + userManager.getOwnerId(screen.getId()));
                    if (videoView.getVideoParams().getUserId().equals(userManager.getOwnerId(screen.getId()))) {
                        Tlog.d(TAG, "getFreeDeviceDefaultType() -> 55555555555");
                        return screen.getId();
                    }
                }
            }
        } else {
            Tlog.d(TAG, "getFreeDeviceDefaultType() -> 6666666666666");
            for (T3VideoView t3VideoView : videoViewList) {
                if (t3VideoView.hasVideo()) {
                    Iterator<MVideo.Camera> iterator = cameras.iterator();
                    while (iterator.hasNext()) {
                        MVideo.Camera camera = iterator.next();
                        if (t3VideoView.getVideoId().equals(camera.getId())) {
                            iterator.remove();
                            break;
                        }
                    }
                }
            }
            if (cameras.size() > 0) {
                List<String> bindUserList = getBindUserList();
                if (bindUserList.size() == 0) {
                    return cameras.get(0).getId();
                }
                for (MVideo.Camera camera : cameras) {
                    for (String userId : bindUserList) {
                        if (userId.equals(userManager.getOwnerId(camera.getId()))) {
                            continue;
                        }
                        return camera.getId();
                    }
                }
            } else {
                for (T3VideoView t3VideoView : videoViewList) {
                    if (t3VideoView.hasVideo()) {
                        Iterator<MScreen.ScreenWindow> iterator = screens.iterator();
                        while (iterator.hasNext()) {
                            MScreen.ScreenWindow screen = iterator.next();
                            if (t3VideoView.getVideoId().equals(screen.getId())) {
                                iterator.remove();
                                break;
                            }
                        }
                    }
                }
                if (screens.size() > 0) {
                    return screens.get(0).getId();
                }
            }
        }
        return null;
    }

    public List<String> getBindUserList() {
        List<String> bindUserIdList = new ArrayList<>();
        for (T3VideoView t3VideoView : videoViewList) {
            if (t3VideoView.hasBindUser()) {
                bindUserIdList.add(t3VideoView.getVideoParams().getUserId());
            }
        }
        return bindUserIdList;
    }

    /**
     * 从已订阅的视频列表中查找出第一个尚未渲染的camera视频
     * @param videoList 视频容器列表
     * @return 获取到的Camera
     */
    public MVideo.Camera getFreeCamera(List<T3VideoView> videoList) {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return null;
        }
        List<MVideo.Camera> cameras = mVideo.getSubscribedCameras();
        if (mVideo.ispublishedLocalCamera()) {
            mVideo.getCurrentCameraId();
            MVideo.Camera localCamera = mVideo.getCamera(mVideo.getCurrentCameraId());
            cameras.add(localCamera);
        }

        for (T3VideoView videoView : videoList) {
            if (videoView.hasVideo()) {
                Iterator<MVideo.Camera> iterator = cameras.iterator();
                while (iterator.hasNext()) {
                    MVideo.Camera camera = iterator.next();
                    if (videoView.getVideoId().equals(camera.getId())) {
                        iterator.remove();
                        break;
                    }
                }
            }
        }
        if (cameras.size() == 0) {
            return null;
        }
        return cameras.get(0);
    }

    /**
     * 从已订阅的视频列表中查找出第一个尚未渲染的screen视频
     * @param videoList 视频容器列表
     * @return 获取到的Screen
     */
    public MScreen.ScreenWindow getFreeScreen(List<T3VideoView> videoList) {
        if (mScreen == null) {
            Log.w(tag, initErrorMsg);
            return null;
        }
        List<MScreen.ScreenWindow> screens = mScreen.getSubscribedScreens();

        for (T3VideoView videoView : videoList) {
            if (videoView.hasVideo()) {
                Iterator<MScreen.ScreenWindow> iterator = screens.iterator();
                while (iterator.hasNext()) {
                    MScreen.ScreenWindow screen = iterator.next();
                    if (videoView.getVideoId().equals(screen.getId())) {
                        iterator.remove();
                        break;
                    }
                }
            }
        }
        if (screens.size() == 0) {
            return null;
        }
        return screens.get(0);
    }

    private void userLeaveRefresh(User user) {
        if (user == null) {
            return;
        }

//        for (T3VideoView videoView : videoViewList) {
//            if (user.getUserId().equals(videoView.getVideoParams().getUserId())) {
//                videoView.recoverVideoParams();
//                videoView.recoverBackground();
//                detachRender(videoView);
//                return;
//            }
//        }

//        Log.w("Log", "list_01-> " + videoViewList.toString());
        int index = -1;

        List<VideoParams> videoParamsList = new ArrayList<>();
        List<Bitmap> bitmapList = new ArrayList<>();

        for (T3VideoView videoView : videoViewList) {
            if (user.getUserId().equals(videoView.getVideoParams().getUserId())) {
                index = videoViewList.indexOf(videoView);
                videoView.recoverVideoParams();
                videoView.recoverBackground();
                continue;
            }

            if (index != -1) {
                bitmapList.add(videoView.getImageBitmap());
                videoParamsList.add(videoView.getVideoParams().clone());
                detachRender(videoView);
                videoView.recoverVideoParams();
                videoView.recoverBackground();
            }
        }

        if (videoParamsList.size() == 0) {
            return;
        }

        T3VideoView removeVideoView = videoViewList.get(videoViewList.size() - 1);
        detachRender(removeVideoView);
        removeVideoView.recoverVideoParams();
        removeVideoView.recoverBackground();

        for (int i = 0; i < videoParamsList.size(); i++) {
            VideoParams videoParams = videoParamsList.get(i);
            if (!StringUtils.isNotEmpty(videoParams.getUserId())) {
                break;
            }
            T3VideoView videoView = videoViewList.get(index + i);
            videoView.setVideoParams(videoParams);
            videoView.setImageBitmap(bitmapList.get(i));

            String deviceId = videoParams.getDeviceId();    // TODO: 2020/8/6  videoParams.getDeviceId() 不再用于记录渲染的视频id，获取已渲染视频id使用 videoView.getVideoId()！
            if (StringUtils.isNotEmpty(deviceId)) {
                attachRender(videoView, deviceId);
            }
        }
    }

    private void releaseVideo() {
        if (videoViewList.isEmpty()) {
            return;
        }
        for (T3VideoView videoView : videoViewList) {
            if (videoView.hasVideo()) {
                detachRender(videoView);
            }
            if (isEnableReleaseVideoView) {
                Tlog.i(tag, "MeetingManager dispose videoView: " + videoView);
                videoView.dispose();
            }
        }
        videoViewList.clear();
    }

    /**
     * 设置本地视频发布码流区间（单位bps）
     * @param minBitrate 最小码流值
     * @param maxBitrate 最大码流值
     * @return
     */
    public MeetingManager setVideoBitrateWithBps(int minBitrate, int maxBitrate) {
        this.maxBitrate = maxBitrate;
        this.minBitrate = minBitrate;
        return this;
    }

    /**
     * 设置视频相关事件监听
     * @param listener
     */
    public void setMVideoListener(MVideo.Listener listener) {
        if (mVideo == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        mVideo.setListener(listener);
    }

    /**
     * 设置用户事件监听
     * @param listener
     */
    public void setMeetingManagerListener(MUserManager.Listener listener) {
        if (userManager == null) {
            Log.w(tag, initErrorMsg);
            return;
        }
        userManager.setListener(listener);
    }

    public MeetingManager setDefaultCamera(MVideo.CameraType type) {
        if (type == MVideo.CameraType.unknow) {
            type = MVideo.CameraType.front;
        }
        this.defaultCamera = type;
        return this;
    }

    public boolean isEnableAutoSub() {
        return enableAutoSub;
    }

    public List<User> getAutoSubUserList() {
        return autoSubUserList;
    }

    public void setAutoSubUserList(List<User> autoSubUserList) {
        synchronized (autoSubUserList) {
            this.autoSubUserList = autoSubUserList;
        }
    }

    public void addAutoSubUser(User user) {
        synchronized (autoSubUserList) {
            autoSubUserList.add(user);
        }
    }

    public void removeAutoSubUser(User user) {
        synchronized (autoSubUserList) {
            autoSubUserList.remove(user);
        }
    }

    /**
     * 配置是否自动渲染视频（包括共享屏幕）
     * @param enableAutoSub
     * @return
     */
    public MeetingManager setEnableAutoSub(boolean enableAutoSub) {
        this.enableAutoSub = enableAutoSub;
        return this;
    }

    /**
     * 设置绑定视频的类型：绑定用户和绑定设备
     * @param bindType 绑定类型
     */
    public MeetingManager setBindType(BindType bindType) {
        this.bindType = bindType;
        return this;
    }

    /**
     * 获取视频Video类实例
     * @return
     */
    public MVideo getMVideo() {
        return mVideo;
    }

    /**
     * 获取视频Video类实例
     * @return
     */
    public MScreen getScreen() {
        return mScreen;
    }

    /**
     * 获取音频Audio类实例
     * @return
     */
    public MAudio getMAudio() {
        return mAudio;
    }

    /**
     * 获取用户处理类实例
     * @return
     */
    public MUserManager getMUserManager() {
        return userManager;
    }

    /**
     * 获取房间用户列表，第一个元素固定是自己。
     */
    public List<User> getMeetingUsers() {
        List<User> userList = new ArrayList<>();
        if (userManager != null) {
            userList.add(userManager.getSelfUser());
            userList.addAll(userManager.getParticipants(0, userManager.getParticipantsCount()));
        }
        return userList;
    }

    /**
     * 获取userId用户发布的视频列表（摄像头 MVideo.Camera、桌面 MScreen.ScreenWindow）
     * @param userId 用户id号
     */
    public List<VideoDevice> getDevicesByUserId(String userId) {
        List<VideoDevice> deviceList = new ArrayList<>();
        if (isMeetingInit && !TextUtils.isEmpty(userId)) {
            List<MVideo.Camera> cameraList = mVideo.getPublishedCameras();
            List<MScreen.ScreenWindow> screenWindowList = mScreen.getPublishedScreens();

            for (MVideo.Camera camera : cameraList) {
                if (userId.equals(userManager.getOwnerId(camera.getId()))) {
                    deviceList.add(camera);
                }
            }

            for (MScreen.ScreenWindow screen : screenWindowList) {
                if (userId.equals(userManager.getOwnerId(screen.getId()))) {
                    deviceList.add(screen);
                }
            }
        }

        return deviceList;
    }

    public MeetingManager keepScreenOn(Activity activity) {
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        return this;
    }

    public MWhiteboard getWhiteboard() {
        return mWhiteboard;
    }

    public boolean initWhiteboard(boolean showToolbar, WhiteboardView postBindView) {
        Room room = RoomManager.getInstance().getRoom();
        if (room == null) {
            return false;
        }
        mWhiteboard = MWhiteboard.getWhiteboard(room);
        if (mWhiteboard == null) {
            return false;
        }
        mWhiteboard.showToolbar(showToolbar)
                .onAttachView(postBindView);
        return true;
    }

    public boolean initWhiteboard(boolean showToolbar, WhiteboardView postBindView, MWhiteboard.Listener listener) {
        Room room = RoomManager.getInstance().getRoom();
        if (room == null) {
            return false;
        }
        mWhiteboard = MWhiteboard.getWhiteboard(room);
        if (mWhiteboard == null) {
            return false;
        }
        mWhiteboard.showToolbar(showToolbar)
                .onAttachView(postBindView)
                .setListener(listener);
        return true;
    }

    public void createWhiteboard(String title, String desc, int color) {
        if (mWhiteboard == null) {
            return;
        }
        mWhiteboard.createLocalWhiteboard(title, desc, color);
    }

    public void closeWhiteboard() {
        if (mWhiteboard == null) {
            return;
        }
        mWhiteboard.closeLocalBoard();
    }

    /**
     * 资源释放
     */
    @Override
    public void dispose() {
        Tlog.w(TAG, "dispose(): videoViewList.size = " + videoViewList.size());
        if (disposable != null) {
            disposable.dispose();
            disposable = null;
        }
        deviceMap.clear();
        isCameraBusying = false;
        releaseVideo();
        mVideo = null;
        mScreen = null;
        mAudio = null;
        userManager = null;
        defaultCamera = MVideo.CameraType.front;
        isMeetingInit = false;
        activity = null;
        hasVideoFree = false;
    }

    public static class VideoParams implements Cloneable {
        private String userId;
        private String userName;
        private String deviceId;
        private DeviceType deviceType;

        public enum DeviceType {
            UNKNOWN,
            CAMERA,
            SCREEN
        }

        public VideoParams(String userId, String userName) {
            this.userId = userId;
            this.userName = userName;
            this.deviceType = DeviceType.CAMERA;
        }

        public VideoParams(String userId, String userName, String deviceId) {
            this.userId = userId;
            this.userName = userName;
            this.deviceId = deviceId;
            this.deviceType = DeviceType.CAMERA;
        }

        public VideoParams(String userId, String userName, String deviceId, DeviceType deviceType) {
            this.userId = userId;
            this.userName = userName;
            this.deviceId = deviceId;
            this.deviceType = deviceType;
        }

        public String getUserId() {
            return userId;
        }

        public void setUserId(String userId) {
            this.userId = userId;
        }

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public String getDeviceId() {
            return deviceId;
        }

        public void setDeviceId(String deviceId) {
            this.deviceId = deviceId;
        }

        public DeviceType getDeviceType() {
            return deviceType;
        }

        public void setDeviceType(DeviceType deviceType) {
            this.deviceType = deviceType;
        }

        public VideoParams clone() {
            VideoParams clone = null;
            try {
                clone = (VideoParams) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return clone;
        }

        @Override
        public String toString() {
            return "VideoParams{" +
                    "userId='" + userId + '\'' +
                    ", userName='" + userName + '\'' +
                    ", deviceId='" + deviceId + '\'' +
                    ", deviceType=" + deviceType +
                    '}';
        }
    }

    public static class CallbackParams {
        private int result;
        private String fromId;

        public CallbackParams(int result, String fromId) {
            this.result = result;
            this.fromId = fromId;
        }

        public int getResult() {
            return result;
        }

        public void setResult(int result) {
            this.result = result;
        }

        public String getFromId() {
            return fromId;
        }

        public void setFromId(String fromId) {
            this.fromId = fromId;
        }
    }

    public interface CameraCallback {
        void success(boolean isOpen);

        void error(int errorCode);
    }

    public interface MicrophoneCallback {
        void success(boolean isOpen);

        void error(int errorCode);
    }

    public interface SpeakerCallback {
        void success(boolean isOpen);

        void error(String errorCode);
    }

    public interface UsbCameraCallback {
        void success(boolean isOpen);

        void error(int errorCode);
    }

    public interface SetupCallback {
        void success(String code);

        void error(String code);
    }

}
