package com.videogo.openapi;

import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.text.TextUtils;

import com.ez.stream.EZStreamClientManager;
import com.ezviz.sdk.configwifi.touchAp.GetAccessDeviceInfoCallback;
import com.ezviz.sdk.configwifi.touchAp.GetDeviceWifiListCallback;
import com.ezviz.sdk.configwifi.touchAp.GetTokenCallback;
import com.ezviz.sdk.configwifi.touchAp.QueryPlatformBindStatusCallback;
import com.ezviz.sdk.configwifi.touchAp.StartNewApConfigCallback;
import com.hikvision.wifi.configuration.DeviceDiscoveryListener;
import com.videogo.auth.EZAuthApi;
import com.videogo.stream.EZDeviceStreamDownload;
import com.videogo.bandwidthcheck.EZBWCheckManager;
import com.videogo.util.LocalInfo;
import com.videogo.constant.Config;
import com.videogo.constant.Constant;
import com.videogo.device.DeviceManager;
import com.videogo.errorlayer.ErrorLayer;
import com.videogo.errorlayer.ErrorOpenSDKProxy;
import com.videogo.exception.BaseException;
import com.videogo.main.AppManager;
import com.videogo.openapi.EZConstants.EZAlarmStatus;
import com.videogo.openapi.EZConstants.EZMessageStatus;
import com.videogo.openapi.EZConstants.EZMessageType;
import com.videogo.openapi.EZConstants.EZPTZAction;
import com.videogo.openapi.EZConstants.EZPTZCommand;
import com.videogo.openapi.bean.EZAccessToken;
import com.videogo.openapi.bean.EZAlarmInfo;
import com.videogo.openapi.bean.EZCloudRecordFile;
import com.videogo.openapi.bean.EZDeviceInfo;
import com.videogo.openapi.bean.EZDeviceRecordFile;
import com.videogo.openapi.bean.EZDeviceUpgradeStatus;
import com.videogo.openapi.bean.EZDeviceVersion;
import com.videogo.openapi.bean.EZLeaveMessage;
import com.videogo.openapi.bean.EZProbeDeviceInfo;
import com.videogo.openapi.bean.EZProbeDeviceInfoResult;
import com.videogo.openapi.bean.EZStorageStatus;
import com.videogo.openapi.bean.EZUserInfo;
import com.videogo.util.LogUtil;
import com.videogo.util.Utils;
import com.videogo.wificonfig.APWifiConfig;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 开放平台总接口类
 * 如果接口函数调用失败，请仔细查看日志中是否有android.os.NetworkOnMainThreadException异常。EZOpenSDK中的大部分接口都有网络操作，需要在线程中调用。
 *
 * @author xia xingsuo
 * @date 2015-9-16
 */
public class EZOpenSDK {

    // region ------------------------------ 全局变量 ------------------------------

    private static final String TAG = "EZOpenSDK";
    public static String API_URL = "https://open.ys7.com";
    public static String WEB_URL = "https://openauth.ys7.com";

    protected static EZOpenSDK mEZOpenSDK = null;
    protected Application mApplication = null;
    protected EzvizAPI mEzvizAPI = null;
    private EZOpenSDKListener.LogCallback mLogCallback;

    // endregion

    // region ------------------------------ 构造方法 ------------------------------

    @SuppressWarnings("unused")
    private EZOpenSDK() {
    }

    @SuppressWarnings("unused")
    protected EZOpenSDK(Application application, String appKey) {
        LogUtil.i(TAG, "construct EZOpenSDK, version:" + getVersion());
        mEzvizAPI = EzvizAPI.getInstance();
        AppManager.getInstance().getEZSDKConfigurationSyn();
        mApplication = application;
    }

    /**
     * 获取单例对象
     *
     * @return 获取单例对象
     */
    public static EZOpenSDK getInstance() {
        return mEZOpenSDK;
    }

    // endregion

    // region ------------------------------ Auth认证相关Api（通用）------------------------------

    /**
     * @deprecated 不再支持加载外置so库，仅保留形式兼容老版本sdk
     */
    public static boolean initLib(Application application, String appKey, String loadLibraryAbsPath) {
        return initLib(application, appKey);
    }

    /**
     * SDK 初始化
     *
     * @param application 客戶端app的 Application 對象
     * @param appKey      客戶端app申請的app key
     * @return true 表示成功，false 表示失败
     * @since 4.8.0
     */
    public static boolean initLib(Application application, String appKey) {
        LogUtil.i(TAG, "Enter initLib");
        if (TextUtils.isEmpty(appKey)) {
            LogUtil.e(TAG, "initLib EZOpenSDK, appKey is null");
            return false;
        }

        ErrorLayer.setErrorProxy(ErrorOpenSDKProxy.getInstance());

        // 创建LocalInfo对象并初始化
        LocalInfo.init(application, appKey);
        EzvizAPI.init(application, appKey, false);
        EzvizAPI.getInstance().setServerUrl(API_URL, WEB_URL);
        EzvizAPI.getInstance().preSetToken();
        EZAuthApi.platformType = EZPlatformType.EZPlatformTypeOPENSDK;
        mEZOpenSDK = new EZOpenSDK(application, appKey);

        showSDKLog(Config.LOGGING);

        return true;
    }

    /**
     * 切换到指定服务区域
     *
     * @param apiUrl 区域接口请求域名
     * @param webUrl 区域WEB登录域名（默认可不填写）
     */
    public void setServerUrl(String apiUrl, String webUrl) {
        mEzvizAPI.setServerUrl(apiUrl, webUrl);
    }

    /**
     * 释放sdk资源
     */
    public static void finiLib() {
        LogUtil.i(TAG, "FiniLib EZOpenSDK, version:" + getVersion());
        AppManager.getInstance().finiLibs();

        EzvizAPI.getInstance().releaseSDK();
    }

    /**
     * 设置是否打印sdk中logcat日志
     * 建议在debug下设置打印，release下设置不打印
     * 此函数建议在初始化之前调用
     *
     * @param showLog true打印 false不打印
     */
    public static void showSDKLog(boolean showLog) {
        // 控制EzOpenSDK的日志
        Config.LOGGING = showLog;
        // 控制配网sdk的日志
        com.ezviz.sdk.configwifi.Config.LOGGING = showLog;
        // 控制通用库的日志
        if (mEZOpenSDK != null && mEZOpenSDK.mApplication != null) {
            EZStreamClientManager streamClientManager = EZStreamClientManager.create(mEZOpenSDK.mApplication.getApplicationContext());
            streamClientManager.setLogPrintEnable(showLog, false, Config.playCtrlLogLevel);
        }
    }

    /**
     * 设置是否缓存sdk中的码流文件，用于码流调试；打开后，每次取流都会将码流保存到沙盒中
     * 在debug模式下设置开启，release下必须关闭或者删除，否则沙盒中会产生大量的码流文件，占用手机存储空间
     * 视频码流路径：/sdcard/Android/data/应用包名/files/streams
     * 对讲码流路径：/sdcard/Android/data/应用包名/files/talkback
     * 录制码流路径：/sdcard/Android/data/应用包名/cache/0_OpenSDK/Records
     * 此函数建议在初始化之前调用
     *
     * @param enable 是否打开，默认false
     */
     public static void setDebugStreamEnable(boolean enable) {
        Config.STREAMDEBUGGING = enable;
     }

    /**
     * 设置SDK日志回调
     *
     * @since 4.11.0
     */
    public void setLogCallback(EZOpenSDKListener.LogCallback callback) {
        mLogCallback = callback;
        if (mLogCallback != null) {
            LogUtil.setLogCallback(new LogUtil.LogCallback() {
                @Override
                public void onLog(String tag, String msg) {
                    if (mLogCallback != null){
                        mLogCallback.onLog(tag, msg);
                    }
                }
            });
        } else {
            LogUtil.setLogCallback(null);
        }
    }

    /**
     * 登出账号
     * 该接口为耗时操作，必须在线程中调用
     */
    public void logout() {
        mEzvizAPI.logout();
    }

    /**
     * 获取SDK版本
     *
     * @return sdk 版本
     */
    public static String getVersion() {
        return Config.VERSION;
    }

    /**
     * 是否已登录授权
     *
     * @since 4.8.3
     */
    public boolean isLogin() {
        return EZAuthApi.isLogin();
    }

    // endregion

    // region ------------------------------ Auth认证相关Api（AccessToken模式）------------------------------

    /**
     * 授权登录以后给EZOpenSDK设置AccessToken
     *
     * @param accessToken 授权登录获取的accessToken
     */
    public void setAccessToken(String accessToken) {
        mEzvizAPI.setAccessToken(accessToken);
    }

    /**
     * 获取登录token
     *
     * @return token包含2个字段，
     * 一个是token string本身，另外一个表示过期时间，单位为秒
     */
    public EZAccessToken getEZAccessToken() {
        return mEzvizAPI.getEZAccessToken();
    }

    /**
     * 打开授权登录中间页面,用于获取Accesstoken
     */
    public void openLoginPage() {
        mEzvizAPI.gotoLoginPage(false, -1, -1);
    }

    /**
     * 打开授权登录中间页面,用于获取Accesstoken
     *
     * @param flag 页面跳转flag 类似Intent.FLAG_ACTIVITY_CLEAR_TOP
     */
    public void openLoginPage(int flag) {
        mEzvizAPI.gotoLoginPage(false, -1, flag);
    }

    /**
     * 设置登录页的v参数，用于控制登录页样式
     *
     * @param vParam 登录页样式（mobile-移动版、wap-pc版。。。）
     * @since 4.8.7
     */
    public void setVparamForLoginPage(String vParam) {
        mEzvizAPI.setVparamForLoginPage(vParam);
    }

    /**
     * 打开云存储H5页面
     * 仅适用于单通道设备，如IPC，不建议使用
     *
     * @param deviceSerial 设备序列号
     * @throws BaseException 调用失败的异常信息
     * @deprecated 仅保留接口兼容老用户，请尽快换用void openCloudPage(String deviceSerial, int cameraNo)
     */
    public void openCloudPage(String deviceSerial) throws BaseException {
        mEzvizAPI.openCloudPage(deviceSerial, 1);
    }

    /**
     * 打开云存储H5页面
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     camera序号，EZCameraInfo.cameraNo
     * @throws BaseException 调用失败的异常信息
     * @since 4.8.8 增加camera序号
     */
    public void openCloudPage(String deviceSerial, int cameraNo) throws BaseException {
        mEzvizAPI.openCloudPage(deviceSerial, cameraNo);
    }

    /**
     * 修改密码页面
     */
    public void openChangePasswordPage() {
        mEzvizAPI.openChangePasswordPage();
    }

    // endregion

    // region ------------------------------ Auth认证相关Api（小权限TKToken模式）------------------------------

    /**
     * SDK是否使用自己服务器生成的tkToken 代替 accessToken，默认NO；在`initLib`前调用
     * 此开关打开后，必须设置如下token，否则将影响各个功能的使用
     * @see EZOpenSDK#setHttpToken(String) 设置非设备类小权限token（入参不含设备序列号、通道号的接口会使用httpToken）
     * @see EZOpenSDK#setDeviceToken(String, String) 设置设备类小权限token（入参含设备序列号、不包含通道号的接口会使用deviceToken）
     * @see EZOpenSDK#setDeviceToken(String, int, String, String) 设置设备类通道级小权限token（入参包含设备序列号、通道号的接口会使用deviceGlobalToken 和 deviceVideoToken）
     * @see EZPlayer#setStreamToken(String) 预览、对讲、SD卡录像回放设置取流小权限token；云存储录像回放使用的是ticket认证方式，SDK内部实现，无需设置小权限token
     * @see EZDeviceStreamDownload#setStreamToken(String) SD卡录像下载设置取流小权限token
     *
     * 此开关打开后，以下接口不能使用
     * @see EZOpenSDK#setAccessToken(String)
     * @see EZOpenSDK#getEZAccessToken()
     * @see EZOpenSDK#openLoginPage()
     * @see EZOpenSDK#openCloudPage(String, int)
     * @see EZOpenSDK#openChangePasswordPage()
     *
     * @param enable 是否使用自己服务器生成的tkToken
     */
    public static void enableSDKWithTKToken(boolean enable) {
        Config.ENABLE_SDK_TKTOKEN = enable;
    }

    /**
     * SDK是否使用了小权限token模式
     * @return
     */
    public static boolean isEnableSDKWithTKToken() {
        return Config.ENABLE_SDK_TKTOKEN;
    }

    /**
     * 给EZOpenSDK设置非设备类小权限token
     *
     * @param httpToken 非设备类小权限token
     */
    public void setHttpToken(String httpToken) {
        mEzvizAPI.setHttpToken(httpToken);
    }

    /**
     * 给EZOpenSDK设置【设备类】小权限token
     *
     * @param deviceSerial  设备序列号
     * @param deviceToken   设备类类型小权限token（action=* resourceCategory=Global channelNo=*生成的小权限token）
     */
    public void setDeviceToken(String deviceSerial, String deviceToken) {
        mEzvizAPI.setDeviceToken(deviceSerial, deviceToken);
    }

    /**
     * 给EZOpenSDK设置【设备类通道级别】小权限token
     *
     * @param deviceSerial  设备序列号
     * @param cameraNo      设备通道号
     * @param deviceGlobalToken 设备类Global类型小权限token（action=* resourceCategory=Global channelNo=对应通道号 生成的小权限token）
     * @param deviceVideoToken  设备类Video类型小权限token（action=* resourceCategory=Video channelNo=对应通道号 生成的小权限token）
     */
    public void setDeviceToken(String deviceSerial, int cameraNo, String deviceGlobalToken, String deviceVideoToken) {
        mEzvizAPI.setDeviceToken(deviceSerial, cameraNo, deviceGlobalToken, deviceVideoToken);
    }

    // endregion

    // region ------------------------------ 播放器创建Api ------------------------------

    /**
     * 根据deviceSerial 和 cameraNo 构造EZPlayer对象
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     通道号
     * @return EZPlayer对象
     */
    public EZPlayer createPlayer(final String deviceSerial, int cameraNo) {
        return mEzvizAPI.createPlayer(deviceSerial, cameraNo);
    }

    /**
     * 根据deviceSerial 和 cameraNo 和 useSubStream构造EZPlayer对象
     * 一个页面存在多个视频使用最小的码流，没有子码流的话还是使用主码流
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     通道号
     * @param useSubStream 是否使用子码流
     * @return EZPlayer对象
     * @since V4.19.2
     */
    public EZPlayer createPlayer(final String deviceSerial, int cameraNo, boolean useSubStream) {
        return mEzvizAPI.createPlayerWithDeviceSerial(deviceSerial, cameraNo, useSubStream);
    }

    /**
     * 根据视频url构造EZPlayer对象，用于通过视频url进行播放
     *
     * @param url 视频url
     * @return EZPlayer对象
     */
    public EZPlayer createPlayerWithUrl(final String url) {
        return mEzvizAPI.createPlayerWithUrl(url);
    }

    /**
     * 创建局域网播放器
     *
     * @param userId     设备登录成功后的userId
     * @param cameraNo   设备预览的通道号
     * @param streamType 设备预览的码流类型 1:主码流;2:子码流
     * @return
     */
    public EZPlayer createPlayerWithUserId(int userId, int cameraNo, int streamType) {
        return mEzvizAPI.createPlayerWithUserId(userId, cameraNo, streamType);
    }

    /**
     * 释放  EZPlayer 对象
     *
     * @param player 将要释放的EZPlayer对象
     */
    public void releasePlayer(EZPlayer player) {
        mEzvizAPI.releasePlayer(player);
    }

    // endregion

    // region ------------------------------ 取流相关Api ------------------------------

    /**
     * 设置是否支持内网直连取流，默认是支持的
     * 内网直连取流可以降低转发取流的比例，当客户端和设备处于同一局域网下时，优先走内网直连
     *
     * @param bEnable 内网直连是否开启
     */
    public static void enableDirectInner(boolean bEnable) {
        Config.ENABLE_DirectInner = bEnable;
    }

    /**
     * 设置是否支持P2P取流，默认是不支持的
     * P2P取流可以降低转发取流的比例，但是在多人同时观看时对设备端的带宽要求也更高，设备端带宽不够的情况下，影响播放体验
     * 对于可能多人同时观看的场景，建议不支持
     * 对于家用监控类的场景，建议支持P2P
     * 此函数可以在任意时刻调用
     *
     * @param bEnable true-支持p2p false-不支持p2p
     */
    public static void enableP2P(boolean bEnable) {
        Config.ENABLE_P2P = bEnable;
    }

    /**
     * 设置是否支持流媒体取流，默认是支持的
     * 如果禁用，只能通过内网直连方式方式进行取流；若开启了支持p2p取流，会再尝试p2p取流；关闭后会严重影响取流成功率，慎用！
     * @param bEnable true-支持流媒体 false-不支持流媒体
     */
    public static void enableVTDU(boolean bEnable) {
        Config.ENABLE_VTDU = bEnable;
    }

    /**
     * 清除取流时的缓存数据
     *
     * @since 4.5.0
     */
    public void clearStreamInfoCache() {
        mEzvizAPI.clearStreamInfoCache();
    }

    /**
     * 获取所有的p2p预连接设备序列号（包括正在进行预操作的以及预操作完成的）
     *
     * @return 设备序列号数组
     * @since 4.19.6
     */
    public ArrayList<String> getAllProcessedPreconnectSerials () {
        return EZStreamClientManager.create(mEZOpenSDK.mApplication.getApplicationContext()).getAllProcessedPreconnectSerials();
    }

    /**
     * 获取所有正在排队的p2p预连接设备序列号（指还没有进行预操作的）
     *
     * @return 设备序列号数组
     * @since 4.19.6
     */
    public ArrayList<String> getAllToDoPreconnectSerials () {
        return EZStreamClientManager.create(mEZOpenSDK.mApplication.getApplicationContext()).getAllToDoPreconnectSerials();
    }

    /**
     * 某一设备p2p预连接是否成功
     *
     *  @param deviceSerial 设备序列号
     */
    public boolean isPreConnectionSucceed(String deviceSerial) {
        return EZStreamClientManager.create(mEZOpenSDK.mApplication.getApplicationContext()).isPreConnectionSucceed(deviceSerial);
    }

    /**
     * 对某一设备进行p2p预连接操作
     *
     * @since 4.19.6
     */
    public void startP2PPreconnect (final String deviceSerial) {
        if (deviceSerial == null || deviceSerial.length() == 0) {
            LogUtil.e(TAG, "startP2PPreconnect error: deviceSerial is null.");
            return;
        }
        DeviceManager.getInstance().startPreConnect(deviceSerial);
    }

    /**
     * 对某一设备进行p2p预连接清除操作
     *
     * @since 4.19.6
     */
    public void clearP2PPreconnect (final String deviceSerial) {
        if (deviceSerial == null || deviceSerial.length() == 0) {
            LogUtil.e(TAG, "clearP2PPreconnect error: deviceSerial is null.");
            return;
        }
        EZStreamClientManager.create(mEZOpenSDK.mApplication.getApplicationContext()).clearPreconnectInfo(deviceSerial);
    }

    /**
     * 设置取流url扩展字段
     *
     * @param map 扩展参数
     */
    public void setExtendStreamParam(Map<String, String> map) {
        mEzvizAPI.setExtendStreamParam(map);
    }

    // endregion

    // region ------------------------------ 录像查询Api ------------------------------

    /**
     * 查询云存储录像信息列表
     * 该接口中有网络操作，需要在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     camera的序号，EZCameraInfo.cameraNo
     * @param startTime    查询时间范围开始时间
     * @param endTime      查询时间范围结束时间
     * @return 云存储录像信息列表
     * @throws BaseException 操作失败的异常信息
     */
    public List<EZCloudRecordFile> searchRecordFileFromCloud(String deviceSerial, int cameraNo, Calendar startTime, Calendar endTime)
            throws BaseException {
        return mEzvizAPI.searchRecordFileFromCloudEx(deviceSerial, cameraNo, startTime, endTime);
    }

//    /**
//     * 查询云存储录像信息列表
//     * （相比于searchRecordFileFromCloud，可以额外获取连续云存储录像）
//     * 该接口中有网络操作，需要在线程中调用
//     *
//     * @param deviceSerial 设备序列号
//     * @param cameraNo     camera的序号，EZCameraInfo.cameraNo
//     * @param startTime    查询时间范围开始时间
//     * @param endTime      查询时间范围结束时间
//     * @return 云存储录像信息列表
//     * @throws BaseException 操作失败的异常信息
//     */
//    public List<EZCloudRecordFile> searchRecordFileFromCloudEx(String deviceSerial, int cameraNo, Calendar startTime, Calendar endTime)
//            throws BaseException {
//        return mEzvizAPI.searchRecordFileFromCloud(deviceSerial, cameraNo,startTime,endTime);
//    }

    /**
     * 查询远程SD卡存储录像信息列表
     * 当设备关联了NVR存储设备后，此时查找的是NVR设备中录像，不再提供查找设备SD卡中录像
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     camera的序号，EZCameraInfo.cameraNo
     * @param startTime    查询时间范围开始时间
     * @param endTime      查询时间范围结束时间
     * @return 远程SD卡存储录像信息列表
     * @throws BaseException
     */
    public List<EZDeviceRecordFile> searchRecordFileFromDevice(String deviceSerial, int cameraNo, Calendar startTime, Calendar endTime)
            throws BaseException {
        return mEzvizAPI.searchRecordFileFromDevice(deviceSerial, cameraNo, startTime, endTime);
    }

    /**
     * 查询远程SD卡存储录像信息列表
     * 当设备关联了NVR存储设备后，此时查找的是NVR设备中录像，不再提供查找设备SD卡中录像
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial    设备序列号
     * @param cameraNo        camera的序号，EZCameraInfo.cameraNo
     * @param startTime       查询时间范围开始时间
     * @param endTime         查询时间范围结束时间
     * @param videoRecordType 录像类型
     * @return 远程SD卡存储录像信息列表
     * @throws BaseException
     */
    public List<EZDeviceRecordFile> searchRecordFileFromDevice(String deviceSerial, int cameraNo, Calendar startTime, Calendar endTime, EZConstants.EZVideoRecordType videoRecordType)
            throws BaseException {
        return mEzvizAPI.searchRecordFileFromDevice(deviceSerial, cameraNo, startTime, endTime, videoRecordType.recordType);
    }

    /**
     * 查询远程SD卡录像信息列表（支持获取浓缩录像）
     * 当设备关联了NVR存储设备后，此时查找的是NVR设备中录像，不再提供查找设备SD卡中录像
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial    设备序列号
     * @param cameraNo        camera的序号，EZCameraInfo.cameraNo
     * @param startTime       查询时间范围开始时间
     * @param endTime         查询时间范围结束时间
     * @param videoRecordTypeEx 录像类型
     * @return 远程SD卡存储录像信息列表
     * @throws BaseException
     * @since v5.8
     */
    public List<EZDeviceRecordFile> searchRecordFileFromDeviceEx(String deviceSerial, int cameraNo, Calendar startTime, Calendar endTime, EZConstants.EZVideoRecordTypeEx videoRecordTypeEx)
            throws BaseException {
        return mEzvizAPI.searchRecordFileFromDeviceEx(deviceSerial, cameraNo, startTime, endTime, String.valueOf(videoRecordTypeEx.recordType));
    }

    /**
     * 获取CVR中心录像文件列表
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     相机通道号
     * @param startTime    录像开始时间
     * @param endTime      录像结束时间
     * @return CVR中心录像文件列表
     * @throws BaseException 操作失败的异常
     */
    public List<EZDeviceRecordFile> searchRecordFileFromCVR(String deviceSerial, int cameraNo, Calendar startTime,
                                                            Calendar endTime) throws BaseException {
        return mEzvizAPI.searchRecordFileFromCVR(deviceSerial, cameraNo, startTime, endTime);
    }

    /**
     *  查询设备SDK云录制录像信息列表接口
     *
     *  @param deviceSerial 设备序列号
     *  @param cameraNo     通道号，传入<=0的值则为默认值
     *  @param startTime    开始时间，传入nil则为当天00:00:00
     *  @param endTime      结束时间，传入nil则为当天23:59:59
     *  @param spaceId      录像空间
     */
    public List<EZCloudRecordFile> searchRecordFileFromSDKCloud(String deviceSerial, int cameraNo, Calendar startTime, Calendar endTime, String spaceId)
            throws BaseException {
        return mEzvizAPI.searchRecordFileFromSDKCloud(deviceSerial, cameraNo, startTime, endTime, spaceId);
    }

    // endregion

    // region ------------------------------ 设备相关Api ------------------------------

    /**
     * 添加设备
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param verifyCode   设备验证码，验证码位于设备机身上，6位大写字母
     * @return true 表示成功， false 表示失败
     * @throws BaseException
     */
    public boolean addDevice(String deviceSerial, String verifyCode) throws BaseException {
//        return mEzvizAPI.addDeviceBySerial(deviceSerial, deviceCode);
        return mEzvizAPI.addDeviceBySerialNonTransfer(deviceSerial, verifyCode);
    }

    /**
     * 获取用户的设备列表，返回EZDeviceInfo的对象数组，只提供设备基础数据
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param pageIndex 查询页index，从0开始
     * @param pageSize  每页数量（建议20以内）
     * @return 返回的EZDeviceInfo
     * @throws BaseException
     */
    public List<EZDeviceInfo> getDeviceList(int pageIndex, int pageSize) throws BaseException {
        return mEzvizAPI.getDeviceList(pageIndex, pageSize, false);
    }

    /**
     * 获取用户的设备列表，返回EZDeviceInfo的对象数组，只提供设备基础数据（包含子设备）
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param pageIndex 查询页index，从0开始
     * @param pageSize  每页数量（建议20以内）
     * @return 返回的EZDeviceInfo
     * @throws BaseException
     */
    public List<EZDeviceInfo> getDeviceListEx(int pageIndex, int pageSize) throws BaseException {
        return mEzvizAPI.getDeviceList(pageIndex, pageSize, true);
    }

    /**
     * 获取好友分享的设备列表，返回EZDeviceInfo的对象数组
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param pageIndex 分页起始页，从0开始
     * @param pageSize  分页每页数量（建议20以内）
     * @return
     * @throws BaseException
     * @since 4.2
     */
    public List<EZDeviceInfo> getSharedDeviceList(int pageIndex, int pageSize) throws BaseException {
        return mEzvizAPI.getSharedDeviceList(pageIndex, pageSize);
    }

    /**
     * 获取用户设备和托管设备列表
     * (接口暂未启用,请勿调用)
     *
     * @param pageIndex  分页起始页，从0开始
     * @param ezpageSize 分页每页数量（建议20以内）
     * @throws BaseException 报错信息
     */
    public List<EZDeviceInfo> getTrustDeviceList(final int pageIndex, final int ezpageSize) throws BaseException {
        return mEzvizAPI.getTrustDeviceList(pageIndex, ezpageSize);
    }

    /**
     * 获取单个设备信息
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @return 设备信息对象EZDeviceInfo，与getDeviceList中对象一致
     * @throws BaseException
     * @since 4.3
     */
    public EZDeviceInfo getDeviceInfo(String deviceSerial) throws BaseException {
        return mEzvizAPI.getDeviceInfo(deviceSerial, false);
    }

    /**
     * 获取单个设备信息（包含子设备）
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @return 设备信息对象EZDeviceInfo，与getDeviceList中对象一致
     * @throws BaseException
     * @since 5.7
     */
    public EZDeviceInfo getDeviceInfoEx(String deviceSerial) throws BaseException {
        return mEzvizAPI.getDeviceInfo(deviceSerial, true);
    }

    /**
     * 获取设备的版本信息接口
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @return 版本号EZDeviceVersion对象
     * @throws BaseException
     */
    public EZDeviceVersion getDeviceVersion(String deviceSerial) throws BaseException {
        return mEzvizAPI.getDeviceVersion(deviceSerial);
    }

    /**
     * 设备视频图片加解密开关接口
     * 该接口为耗时操作，必须在线程中调用
     * As of v4.2.0, replaced setDeviceEncryptStatusEx
     *
     * @param deviceSerial 设备序列号
     * @param validateCode 设备验证码
     * @param encrypt      是否加密 1加密，0不加密
     * @return true成功，false失败
     * @throws BaseException
     */
    public boolean setDeviceEncryptStatus(String deviceSerial, String validateCode, boolean encrypt) throws BaseException {
        return mEzvizAPI.setDeviceEncryptStatus(deviceSerial, validateCode, encrypt);
    }

    /**
     * 修改设备名称接口
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param deviceName   修改后的设备名称
     * @return true 表示成功， false 表示失败
     * @throws BaseException
     */
    public boolean setDeviceName(String deviceSerial, String deviceName) throws BaseException {
        return mEzvizAPI.updateDeviceName(deviceSerial, deviceName);
    }

    /**
     * 删除当前账号的设备
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号, 9位数字
     * @return true 表示成功， false 表示失败
     * @throws BaseException
     */
    public boolean deleteDevice(String deviceSerial) throws BaseException {
//        return mEzvizAPI.deleteDeviceBySerial(deviceSerial);
        return mEzvizAPI.deleteDeviceNonTransfer(deviceSerial);
    }

    /**
     * 获取存储介质状态(如是否初始化，格式化进度等)
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 需要获取的设备序列号
     * @return 存储状态(EZStorageStatus)列表，列表每个item代表一个磁盘分区
     * @throws BaseException
     */
    public List<EZStorageStatus> getStorageStatus(String deviceSerial) throws BaseException {
        return mEzvizAPI.getStorageStatus(deviceSerial);
    }

    /**
     * 格式化分区(SD卡)
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial   需要格式化的设备序列号
     * @param partitionIndex getStorageStatus查询返回的分区号
     * @return true 表示成功， false 表示失败
     * @throws BaseException
     */
    public boolean formatStorage(String deviceSerial, int partitionIndex) throws BaseException {
        return mEzvizAPI.formatStorage(deviceSerial, partitionIndex);
    }

    /**
     * 获取设备升级状态
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @return EZDeviceUpgradeStatus对象
     */
    public EZDeviceUpgradeStatus getDeviceUpgradeStatus(String deviceSerial) throws BaseException {
        return mEzvizAPI.getDeviceUpgradeStatus(deviceSerial);
        //        return mEzvizAPI.getDeviceUpgradeStatus_stub(deviceSerial);
    }

    /**
     * 开始升级设备固件
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @throws BaseException
     */
    public void upgradeDevice(String deviceSerial) throws BaseException {
        mEzvizAPI.upgradeDevice(deviceSerial);
    }

    /**
     * 获取摄像头实时图片的url接口，需要设备支持，萤石设备一般都能支持此协议。(该功能和萤石云视频app首页刷新封面的功能一致)
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     通道号
     * @return 图片url
     * @throws BaseException replaced by getRealPicture and capturePicture
     */
    public String captureCamera(String deviceSerial, int cameraNo) throws BaseException {
        return mEzvizAPI.capturePicture(deviceSerial, cameraNo);
    }

    /**
     * 设置指定监控点视频清晰度
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     设备通道号，默认为1
     * @param videoLevel   清晰度 0-流畅，1-均衡，2-高清，3-超清，4-极清，5-3K，6-4K
     * @return
     * @throws BaseException
     * @since 4.2
     */
    public boolean setVideoLevel(String deviceSerial, int cameraNo, int videoLevel) throws BaseException {
        return mEzvizAPI.setDeviceVideoLevel(deviceSerial, cameraNo, videoLevel);
    }

    /**
     * 设置指定监控点视频清晰度(非强制，两个及以上客户端同时在取流时无法设置，会返回失败)
     * 录制过程中不能调用此接口，否则会导致录制视频异常
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     设备通道号，默认为1
     * @param videoLevel   清晰度 0-流畅，1-均衡，2-高清，3-超清，4-极清，5-3K，6-4K
     * @return
     * @throws BaseException
     */
    public boolean setVideoLevelAuto(String deviceSerial, int cameraNo, int videoLevel) throws BaseException {
        return mEzvizAPI.setDeviceVideoLevelAuto(deviceSerial, cameraNo, videoLevel);
    }

    /**
     * 设备设置布防状态，兼容A1和IPC设备的布防
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param defence      布防状态, 摄像机布防状态只有0和1，告警器有0:睡眠 8:在家 16:外出
     * @return 设备设置布防状态是否成功
     * @throws BaseException
     */
    public boolean setDefence(String deviceSerial, EZConstants.EZDefenceStatus defence) throws BaseException {
        return mEzvizAPI.setDefence(deviceSerial, defence);
    }

    /**
     * 刷新设备详细信息（修改验证码后调用）
     * 接口中有网络操作，需要在线程中调用
     *
     * @param deviceSerial
     * @param cameraNo
     * @throws BaseException
     */
    public void refreshDeviceDetailInfo(String deviceSerial, int cameraNo) throws BaseException {
        mEzvizAPI.refreshDeviceDetailInfoEx(deviceSerial, cameraNo);
    }

    // endregion

    // region ------------------------------ 云台控制Api ------------------------------

    /**
     * PTZ 控制接口
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     通道号
     * @param command      ptz控制命令
     * @param action       控制启动/停止
     * @param speed        速度（0-2）
     * @return 操作成功或者失败(返回失败错误码)
     */
    public boolean controlPTZ(String deviceSerial, int cameraNo, EZPTZCommand command, EZPTZAction action, int speed) throws BaseException {
        return mEzvizAPI.controlPTZ(deviceSerial, cameraNo, command, action, speed);
    }

    /**
     * PTZ 控制接口，支持8档速率，更细化
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     通道号
     * @param command      ptz控制命令
     * @param action       控制启动/停止
     * @param newSpeed     速度（0-7）
     * @return 操作成功或者失败(返回失败错误码)
     */
    public boolean controlPTZEx(String deviceSerial, int cameraNo, EZPTZCommand command, EZPTZAction action, int newSpeed) throws BaseException {
        return mEzvizAPI.controlPTZEx(deviceSerial, cameraNo, command, action, newSpeed, "");
    }

    /**
     * PTZ 通过p2p服务+http双通道控制云台，设备响应先到达的指令，响应更快（推荐）
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     通道号
     * @param command      ptz控制命令
     * @param action       控制启动/停止
     * @param newSpeed     速度（0-7）
     * @return 操作成功或者失败(返回失败错误码)
     */
    public boolean controlPTZMix(final String deviceSerial, final int cameraNo, final EZPTZCommand command, final EZPTZAction action, final int newSpeed) throws BaseException {
        return mEzvizAPI.controlPTZMix(deviceSerial, cameraNo, command, action, newSpeed);
    }

    /**
     * PTZ 通过p2p服务控制云台（需要设备支持）
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     通道号
     * @param command      ptz控制命令
     * @param action       控制启动/停止
     * @param newSpeed     速度（0-7）
     * @return 操作成功或者失败(返回失败错误码)
     */
    public boolean controlPTZViaP2P(String deviceSerial, int cameraNo, EZPTZCommand command, EZPTZAction action, int newSpeed) throws BaseException {
        return mEzvizAPI.controlPTZViaP2P(deviceSerial, cameraNo, command, action, newSpeed, "");
    }

    /**
     * 镜像 控制设备视频画面翻转。在摄像头倒装的时候，调用此接口可让画面正过来
     * 需要设备支持
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号
     * @param cameraNo     camera的序号，EZCameraInfo.cameraNo
     * @param command      ptz控制命令
     * @throws BaseException exception 操作失败会抛出异常，错误码 exception.getErrorCode
     * @deprecated 建议用http方式实现，使用说明请参考https://open.ys7.com/doc/zh/book/index/device_ptz.html#device_ptz-api3
     */
    public void controlVideoFlip(String deviceSerial, int cameraNo, EZConstants.EZPTZDisplayCommand command) throws BaseException {
        mEzvizAPI.controlVideoFlip(deviceSerial, cameraNo, command);
    }

    // endregion

    // region ------------------------------ 告警相关Api ------------------------------

    /**
     * 获取告警信息列表
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号，为null时查询整个账户下的告警信息列表
     * @param pageIndex    查询页index，从0开始
     * @param pageSize     每页数量（建议20以内）
     * @param beginTime    搜索时间范围开始时间，开始时间和结束时间可以同时为空
     * @param endTime      搜索时间范围结束时间
     * @return EZAlarmInfo对象列表
     * @throws BaseException
     */
    public List<EZAlarmInfo> getAlarmList(String deviceSerial, int pageIndex, int pageSize,
                                          Calendar beginTime, Calendar endTime) throws BaseException {
        return mEzvizAPI.getAlarmList(deviceSerial, pageIndex, pageSize,
                beginTime, endTime);
    }

    /**
     * 解密数据，设备加密，该接口可以用于解密告警图片
     *
     * @param inputData  解密前数据
     * @param verifyCode 密码，设备加密时通常为设备验证码，平台加密时为平台返回的checkSum
     */
    public byte[] decryptData(byte[] inputData, String verifyCode) {
        return mEzvizAPI.decryptData(inputData, verifyCode, 1);
    }

    /**
     * 解密数据，该接口可以用于解密告警图片
     *
     * @param inputData  解密前数据
     * @param verifyCode 密码，设备加密时通常为设备验证码，平台加密时为平台返回的checkSum
     * @param cryptType  加密类型 1-设备加密 2-平台加密
     */
    public byte[] decryptData(byte[] inputData, String verifyCode, int cryptType) {
        return mEzvizAPI.decryptData(inputData, verifyCode, cryptType);
    }

    /**
     * 设置告警为已读
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param alarmIdList 告警信息Id数组(设置单条告警为已读时，数组中可以只有一个Id)
     * @param alarmStatus 告警状态,目前只支持设为已读功能 EZMessageStatusRead
     * @return true 表示成功， false 表示失败
     * @throws BaseException
     */
    public boolean setAlarmStatus(List<String> alarmIdList, EZAlarmStatus alarmStatus) throws BaseException {
        return mEzvizAPI.setAlarmStatus(alarmIdList, alarmStatus);
    }

    /**
     * 批量删除告警
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param alarmIdList 告警ID list
     * @return true 表示成功， false 表示失败
     * @throws BaseException
     */
    public boolean deleteAlarm(List<String> alarmIdList) throws BaseException {
        return mEzvizAPI.deleteAlarm(alarmIdList);
    }

    /**
     * 获取未读消息数
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 需要获取的设备序列号，为空时返回账户下所有设备的未读消息数
     * @param messageType  消息类型：EZMessageTypeAlarm 告警消息，EZMessageTypeLeave 留言消息
     * @return 未读消息数
     * @throws BaseException
     */
    public int getUnreadMessageCount(String deviceSerial, EZMessageType messageType) throws BaseException {
        return mEzvizAPI.getUnreadMessageCount(deviceSerial, messageType);
    }

    // endregion

    // region ------------------------------ 语音留言消息数据Api ------------------------------

    /**
     * 获取留言消息列表,默认为止查询语音留言信息（已废弃）
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 设备序列号（可为空），为空时返回账户下所有设备的留言消息
     * @param pageIndex    当前分页，从0开始
     * @param pageSize     分页大小
     * @param beginTime    开始时间
     * @param endTime      结束时间
     * @return 留言EZLeaveMessage列表
     * @throws BaseException
     * @deprecated 不再使用
     */
    public List<EZLeaveMessage> getLeaveMessageList(String deviceSerial, int pageIndex, int pageSize,
                                                    Calendar beginTime, Calendar endTime) throws BaseException {
        return mEzvizAPI.getLeaveMessageList(deviceSerial, pageIndex, pageSize, beginTime, endTime);
    }

    /**
     * 批量设置留言消息已读功能（已废弃）
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param msgIdList     留言消息Id数组(最大数量为10，允许只有1个)
     * @param messageStatus 需要设置的留言状态，目前只支持 EZMessageStatusRead(已读);
     * @return true 表示成功， false 表示失败
     * @throws BaseException
     * @deprecated 不再使用
     */
    public boolean setLeaveMessageStatus(List<String> msgIdList, EZMessageStatus messageStatus) throws BaseException {
        return mEzvizAPI.setLeaveMessageStatus(msgIdList, messageStatus);
    }

    /**
     * 批量删除留言消息（已废弃）
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param msgIdList 留言消息Id数组(最大数量为10，允许只有1个)
     * @return true 表示成功， false 表示失败
     * @throws BaseException
     * @deprecated 不再使用
     */
    public boolean deleteLeaveMessages(List<String> msgIdList) throws BaseException {
        return mEzvizAPI.deleteLeaveMessages(msgIdList);
    }

    /**
     * 获取语音留言数据（已废弃）
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param handler  handler，用于发送成功或者失败消息, 301:下载留言失败 ;302:下载留言成功 消息obj为留言ID
     * @param msg      留言消息
     * @param callback 获取留言消息数据回调
     * @deprecated 不再使用
     */
    public void getLeaveMessageData(Handler handler, EZLeaveMessage msg, EZOpenSDKListener.EZLeaveMessageFlowCallback callback) {
        mEzvizAPI.getLeaveMessageData(handler, msg, callback);
    }

    // endregion

    // region ------------------------------ VideoTalk视频通话相关Api ------------------------------

    /**
     * 呼叫设备进入房间进行视频通话
     * 该接口为耗时操作，必须在线程中调用
     * @param appId                 ERTC AppId，不是AppKey
     * @param ertcToken             ERTC 资源token
     * @param roomId                房间号
     * @param deviceSerial          设备序列号
     * @param cameraNo              通道号
     * @param account               联系人id
     * @return true 表示成功， false 表示失败
     */
    public boolean inviteDeviceEnterMeeting(String appId, String ertcToken, String roomId, String deviceSerial, int cameraNo, String account) throws BaseException {
        return mEzvizAPI.videoTalkAction("request", appId, ertcToken, roomId, deviceSerial, cameraNo, account);
    }

    /**
     * 取消呼叫设备进入房间进行视频通话
     * 该接口为耗时操作，必须在线程中调用
     * @param appId                 ERTC AppId，不是AppKey
     * @param ertcToken             ERTC 资源token
     * @param roomId                房间号
     * @param deviceSerial          设备序列号
     * @param cameraNo              通道号
     * @param account               联系人id
     * @return true 表示成功， false 表示失败
     */
    public boolean cancelInviteDeviceEnterMeeting(String appId, String ertcToken, String roomId, String deviceSerial, int cameraNo, String account) throws BaseException {
        return mEzvizAPI.videoTalkAction("cancel", appId, ertcToken, roomId, deviceSerial, cameraNo, account);
    }

    /**
     * 拒绝接听设备的视频通话请求
     * 该接口为耗时操作，必须在线程中调用
     * @param appId                 ERTC AppId，不是AppKey
     * @param ertcToken             ERTC 资源token
     * @param roomId                房间号
     * @param deviceSerial          设备序列号
     * @param cameraNo              通道号
     * @param account               联系人id
     * @return true 表示成功， false 表示失败
     */
    public boolean rejectVideoCallReqFromDevice(String appId, String ertcToken, String roomId, String deviceSerial, int cameraNo, String account) throws BaseException {
        return mEzvizAPI.videoTalkAction("reject", appId, ertcToken, roomId, deviceSerial, cameraNo, account);
    }

    // endregion

    // region ------------------------------ WiFi配网相关Api ------------------------------

    /**
     * 查询设备信息（用于添加设备之前, 简单查询设备信息，如是否在线，是否添加等）
     * 该接口为耗时操作，建议放在线程中调用
     *
     * @param deviceSerial 需要查询的设备序列号
     * @return 返回 EZProbeDeviceInfo 对象，包含设备简单信息，用于添加目的
     * @throws BaseException
     * @deprecated 仅保留接口兼容老用户，请尽快换用EZOpenSDK.probeDeviceInfo(String deviceSerial,String deviceType)
     */
    public EZProbeDeviceInfo probeDeviceInfo(String deviceSerial) throws BaseException {
        return mEzvizAPI.probeDeviceInfo(deviceSerial);
    }

    /**
     * 尝试查询设备信息（用于添加设备之前, 简单查询设备信息，如是否在线，是否添加等）
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param deviceSerial 需要查询的设备序列号
     * @param deviceType   设备型号 (设备型号和设备序列号不能均为空,优先按照设备序列号查询)
     * @return 返回 EZProbeDeviceInfo 对象，包含设备简单信息，用于添加目的
     */
    public EZProbeDeviceInfoResult probeDeviceInfo(String deviceSerial, String deviceType) {
        return mEzvizAPI.probeDeviceInfo(deviceSerial, deviceType);
    }

    /**
     * 开始WiFi配置
     *
     * @param context      应用 activity context
     * @param deviceSerial 配置设备序列号
     * @param ssid         连接WiFi SSID
     * @param password     连接  WiFi 密码
     * @param back         配置回调
     * @since 4.3.0
     */
    public void startConfigWifi(Context context, String deviceSerial, String ssid, String password, EZOpenSDKListener.EZStartConfigWifiCallback back) {
        startConfigWifi(context, deviceSerial, ssid, password, EZConstants.EZWiFiConfigMode.EZWiFiConfigSmart, back);
    }

    /**
     * 开始WiFi配置
     *
     * @param context      应用 activity context
     * @param deviceSerial 配置设备序列号
     * @param ssid         连接WiFi SSID
     * @param password     连接  WiFi 密码
     * @param mode         配网的方式，EZWiFiConfigMode中列举的模式进行任意组合,例如:EZWiFiConfigMode.EZWiFiConfigSmart|EZWiFiConfigMode.EZWiFiConfigWave
     * @param back         配置回调
     * @since 4.8.3
     */
    public void startConfigWifi(Context context, String deviceSerial, String ssid, String password, int mode, EZOpenSDKListener.EZStartConfigWifiCallback back) {
        mEzvizAPI.startConfigWifi(context, deviceSerial, ssid, password, mode, null, back);
    }

    /**
     * 开始WiFi配置
     *
     * @param context      应用 activity context
     * @param deviceSerial 配置设备序列号
     * @param ssid         连接WiFi SSID
     * @param password     连接  WiFi 密码
     * @param mode         配网的方式，EZWiFiConfigMode中列举的模式进行任意组合,例如:EZWiFiConfigMode.EZWiFiConfigSmart|EZWiFiConfigMode.EZWiFiConfigWave
     * @param apiUrl       指定去哪个平台查询
     * @param back         配置回调
     * @since 4.19.8
     */
    public void startConfigWifi(Context context, String deviceSerial, String ssid, String password, int mode, String apiUrl, EZOpenSDKListener.EZStartConfigWifiCallback back) {
        mEzvizAPI.startConfigWifi(context, deviceSerial, ssid, password, mode, apiUrl, back);
    }

    /**
     * 开始WiFi配置
     *
     * @param context  应用 activity context
     * @param ssid     连接WiFi SSID
     * @param password 连接WiFi 密码
     * @param l        回调，用于处理连接设备的WiFi配置状态
     * @return 返回值并没有特别的含义，根据回调判断是否配置wifi成功
     * @deprecated 不再使用
     */
    public boolean startConfigWifi(Context context, String ssid, String password, DeviceDiscoveryListener l) {
        return mEzvizAPI.startConfigWifi(context, ssid, password, l);
    }

    /**
     * 停止Wifi配置
     *
     * @return true 表示成功， false 表示失败
     */
    public boolean stopConfigWiFi() {
        return mEzvizAPI.stopConfigWiFi();
    }

    // endregion

    // region ------------------------------ AP配网相关Api ------------------------------

    /**
     * AP配网接口,默认为"EZVIZ_"+设备序列号,设备AP热点密码默认为"EZVIZ_"+设备验证码,默认自动连接设备热点，需要获取可扫描wifi的权限
     * 封装了设备状态轮询步骤，轮询20次，5秒一次，一共100秒等待时间；如果感觉等待时间过长，可以使用以下方案：
     * 使用此api的同时，创建一个定时器，设置自己期望的一个超时时间。超时后调用stopAPConfigWifi，视为配网失败
     *
     * @param wifiSsid         WiFi的ssid
     * @param wifiPwd          WiFi的密码
     * @param deviceSerial     设备序列号
     * @param deviceVerifyCode 设备验证码
     * @param callback         结果回调
     */
    public void startAPConfigWifiWithSsid(final String wifiSsid, final String wifiPwd, String deviceSerial,
                                          final String deviceVerifyCode, final APWifiConfig.APConfigCallback callback) {
        mEzvizAPI.startAPConfigWifiWithSsid(wifiSsid, wifiPwd, deviceSerial, deviceVerifyCode, callback);
    }


    /**
     * AP配网接口，如果你的设备热点是EZVIZ_开头的，deviceHotspotName和deviceHotspotPwd可传空；如果不是，这两个参数一定要传入对应的设备热点名和设备热点密码，否则配网失败
     * 封装了设备状态轮询步骤，轮询20次，5秒一次，一共100秒等待时间；如果感觉等待时间过长，可以使用以下方案：
     * 使用此api的同时，创建一个定时器，设置自己期望的一个超时时间。超时后调用stopAPConfigWifi，视为配网失败
     *
     * @param wifiSsid                   WiFi的ssid
     * @param wifiPwd                    WiFi的密码
     * @param deviceSerial               设备序列号
     * @param deviceVerifyCode           设备验证码
     * @param deviceHotspotName          设备热点名称，可传空，默认为"EZVIZ_"+设备序列号
     * @param deviceHotspotPwd           设备热点密码,可传空，默认为"EZVIZ_"+设备验证码
     * @param autoConnectToDeviceHotSpot 是否自动连接设备热点,需要获取可扫描wifi的权限
     * @param callback                   结果回调
     */
    public void startAPConfigWifiWithSsid(final String wifiSsid, final String wifiPwd, String deviceSerial,
                                          final String deviceVerifyCode, final String deviceHotspotName,
                                          final String deviceHotspotPwd, boolean autoConnectToDeviceHotSpot,
                                          final APWifiConfig.APConfigCallback callback) {
        mEzvizAPI.startAPConfigWifiWithSsid(wifiSsid, wifiPwd, deviceSerial, deviceVerifyCode, deviceHotspotName, deviceHotspotPwd, autoConnectToDeviceHotSpot, callback);
    }

    /**
     * AP配网接口，如果你的设备热点是EZVIZ_开头的，deviceHotspotName和deviceHotspotPwd可传空；如果不是，这两个参数一定要传入对应的设备热点名和设备热点密码，否则配网失败
     * 封装了设备状态轮询步骤，轮询20次，5秒一次，一共100秒等待时间；如果感觉等待时间过长，可以使用以下方案：
     * 使用此api的同时，创建一个定时器，设置自己期望的一个超时时间。超时后调用stopAPConfigWifi，视为配网失败
     *
     * @param wifiSsid                   WiFi的ssid
     * @param wifiPwd                    WiFi的密码
     * @param deviceSerial               设备序列号
     * @param deviceVerifyCode           设备验证码
     * @param deviceHotspotName          设备热点名称，可传空，默认为"EZVIZ_"+设备序列号
     * @param deviceHotspotPwd           设备热点密码,可传空，默认为"EZVIZ_"+设备验证码
     * @param autoConnectToDeviceHotSpot 是否自动连接设备热点,需要获取可扫描wifi的权限
     * @param apiUrl                     指定去哪个平台查询
     * @param callback                   结果回调
     */
    public void startAPConfigWifiWithSsid(final String wifiSsid, final String wifiPwd, String deviceSerial,
                                          final String deviceVerifyCode, final String deviceHotspotName,
                                          final String deviceHotspotPwd, boolean autoConnectToDeviceHotSpot,
                                          final String apiUrl, final APWifiConfig.APConfigCallback callback) {
        mEzvizAPI.startAPConfigWifiWithSsid(wifiSsid, wifiPwd, deviceSerial, deviceVerifyCode, deviceHotspotName, deviceHotspotPwd, autoConnectToDeviceHotSpot, apiUrl, callback);
    }

    /**
     * 停止AP配网
     */
    public void stopAPConfigWifiWithSsid() {
        mEzvizAPI.stopAPConfigWifiWithSsid();
    }

    // endregion

    // region ------------------------------ 接触式配网 New AP Config ------------------------------

    /**
     * 获取接触式AP配网token
     *
     * @param callback
     */
    public void getNewApConfigToken(GetTokenCallback callback) {
        mEzvizAPI.getNewApConfigToken(callback);
    }

    /**
     * 设备配网（向设备发送wifi信息）
     *
     * @param token 通过getNewApConfigToken接口获取的token
     * @param ssid wifi名称
     * @param password wifi密码
     * @param lbsDomain 通过requestConfigToken接口获取的注册地址
     * @param callback
     */
    public void startNewApConfigWithToken(String token, String ssid, String password, String lbsDomain, StartNewApConfigCallback callback) {
        mEzvizAPI.startNewApConfigWithToken(token, ssid, password, lbsDomain, callback);
    }

    /**
     * 获取设备信息（需连接设备热点）
     *
     * @param callback
     */
    public void getAccessDeviceInfo(GetAccessDeviceInfoCallback callback) {
        mEzvizAPI.getAccessDeviceInfo(callback);
    }

    /**
     * 获取设备环境wifi列表（需连接设备热点）
     *
     * @param callback
     */
    public void getAccessDeviceWifiList(GetDeviceWifiListCallback callback) {
        mEzvizAPI.getAccessDeviceWifiList(callback);
    }

    /**
     * 查询设备绑定状态
     *
     * @param deviceSerial 设备序列号
     * @param callback
     */
    public void queryPlatformBindStatus(String deviceSerial, QueryPlatformBindStatusCallback callback) {
        mEzvizAPI.queryPlatformBindStatus(deviceSerial, callback);
    }

    /**
     * 设置配网设备网关地址 可选
     *
     * @param devRouteDomain 设备网关地址
     */
    public void setDevRouteDomain(String devRouteDomain) {
        mEzvizAPI.setDevRouteDomain(devRouteDomain);
    }

    // endregion

    // region ------------------------------ 带宽检测 ------------------------------

    /**
     * 开始带宽检测
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param bwCheckType 带宽检测类型，EZBWCheckType中列举的类型进行任意组合。
     *      EZBWCheckType.EZBWCheckClientUpward：只进行客户端上行检测
     *      EZBWCheckType.EZBWCheckClientDownward：只进行客户端下行检测
     *      EZBWCheckType.EZBWCheckClientUpward | EZBWCheckType.EZBWCheckClientDownward：串行进行客户端上行和客户端下行检测，耗时为expireTime*2
     * @param deviceSerial  设备序列号，可选。设置后检测客户端到设备机房的带宽
     * @param cameraNo      通道号，默认1，可选。设置后检测客户端到设备机房的带宽
     * @param checkTime     最大带宽测试时长、取值区间【15s~30s】
     * @param bwCheckToken  带宽检测token，必填
     * @param callback      带宽检测结果回调
     * @throws BaseException
     */
    public void startBWCheck(int bwCheckType, String deviceSerial, int cameraNo,
                             int checkTime, String bwCheckToken, EZBWCheckManager.EZBWCheckResultCallback callback) throws BaseException {
        EZBWCheckManager.getInstance().startBWCheck(bwCheckType, deviceSerial, cameraNo, checkTime, bwCheckToken, callback);
    }

    /**
     * 停止带宽检测
     */
    public void stopBWCheck() {
        EZBWCheckManager.getInstance().stopBWCheck();
    }

    // endregion

    // region ------------------------------ 用户相关Api ------------------------------

    /**
     * 获取用户信息；用户信息包含：用户名,头像地址等
     * 该接口为耗时操作，必须在线程中调用
     *
     * @return 返回 EZUserInfo 对象, 目前nickname和avatarUrl字段无效
     * @throws BaseException
     */
    public EZUserInfo getUserInfo() throws BaseException {
        return mEzvizAPI.getEZUserInfo();
    }

    // endregion

    // region ------------------------------ 其他Api ------------------------------

    public Application getApplication() {
        return mApplication;
    }

    /**
     * 获取终端（手机等）唯一识别码
     *
     * @return 终端唯一识别码
     * @since 4.3.0
     */
    public String getTerminalId() {
        return mEzvizAPI.getTerminalId();
    }

    /**
     * 上传SDK云录制图片
     * 该接口为耗时操作，必须在线程中调用
     *
     * @param bitmap
     * @return 图片存储id
     * @throws BaseException
     */
    public String uploadSDKCloudRecordImage(Bitmap bitmap) throws BaseException {
        return mEzvizAPI.uploadSDKCloudRecordPicture(bitmap);
    }

    private Map<String, String> getHTTPPublicParam() {
        HashMap<String, String> result = new HashMap<>();
        result.put("clientType", String.valueOf(Constant.ANDROID_TYPE));
        result.put("featureCode", LocalInfo.getInstance().getHardwareCode());
        result.put("osVersion", android.os.Build.VERSION.RELEASE);
        result.put("netType", EzvizAPI.getInstance().getNetType());
        result.put("sdkVersion", Config.VERSION);
        result.put("appKey", EzvizAPI.getInstance().getAppKey());
        result.put("appID", LocalInfo.getInstance().getPackageName());
        result.put("appName", LocalInfo.getInstance().getAppName());
        return result;
    }

    private String getHTTPPublicParam(String key) {
        if (TextUtils.equals(key, "clientType")) {
            return String.valueOf(Constant.ANDROID_TYPE);
        } else if (TextUtils.equals(key, "featureCode")) {
            return LocalInfo.getInstance().getHardwareCode();
        } else if (TextUtils.equals(key, "osVersion")) {
            return android.os.Build.VERSION.RELEASE;
        } else if (TextUtils.equals(key, "netType")) {
            return EzvizAPI.getInstance().getNetType();
        } else if (TextUtils.equals(key, "sdkVersion")) {
            return Config.VERSION;
        } else if (TextUtils.equals(key, "appKey")) {
            return EzvizAPI.getInstance().getAppKey();
        } else if (TextUtils.equals(key, "appID")) {
            return LocalInfo.getInstance().getPackageName();
        } else if (TextUtils.equals(key, "appName")) {
            return LocalInfo.getInstance().getAppName();
        }
        return null;
    }

    private static boolean urlStub = false;
    private static String _mAppKey;
    private static String _mAPIURL;
    private static String _mWEBURL;

    private static void getVariants() {
        if (urlStub && Config.ENABLE_STUB) {
            final String filePath = "/sdcard/videogo_test_cfg";
            Map<String, String> mMap = new HashMap<String, String>();
            Utils.parseTestConfigFile(filePath, mMap);
            _mAppKey = mMap.get("APP_KEY");
            _mAPIURL = mMap.get("API_URL");
            _mWEBURL = mMap.get("WEB_URL");
        }
    }

    public String getKey() {
        return _mAppKey;
    }

    // endregion

    // region ------------------------------ 海康互联专用Api ------------------------------

    /**
     *  设置研究院播放库日志级别，需在初始化sdk之前调用；默认为2:INFO
     *
     *  @param logLevel 日志级别  1:DEBUG 2:INFO：3:WARN：4:ERROR
     */
    public static void setPlayCtrlLogLevel(int logLevel) {
        Config.playCtrlLogLevel = logLevel;
    }

    // endregion

}
