package net.northking.cctp.upperComputer.webSocket.thread;

import com.alibaba.fastjson.JSONObject;
import net.northking.cctp.upperComputer.constants.AndroidHotKeyCodeEnum;
import net.northking.cctp.upperComputer.constants.KeyBoardCodeEnum;
import net.northking.cctp.upperComputer.constants.ResponseCmd;
import net.northking.cctp.upperComputer.deviceManager.AndroidDeviceManager;
import net.northking.cctp.upperComputer.deviceManager.UpperComputerManager;
import net.northking.cctp.upperComputer.deviceManager.screen.AndroidScreenResponseThread;
import net.northking.cctp.upperComputer.deviceManager.screen.AndroidVideoScreenResponseThread;
import net.northking.cctp.upperComputer.deviceManager.thread.AndroidDeviceInitThread;
import net.northking.cctp.upperComputer.driver.adb.AdbDevice;
import net.northking.cctp.upperComputer.driver.adb.AdbTransport;
import net.northking.cctp.upperComputer.driver.adb.PushBytesCountCallback;
import net.northking.cctp.upperComputer.driver.adb.RemoteFileRecord;
import net.northking.cctp.upperComputer.driver.agent.AndroidAgentSession;
import net.northking.cctp.upperComputer.driver.agent.command.*;
import net.northking.cctp.upperComputer.driver.agent.command.data.PackageInfo;
import net.northking.cctp.upperComputer.driver.agent.command.protocol.ProtocolCommand;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.exception.ExecuteException;
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
import net.northking.cctp.upperComputer.utils.HttpUtils;
import net.northking.cctp.upperComputer.utils.ProcessCmdUtils;
import net.northking.cctp.upperComputer.utils.SessionUtils;
import net.northking.cctp.upperComputer.utils.SpringUtils;
import net.northking.cctp.upperComputer.webSocket.entity.CmdRequest;
import net.northking.cctp.upperComputer.webSocket.entity.CmdResponse;
import net.northking.cctp.upperComputer.webSocket.entity.ParamCheck;
import net.northking.cctp.upperComputer.webSocket.entity.Point;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import javax.websocket.Session;
import java.io.*;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.*;

public class AndroidMessageHandlerThread extends AbstractMessageHandler {

    private final Logger logger = LoggerFactory.getLogger(AndroidMessageHandlerThread.class);

    private AndroidAgentSession asyncAgentSession;  //异步

    private AndroidAgentSession syncAgentSession;   //同步

    private AdbDevice adbDevice;

    private String tenantId;

    private AndroidLogThread androidLogThread;

    private AndroidPerfThread androidPerfThread;

    private String logPath;

    private final String PATH_PREFIX = "/sdcard";

    private boolean shiftDown = false;

    private boolean ctrlDown = false;

    private boolean altDown = false;

    private boolean capsDown = false;

    private AndroidVideoScreenResponseThread videoScreenResponseThread;

    private List<String> capsUpLetters = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z");

    private List<String> capsDownLetters = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z");


    public AndroidMessageHandlerThread(AdbDevice adbDevice, Session session) throws ParamMistakeException,ExecuteException {
        //异步agentSession
        this.asyncAgentSession = new AndroidAgentSession(adbDevice, true);
        //同步agentSession
        this.syncAgentSession = new AndroidAgentSession(adbDevice, false);
        try {
            syncAgentSession.start();
            asyncAgentSession.start();
        } catch (IOException e) {
            logger.error("设备【{}】的agentSession开启失败", adbDevice.getSerial(), e);
            throw new ExecuteException("设备还未准备好，请稍后再试");
        }
        this.adbDevice = adbDevice;
        this.session = session;
        this.serial = adbDevice.getSerial();
        setName("[" + adbDevice.getSerial() + "-handleMsg]");
        initDeviceRequireParam();
    }

    @Override
    public void closeADBDebug(CmdRequest request) {
    }

    @Override
    public void openADBDebug(CmdRequest request) {
    }

    @Override
    public void pullFileFromDevice(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck pathCheck = ParamCheck.build().name("path").type("string").length(Integer.MAX_VALUE);
        ParamCheck tenantIdCheck = ParamCheck.build().name("tenantId").type("string").length(Integer.MAX_VALUE);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, pathCheck, tenantIdCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String path = (String) params.get("path");
        String tenantId = (String) params.get("tenantId");
        try {
            pullFromDeviceAndUploadFile(path,request,tenantId);
        } catch (Exception e) {
            logger.error("下载设备文件操作异常", e);
        }
    }

    private void pullFromDeviceAndUploadFile(String path, CmdRequest request, String tenantId) throws IOException {
        //从路径截取文件名
        String fileName = path;
        if (path.contains("/")) {
            fileName = path.substring(path.lastIndexOf("/") + 1);
        }
        path = addPathPrefix(path);
        //上传到临时文件
        File fileDir = new File(UpperComputerManager.getInstance().getApplicationPath() + "/tempFile/");
        File tmpFile = new File(UpperComputerManager.getInstance().getApplicationPath() + "/tempFile/" + fileName);
        if (!fileDir.exists()) {   //文件夹不存在
            fileDir.mkdirs();
        }
        if (!tmpFile.exists()) {  //文件不存在
            tmpFile.createNewFile();
        }
        File tmpPath = tmpFile.getAbsoluteFile();
        logger.debug("原文件位置：{}，临时文件位置：{}",path,tmpPath);
        FileOutputStream fileOutputStream = new FileOutputStream(tmpFile);
        boolean pull = AndroidDeviceManager.getInstance().getAdb().pull(adbDevice, fileOutputStream, path);
        if (pull) {
            //上传到文件服务器并返回信息给前端
            try {
                //上传到服务器
                String serverIp = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
                String fileUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
                String fileId = HttpUtils.doUpload(serverIp + fileUploadPath, tmpFile, tenantId);
                logger.info("上传文件到服务器完成:{}", fileId);
                Map<String, Object> result = new HashMap<>();
                result.put("fileName", fileName);
                result.put("fileId", fileId);
                SessionUtils.sendSuccessData(session, request, result, "文件下载完成");
            }catch (Exception e) {
                logger.error("文件上传到服务器失败：",e);
                SessionUtils.sendFailureMessage(session, request, "文件上传服务器失败");
            }
        } else {
            SessionUtils.sendFailureMessage(session, request, "从手机下载文件失败");
        }
    }


    @Override
    public void pushFileToDevice(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck fileUrlCheck = ParamCheck.build().name("fileUrl").type("string").length(Integer.MAX_VALUE);
        ParamCheck dirCheck = ParamCheck.build().name("dir").type("string").length(Integer.MAX_VALUE);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, fileUrlCheck,dirCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String fileUrl = (String) params.get("fileUrl");
        String dir = (String) params.get("dir");
        try {
            downloadFileToLocalAndPushToDevice(fileUrl,dir,request);
        } catch (Exception e) {
            logger.error("文件上传到设备异常", e);
            SessionUtils.sendFailureMessage(session, request, "文件上传异常");
            return;
        }
    }

    private void downloadFileToLocalAndPushToDevice(String fileUrl, String dir, CmdRequest request) throws IOException {
        File fileDir = new File(UpperComputerManager.getInstance().getApplicationPath() + "/tempFile/");
        File tmpFile = null;
        if (!fileDir.exists()) {   //文件夹不存在
            fileDir.mkdirs();
        }
        //下载服务器文件到上位机
        String serverIp = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
        String fileDownloadPath = SpringUtils.getProperties("nk.mobile-computer.publicDownloadFile");
        fileDownloadPath = fileDownloadPath.replace("{appUrl}", fileUrl);
        tmpFile = HttpUtils.downloadFileNoName(serverIp + fileDownloadPath, fileDir.getAbsolutePath(), fileUrl);
        if (null == tmpFile) {
            logger.warn("下载临时文件到上位机失败！");
        }
        Long totalSpace = tmpFile.length();
        dir = addPathPrefix(dir);
        if (!dir.endsWith("/")) {
            dir = dir+"/";
        }
        String targetPath= String.format("%s%s", dir, tmpFile.getName());
        logger.info("文件总大小：{},目标位置：{}", totalSpace,targetPath);
        FileInputStream fileInputStream = new FileInputStream(tmpFile);
        DecimalFormat decimalFormat = new DecimalFormat("#.00");
        boolean push = AndroidDeviceManager.getInstance().getAdb().push(adbDevice, fileInputStream, Integer.parseInt(tmpFile.lastModified() / 1000 + ""), 777, targetPath, new PushBytesCountCallback() {
            private double percentOld = 0;
            private long oldTime = System.currentTimeMillis();

            @Override
            public void onSendBytes(long totalCount) {
                Double percent = totalCount / totalSpace.doubleValue() * 100;
                String format = decimalFormat.format(percent);
                double percentNew = Double.parseDouble(format);
                logger.debug("传输大小：{}，文件总大小：{}", totalCount, totalSpace);
                long nowTime = System.currentTimeMillis();
                if (percentNew - percentOld > 5 && nowTime - oldTime > 1000) {
                    logger.debug("文件传输进度：{}", format);
                    Map<String, Object> result = new HashMap<>();
                    result.put("percent", percentNew);
                    percentOld = percentNew;
                    oldTime = nowTime;
                    SessionUtils.sendContinuousData(session, request, result, "文件上传进度");
                }
            }
        });
        if (push) {
            Map<String, Object> result = new HashMap<>();
            result.put("percent", 100);
            SessionUtils.sendContinuousData(session, request, result, "文件上传进度");
            SessionUtils.sendSuccessData(session, request, null, "文件上传完成");
        } else {
            SessionUtils.sendFailureMessage(session, request, "文件无法推送到手机");
            return;
        }
        logger.debug("上传文件是否成功：{}",push);
        //上传完成，删除临时文件
        if (tmpFile.exists()) {
            boolean delete = tmpFile.delete();
            if (!delete) {
                logger.warn("临时文件【{}】删除失败", tmpFile.getAbsolutePath());
            }
        }
        logger.debug("临时文件是否存在：{}",tmpFile.exists());
    }


    @Override
    public void getFileList(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck dirCheck = ParamCheck.build().name("dir").type("string").length(Integer.MAX_VALUE);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, dirCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String dir = (String) params.get("dir");
        dir = addPathPrefix(dir);
        ArrayList<RemoteFileRecord> fileRecords = AndroidDeviceManager.getInstance().getAdb().list(adbDevice, dir);
        Map<String, Object> result = new HashMap<>();
        List<Map<String, Object>> fileList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(fileRecords)) {
            for (RemoteFileRecord fileRecord : fileRecords) {
                Map<String, Object> fileMap = new HashMap<>();
                fileMap.put("name", fileRecord.getPath());
                fileMap.put("size", fileRecord.getSize() / 1024); //b转换为kb
                fileMap.put("lastModified",fileRecord.getLastModified());
                fileMap.put("dirFlag",fileRecord.isDirectory()?1:0);
                fileList.add(fileMap);
            }
        }else {
            logger.debug("当前目录文件列表为空，dir："+dir);
        }
        result.put("result", fileList);
        SessionUtils.sendSuccessData(session, request, result, "文件列表获取成功");
    }

    /**
     * 给设备目录增加/sdcard前缀
     * @param dir
     * @return
     */
    private String addPathPrefix(String dir) {
        StringBuilder sb = new StringBuilder(PATH_PREFIX);
        if (!dir.startsWith(PATH_PREFIX)) {
            sb.append(dir);
        }
        logger.debug("增加前缀后的设备目录为："+sb.toString());
        return sb.toString();
    }


    @Override
    public void downloadLogcat(CmdRequest request) {
    }

    @Override
    public void startUiNode(CmdRequest request) {

    }

    @Override
    public void stopUiNode(CmdRequest request) {

    }

    @Override
    public void getNodeTreeOnce(CmdRequest request) {

    }

    @Override
    public void clearAndCloseDeviceAgentConnection() {
        if (null != asyncAgentSession) {
            asyncAgentSession.closeSilence();
            asyncAgentSession = null;
        }
        if (null != syncAgentSession) {
            syncAgentSession.closeSilence();
            syncAgentSession = null;
        }
        //通知前端设备现在处于掉线的状态,前端开始转圈
        notifyDeviceOfflineToWeb();
    }

    @Override
    public void reConnectDeviceAgent() {
        if (null != asyncAgentSession) {
            asyncAgentSession.closeSilence();
        }
        if (null != syncAgentSession) {
            syncAgentSession.closeSilence();
        }
        //异步agentSession
        this.asyncAgentSession = new AndroidAgentSession(adbDevice, true);
        //同步agentSession
        this.syncAgentSession = new AndroidAgentSession(adbDevice, false);
        try {
            syncAgentSession.start();
            asyncAgentSession.start();
        } catch (IOException e) {
            logger.error("设备【{}】的agentSession开启失败", adbDevice.getSerial(), e);
            throw new ExecuteException("设备还未准备好，请稍后再试");
        }
        notifyDeviceOnlineToWeb();
        //发送前端重连成功消息的标志
        AndroidScreenResponseThread screenThread = AndroidDeviceManager.getInstance().getScreenThread(adbDevice.getSerial());
        if (null != screenThread) {
            screenThread.setSendDeviceStatus(true);
        }
    }


    @Override
    public void stopLogcat(CmdRequest request) {
        Map<String, Object> data = request.getData();
        if (androidLogThread != null && androidLogThread.isAlive()) {
            ParamCheck tenantIdCheck = ParamCheck.build().name("tenantId").type("string").length(32);
            Map<String, Object> params = null;
            try {
                params = checkParam(data, false, tenantIdCheck);
            } catch (Exception e) {
                logger.error("参数校验失败！", e);
                if (e instanceof ParamMistakeException) {
                    SessionUtils.sendFailureMessage(session, request, e.getMessage());
                    return;
                }
            }
            //若无值则记录临时日志文件地址
            tenantId = (String) params.get("tenantId");
            logPath = StringUtils.isBlank(logPath) ? androidLogThread.getLogFilePath() : logPath;
            String logFilePath = androidLogThread.stopLog(tenantId);
            if (logFilePath != null) {
                Map<String, Object> result = new HashMap<>();
                result.put("filePath", logFilePath);
                SessionUtils.sendSuccessData(session, request, result, "结束日志成功");
            } else {
                SessionUtils.sendFailureMessage(session, request, "上传日志失败");
            }
        } else {
            SessionUtils.sendFailureMessage(session, request, "未找到开启日志信息");
        }
    }

    @Override
    public void startLogcat(CmdRequest request) {
        if (null != androidLogThread && androidLogThread.isAlive()) {
            //拿上一次日志的临时文件位置
            logPath = StringUtils.isBlank(logPath) ? androidLogThread.getLogFilePath() : logPath;
            androidLogThread.killLog();
        }
        androidLogThread = new AndroidLogThread(adbDevice.getSerial(),adbDevice,session,request,logPath);
        androidLogThread.start();
        SessionUtils.sendContinuousData(session, request, null, "开启日志成功");
    }

    @Override
    public void startPerf(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck packageNameCheck = ParamCheck.build().name("packageName").type("string").length(Integer.MAX_VALUE);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, packageNameCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String packageName = (String) data.get("packageName");
        if (null != androidPerfThread && androidPerfThread.isAlive()) {
            androidPerfThread.stopThread();
            logger.debug("成功关闭原有性能线程");
        }
        androidPerfThread = new AndroidPerfThread(session,request);
        androidPerfThread.init(adbDevice.getSerial(),packageName);
        androidPerfThread.start();
        SessionUtils.sendContinuousData(session, request, null, "开启性能数据成功");
    }

    @Override
    public void stopPerf(CmdRequest request) {
        if (androidPerfThread != null && androidPerfThread.isAlive()) {
            logger.debug("关闭"+serial+"设备安卓性能线程");
            try {
                androidPerfThread.stopThread();
                androidPerfThread.interrupt();
                androidLogThread = null;
                SessionUtils.sendSuccessData(session, request, null, "停止性能数据成功");
            }catch (Exception e) {
                logger.error("关闭性能线程异常",e);
                SessionUtils.sendFailureMessage(session, request,  "停止性能数据异常");
            }
        }else {
            logger.warn("性能线程未开启，设备id：{}",serial);
        }
    }

    @Override
    public void sendShellToDevice(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck commandCheck = ParamCheck.build().name("command").type("string").length(Integer.MAX_VALUE);
        ParamCheck timeoutCheck = ParamCheck.build().name("timeout").type("int").withMin(0);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, commandCheck, timeoutCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String commandShell = (String) params.get("command");
        int timeout = (int) params.get("timeout");
        Future<String> future = Executors.newSingleThreadExecutor().submit(() -> {
            StringBuilder builder = new StringBuilder();
            AdbTransport transport = AndroidDeviceManager.getInstance().getAdb().shell(adbDevice, commandShell);
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new InputStreamReader(transport.getInputStream()));
                String line;
                while ((line = reader.readLine()) != null) {
                    builder.append(line);
                }
            } catch (IOException e) {
                logger.warn("读取设备执行shell命令的日志出错。。。。。。。。", e);
            } finally {
                if (null != reader) {
                    try {
                        reader.close();
                    } catch (IOException e) {

                    }
                }
            }
            return builder.toString();
        });
        try {
            String shellResult = future.get(timeout, TimeUnit.SECONDS);
            Map<String, Object> result = new HashMap<>();
            result.put("result", shellResult);
            SessionUtils.sendSuccessData(session, request, result, "shell命令执行成功");
        } catch (InterruptedException e) {
            logger.error("shell【{}】执行出错", commandShell, e);
            SessionUtils.sendFailureMessage(session, request, "shell命令执行出错");
            future.cancel(true);
        } catch (ExecutionException e) {
            logger.error("shell【{}】执行报错", commandShell, e);
            SessionUtils.sendFailureMessage(session, request, "shell命令执行出错");
            future.cancel(true);
        } catch (TimeoutException e) {
            SessionUtils.sendFailureMessage(session, request, "shell命令执行超时");
            future.cancel(true);
        }


    }

    @Override
    public void uninstallApp(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck packageNameCheck = ParamCheck.build().name("packageName").type("string").length(Integer.MAX_VALUE);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, packageNameCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String packageName = (String) params.get("packageName");
        CmdResponse response = CmdResponse.build(request).finished().fail("卸载应用失败！");
        AdbTransport transport = AndroidDeviceManager.getInstance().getAdb().shell(adbDevice, String.format("pm uninstall %s", packageName));
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(transport.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                logger.debug("设备【{}】卸载应用时输出:{}", adbDevice.getSerial(), line);
                if ("Success".equals(line)) {
                    logger.info("设备【{}】卸载应用【{}】成功。。。。。。", adbDevice.getSerial(), packageName);
                    response.success().withMessage("卸载应用成功！");
                }
            }
        } catch (IOException e) {
            logger.warn("读取设备卸载应用时日志出错。。。。。。。。", e);
        } finally {
            SessionUtils.sendMessage(session, JSONObject.toJSONString(response));
            if (null != reader) {
                try {
                    reader.close();
                } catch (IOException e) {

                }
            }
        }
    }

    @Override
    public void getAppList(CmdRequest request) {
        ArrayList<PackageInfo> packageInfos = null;
        if (null != syncAgentSession) {
            packageInfos = syncAgentSession.send(PackageListCommand.getInstance());
        } else {
            SessionUtils.sendFailureMessage(session, request, "设备中获取应用列表的服务还未准备好，请稍后再试");
            return;
        }
        Map<String, Object> result = new HashMap<>();
        if (!CollectionUtils.isEmpty(packageInfos)) {
            List<Map<String, String>> appList = new ArrayList<>();
            for (PackageInfo packageInfo : packageInfos) {
                if (null != packageInfo && !packageInfo.isSystemApp()) {
                    Map<String, String> appMap = new HashMap<>();
                    appMap.put("value", packageInfo.getPackageName());
                    appMap.put("label", packageInfo.getLabel());
                    appList.add(appMap);
                }
            }
            result.put("result", appList);
            SessionUtils.sendSuccessData(session, request, result, "应用列表获取成功");
        }
    }

    @Override
    public void inputKeyUp(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck keyCodeCheck = ParamCheck.build().name("key").type("string");
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, keyCodeCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String key = (String) params.get("key");
        setKeyCodeStatus(key, false);
        int code = KeyBoardCodeEnum.getCode(key, !shiftDown);
        if (code < 0) {
            SessionUtils.sendFailureMessage(session, request, "键[" + key + "]不存在");
            return;
        }
        KeyEventCommand command = new KeyEventCommand(KeyEventCommand.Action.UP, code);
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "抬起键[" + key + "]处理成功");
    }

    @Override
    public void inputKeyDown(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck keyCodeCheck = ParamCheck.build().name("key").type("string");
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, keyCodeCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String key = (String) params.get("key");
        setKeyCodeStatus(key, true);
        int code = KeyBoardCodeEnum.getCode(key, !shiftDown);
        if (code < 0) {
            SessionUtils.sendFailureMessage(session, request, "键[" + key + "]不存在");
            return;
        }
        int metaState = getMetaState();
        ;
        KeyEventCommand command = new KeyEventCommand(KeyEventCommand.Action.DOWN, code);
        if (metaState > 0) {
            logger.debug("打开meta键：{}", metaState);
            command.setMetaState(metaState);
        }
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "摁下键[" + key + "]处理成功");
    }

    private int getMetaState() {
        int meta = 0;
        if (shiftDown) {
            meta += 1;
        }
        //防止alt+tab快捷键未完全监听导致键盘无法正常输入
//        if (altDown) {
//            meta += 2;
//        }
        if (ctrlDown) {
            meta += 4096;
        }
        if (capsDown) {
            meta += 1048576;
        }
        return meta;
    }

    /**
     * 设置摁下的是否是shift,ctrl,alt
     *
     * @param key
     * @param down 摁下为true，抬起为false
     */
    private void setKeyCodeStatus(String key, boolean down) {
        if (key.equals(KeyBoardCodeEnum.KEYCODE_SHIFT_LEFT.getWebValue())) {
            shiftDown = down;
        } else if (key.equals(KeyBoardCodeEnum.KEYCODE_ALT_LEFT.getWebValue())) {
            altDown = down;
        } else if (key.equals(KeyBoardCodeEnum.KEYCODE_CTRL_LEFT.getWebValue())) {
            ctrlDown = down;
        } else if (capsUpLetters.contains(key)) {  //包含大写字母
            if (shiftDown) {  //如果是按了shift
                logger.debug("没摁大写锁定");
                capsDown = false;
            } else {
                logger.debug("摁了大写锁定");
                capsDown = true;
            }
        } else if (capsDownLetters.contains(key)) {  //小写字母
            if (shiftDown) {  //如果按了shift
                logger.debug("没摁大写锁定");
                capsDown = true;
            } else {
                logger.debug("摁了大写锁定");
                capsDown = false;
            }
        } else if (key.equals(KeyBoardCodeEnum.KEYCODE_CAPS_LOCK.getWebValue())) {
            logger.debug("摁大写锁定。。。。。。。。。。");
            capsDown = !capsDown;
        }
    }

    @Override
    public void installApp(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck textCheck = ParamCheck.build().name("appUrl").type("string").length(Integer.MAX_VALUE);
        ParamCheck packageCheck = ParamCheck.build().name("packageName").type("string").length(Integer.MAX_VALUE);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, textCheck,packageCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String appUrl = (String) params.get("appUrl");
        String packageName = (String) params.get("packageName");
        try {
            downloadAppToLocalAndPushToDevice(appUrl, request, packageName);
        } catch (IOException e) {
            logger.error("应用安装失败", e);
            SessionUtils.sendFailureMessage(session, request, "应用安装失败");
            return;
        }

    }

    private void downloadAppToLocalAndPushToDevice(String appUrl, CmdRequest request, String packageName) throws IOException {
        File fileDir = new File(UpperComputerManager.getInstance().getApplicationPath() + "/tempApp/");
        File appFile = new File(UpperComputerManager.getInstance().getApplicationPath() + "/tempApp/" + appUrl);
        if (!fileDir.exists()) {   //文件夹不存在
            fileDir.mkdirs();
        }
        if (!appFile.exists()) {  //文件不存在
            String serverIp = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
            String appDownloadPath = SpringUtils.getProperties("nk.mobile-computer.publicDownloadFile");
            appDownloadPath = appDownloadPath.replace("{appUrl}", appUrl);
            HttpUtils.downloadFileToLocal(serverIp + appDownloadPath, fileDir.getAbsolutePath(), appUrl);
        }
        Long totalSpace = appFile.length();
        logger.info("应用总大小：{}", totalSpace);
        FileInputStream fileInputStream = new FileInputStream(appFile);
        DecimalFormat decimalFormat = new DecimalFormat("#.00");
        boolean push = AndroidDeviceManager.getInstance().getAdb().push(adbDevice, fileInputStream, Integer.parseInt(appFile.lastModified() / 1000 + ""), 777, "/data/local/tmp/tmp_install.apk", null);
//                new PushBytesCountCallback() {
//            private double percentOld = 0;
//            private long oldTime = System.currentTimeMillis();
//
//            @Override
//            public void onSendBytes(long totalCount) {
//                Double percent = totalCount / totalSpace.doubleValue() * 100;
//                String format = decimalFormat.format(percent);
//                double percentNew = Double.parseDouble(format);
//                logger.debug("传输大小：{}，总大小：{}", totalCount, totalSpace);
//                long nowTime = System.currentTimeMillis();
//                if (percentNew - percentOld > 5 && nowTime - oldTime > 1000) {
//                    logger.debug("传输进度：{}", format);
//                    Map<String, Object> result = new HashMap<>();
//                    result.put("percent", percentNew / 2);
//                    percentOld = percentNew;
//                    oldTime = nowTime;
//                    SessionUtils.sendContinuousData(session, request, result, "应用安装进度");
//                }
//            }
//        });
        if (push) {
            Map<String, Object> result = new HashMap<>();
            result.put("percent", 50);
            SessionUtils.sendContinuousData(session, request, result, "应用安装进度");
        } else {
            SessionUtils.sendFailureMessage(session, request, "应用无法推送到手机");
            return;
        }
        logger.debug("上传应用：{}",push);
        //上传完成，开始安装应用
        AdbTransport adbTransport = AndroidDeviceManager.getInstance().getAdb().shell(adbDevice, "pm install /data/local/tmp/tmp_install.apk");
        String line;
        BufferedReader reader = new BufferedReader(new InputStreamReader(adbTransport.getInputStream()));
        boolean success = false;
        while ((line = reader.readLine()) != null) {
            logger.debug("[" + adbDevice.getSerial() + "]应用安装日志：{}", line);
            if (line.startsWith("Success")) {
                success = true;
            }
        }
        logger.info("[{}]应用安装完成,成功：{}", adbDevice.getSerial(), success);
        adbTransport.close();
        AndroidDeviceManager.getInstance().getAdb().shell(adbDevice, "rm -rf /data/local/tmp/tmp.apk");
        if (success) {
            Map<String, Object> result = new HashMap<>();
            result.put("percent", 100);
            SessionUtils.sendContinuousData(session, request, result, "应用安装进度");
            SessionUtils.sendSuccessData(session, request, null, "应用安装完成");
            //有包名要应用自启
            if (StringUtils.isNotBlank(packageName)) {
                logger.debug("自启应用：{}",packageName);
                startApp(serial, packageName);
            }else {
                logger.warn("安装应用包名为空！");
            }
        } else {
            SessionUtils.sendFailureMessage(session, request, "应用安装失败");
        }
        //删除临时文件
        if (appFile != null && appFile.exists()) {
            appFile.delete();
        }
    }

    private void startApp(String serial, String packageName) {
        Process proc;
        String packageStr = null;
        try {
            String[] appCmd = new String[]{"adb","-s",serial,"shell","monkey","-p",packageName,"-v -v -v","1","|","grep","component="};
            proc = ProcessCmdUtils.execCmd(Arrays.asList(appCmd));
//            Runtime runtime = Runtime.getRuntime();
//            String pro = "adb -s " + serial + " shell monkey -p " + PackageName + " -v -v -v 1 | grep component=";
//            proc = runtime.exec(pro);
//            logger.debug(pro);
            if (proc.waitFor() != 0) {
            }
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    proc.getInputStream()));
            String line = null;
            while ((line = in.readLine()) != null) {
                if (line.indexOf("component=") > 0) {
                    packageStr = line;
                    break;
                }
            }
            if (!org.springframework.util.StringUtils.isEmpty(packageStr)) {
                packageStr = packageStr.substring(packageStr.lastIndexOf("=") + 1, packageStr.lastIndexOf(";"));
                List<String> cmdList1 = new ArrayList<>();
                cmdList1.add("adb");
                cmdList1.add("-s");
                cmdList1.add(serial);
                cmdList1.add("shell");
                cmdList1.add("am");
                cmdList1.add("start");
                cmdList1.add("-n");
                cmdList1.add(packageStr);
                proc = ProcessCmdUtils.execCmd(cmdList1);
//                pro = "adb -s " + serial + " shell am start -n " + packageStr;
//                proc = runtime.exec(pro);
//                logger.debug(pro);
                if (proc.waitFor() != 0) {
                }
            }
            logger.debug("[" +serial+ "]启动应用，包名:{}",packageName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void openBrowser(CmdRequest request) {
        Map<String, Object> data = request.getData();
        String url = data.get("url") + "";
        ActivityManagerServiceShellCommand command = new ActivityManagerServiceShellCommand("start", "-a", "android.intent.action.VIEW", "-d", url);
        asyncSendCommand(command);
        logger.debug("打开浏览器指定网站：{}", url);
        SessionUtils.sendSuccessData(session, request, null, "浏览器启动成功");
    }

    @Override
    public void setWifiStatus(CmdRequest request) {
        Map<String, Object> data = request.getData();
        Boolean enable = Boolean.parseBoolean(data.get("enable") + "");
        SwitchWifiCommand command = new SwitchWifiCommand(enable);
        asyncSendCommand(command);
        logger.debug("设置wifi状态：{}", enable);
        SessionUtils.sendSuccessData(session, request, null, "wifi设置成功");
    }

    @Override
    public void openSetting(CmdRequest request) {
        ActivityManagerServiceShellCommand command = new ActivityManagerServiceShellCommand("start", "com.android.settings/com.android.settings.Settings");
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "设置打开成功");
    }

    @Override
    public void deviceOpenCamera(CmdRequest request) {
        ActivityManagerServiceShellCommand command = new ActivityManagerServiceShellCommand("start", "-a", "android.media.action.STILL_IMAGE_CAMERA");
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "相机启动成功");
    }

    @Override
    public void restartDevice(CmdRequest request) {

    }

    @Override
    public void rotationScreen(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck textCheck = ParamCheck.build().name("rotation").type("int").rangeSelection().withIntValue(Arrays.asList(0, 1, 2, 3));
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, textCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        int rotation = (int) params.get("rotation");
        RotationScreenCommand rotationScreenCommand = null;
        switch (rotation) {
            case 0:
                rotationScreenCommand = new RotationScreenCommand(RotationScreenCommand.Rotation.ROTATION_0);
                break;
            case 1:
                rotationScreenCommand = new RotationScreenCommand(RotationScreenCommand.Rotation.ROTATION_90);
                break;
            case 2:
                rotationScreenCommand = new RotationScreenCommand(RotationScreenCommand.Rotation.ROTATION_180);
                break;
            case 3:
                rotationScreenCommand = new RotationScreenCommand(RotationScreenCommand.Rotation.ROTATION_270);
                break;
            default:
                SessionUtils.sendFailureMessage(session, request, "屏幕旋转方向参数传递错误");
                return;
        }
        asyncSendCommand(rotationScreenCommand);
        SessionUtils.sendSuccessData(session, request, null, "改变屏幕方向成功");
    }


    @Override
    public void clipboardCopy(CmdRequest request) {
        String data = null;
        if (null != syncAgentSession) {
            data = syncAgentSession.send(new ClipboardCommand());
        } else {
            SessionUtils.sendFailureMessage(session, request, "设备中复制粘贴服务还未准备好，请稍后再试");
            return;
        }
        logger.debug("获取手机【{}】剪切板的内容:{}", adbDevice.getSerial(), data);
        Map<String, Object> result = new HashMap<>();
        result.put("clipboardData", data);
        SessionUtils.sendSuccessData(session, request, result, "获取手机剪切板内容成功");
    }

    @Override
    public void clipboardPast(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck textCheck = ParamCheck.build().name("text").type("string").length(Integer.MAX_VALUE);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, textCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String text = (String) params.get("text");
        logger.debug("设置手机剪切板的值为：{}", text);
        asyncSendCommand(new ClipboardCommand(text));
        SessionUtils.sendSuccessData(session, request, null, "设置剪切板内容成功");
    }

    @Override
    public void inputSetText(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck textCheck = ParamCheck.build().name("text").type("string").length(Integer.MAX_VALUE);
        ParamCheck clearCheck = ParamCheck.build().name("clear").type("boolean");
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, textCheck, clearCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String text = (String) params.get("text");
        boolean clear = (boolean) params.get("clear");
        TextInputCommand command = new TextInputCommand(clear, text);
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "设置输入文本成功");
    }

    @Override
    public void inputType(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck textCheck = ParamCheck.build().name("text").type("string").length(Integer.MAX_VALUE);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, textCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String text = (String) params.get("text");
        logger.debug("输入文本：{}", text);
        TextInputCommand command = new TextInputCommand(text);
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "键盘输入成功");
    }

    @Override
    public void inputKeyPress(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck keyCodeCheck = ParamCheck.build().name("key").type("string");
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, keyCodeCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        String key = (String) params.get("key");
        int code = AndroidHotKeyCodeEnum.getCode(key);
        if (code < 0) {
            SessionUtils.sendFailureMessage(session, request, "热键[" + key + "]不存在");
            return;
        }
        KeyEventCommand command = new KeyEventCommand(KeyEventCommand.Action.DOWN, code);
        asyncSendCommand(command);
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {

        }
        command.setKeyEvent(KeyEventCommand.Action.UP);
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "热键处理成功");
    }

    @Override
    public void touchUp(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck xCheck = ParamCheck.build().name("x").type("double").withMin(0).withMax(catchParam.getWidth());
        ParamCheck yCheck = ParamCheck.build().name("y").type("double").withMin(0).withMax(catchParam.getHeight());
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, xCheck, yCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        Point point = calculatePoint(params);
        MotionEventCommand command = new MotionEventCommand(4098, 1, point.getX(), point.getY());
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "抬起成功");
    }

    @Override
    public void touchMove(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck xCheck = ParamCheck.build().name("x").type("double").withMin(0).withMax(catchParam.getWidth());
        ParamCheck yCheck = ParamCheck.build().name("y").type("double").withMin(0).withMax(catchParam.getHeight());
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, xCheck, yCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        Point point = calculatePoint(params);
        MotionEventCommand command = new MotionEventCommand(4098, 2, point.getX(), point.getY());
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "滑动成功");
    }

    private void initDeviceRequireParam() throws ParamMistakeException {
        logger.info("获取手机【{}】的实际宽高............................",this.serial);
        AndroidDeviceInitThread androidInitThread = AndroidDeviceManager.getInstance().getAndroidInitThread(adbDevice.getSerial());
        if (null == androidInitThread) {
            throw new ParamMistakeException(String.format("手机【%s】已离线,请稍后再试！", adbDevice.getSerial()));
        }
        Map<String, Integer> screenSizeMap = androidInitThread.getRealScreenSize();
        if (null == screenSizeMap) {
            throw new ParamMistakeException(String.format("获取手机【%s】实际宽高失败,请稍后再试！", adbDevice.getSerial()));
        }
        catchParam.setRealHeight(screenSizeMap.get("height"));
        catchParam.setRealWidth(screenSizeMap.get("width"));
        logger.info("手机【{}】的实际宽:{}高:{}............................", this.serial,catchParam.getRealWidth(), catchParam.getRealHeight());
    }

    @Override
    public void touchDown(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck xCheck = ParamCheck.build().name("x").type("double").withMin(0).withMax(catchParam.getWidth());
        ParamCheck yCheck = ParamCheck.build().name("y").type("double").withMin(0).withMax(catchParam.getHeight());
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, xCheck, yCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        Point point = calculatePoint(params);
        MotionEventCommand command = new MotionEventCommand(4098, 0, point.getX(), point.getY());
        asyncSendCommand(command);
        SessionUtils.sendSuccessData(session, request, null, "摁下成功");
    }

    private Point calculatePoint(Map<String, Object> data) {
        Double x = Double.parseDouble(data.get("x") + "");
        Double y = Double.parseDouble(data.get("y") + "");
        Double realX = x.doubleValue() / catchParam.getWidth() * catchParam.getRealWidth();
        Double realY = y.doubleValue() / catchParam.getHeight() * catchParam.getRealHeight();
        return new Point(realX.floatValue(), realY.floatValue());
    }

    @Override
    public void screenShot(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck tenantIdCheck = ParamCheck.build().name("tenantId").type("string").length(32);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, tenantIdCheck);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        byte[] screenShotData = null;
        if (null != syncAgentSession) {
            screenShotData = syncAgentSession.send(TakeScreenshotCommand.getInstance());
        }else {
            SessionUtils.sendFailureMessage(session, request, "设备中截图服务还未准备好，请稍后再试");
            return;
        }
        if (null != screenShotData && screenShotData.length > 0) { //截图
            logger.debug("收到手机【{}】截图,大小：{}", adbDevice.getSerial(), screenShotData.length);
            tenantId = (String) params.get("tenantId");
            //上传文件
            logger.debug("[{}]开始上传截图", adbDevice.getSerial());
            String serverAddress = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
            String pubUploadPath = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
            Attachment upload = null;
            try {
                upload = HttpUtils.upload(serverAddress + pubUploadPath, screenShotData, tenantId);
            } catch (Exception e) {
                logger.debug("[{}]上传截图失败", adbDevice.getSerial(),e);
            }
            if (null != upload && StringUtils.isNotBlank(upload.getId())) {
                logger.debug("文件上传成功，返回id：{}", upload.getId());
                Map<String, Object> result = new HashMap<>();
                result.put("urlPath", upload.getUrlPath());
                result.put("id", upload.getId());
                SessionUtils.sendSuccessData(session, request, result, "截图成功");
            } else {
                SessionUtils.sendFailureMessage(session, request, "上传截图失败");
            }
        } else {
            SessionUtils.sendFailureMessage(session, request, "未读到截图数据");
        }


    }





    @Override
    public void changeScreenQuality(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck quality = ParamCheck.build().name("quality").type("int").withMin(0).withMax(100);
        ParamCheck frameRate = ParamCheck.build().name("framerate").type("int").withMin(0).withMax(100);
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, quality, frameRate);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        int qualityUse = (int) params.get("quality");
        catchParam.setQuality(qualityUse);
        int frameRateUse = (int) params.get("framerate");
        catchParam.setFrameRate(frameRateUse);
        logger.debug("改变屏幕质量=====>width：{} height:{} quality:{} frameRate:{}", catchParam.getWidth(), catchParam.getHeight(), qualityUse, frameRateUse);
        AndroidScreenResponseThread responseThread = AndroidDeviceManager.getInstance().getScreenThread(adbDevice.getSerial());
        if (null != responseThread) {  //图片流
            ScreenStreamCommand screenStreamCommand = new ScreenStreamCommand(true, 0, catchParam.getWidth(), catchParam.getHeight(), frameRateUse, qualityUse);
            asyncSendCommand(screenStreamCommand);
        } else if (null != videoScreenResponseThread) {  //视频流
            videoScreenResponseThread.changeScreenQuality(qualityUse,frameRateUse);
        } else {
            SessionUtils.sendFailureMessage(session, request, "设备未连接，无需改变视频质量");
            return;
        }

    }

    @Override
    public void changeScreenSize(CmdRequest request) {
        Map<String, Object> data = request.getData();
        ParamCheck width = ParamCheck.build().name("width").type("int").withMin(0).withMax(catchParam.getRealWidth());
        ParamCheck height = ParamCheck.build().name("height").type("int").withMin(0).withMax(catchParam.getRealHeight());
        Map<String, Object> params = null;
        try {
            params = checkParam(data, false, width, height);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        int widthUse = (int) params.get("width");
        catchParam.setWidth(widthUse);
        int heightUse = (int) params.get("height");
        catchParam.setHeight(heightUse);
        logger.debug("改变宽高屏幕=====>width：{} height:{} quality:{} frameRate:{}", widthUse, heightUse, catchParam.getQuality(), catchParam.getFrameRate());
        AndroidScreenResponseThread responseThread = AndroidDeviceManager.getInstance().getScreenThread(adbDevice.getSerial());
        if (null != responseThread) {  //图片流
            ScreenStreamCommand screenStreamCommand = new ScreenStreamCommand(true, 0, widthUse, heightUse, catchParam.getFrameRate(), catchParam.getQuality());
            asyncSendCommand(screenStreamCommand);
        } else if (null != videoScreenResponseThread) {  //视频流
            convertWidthAndHeight();
            videoScreenResponseThread.changeScreenSize(catchParam.getVideoWidth(),catchParam.getVideoHeight());
        } else {
            SessionUtils.sendFailureMessage(session, request, "设备未连接，无需改变视频宽高");
            return;
        }

    }


    @Override
    public void handleScreenOn(CmdRequest request) {
        Map<String, Object> data = request.getData();
        String type = (String) data.get("type");
        if (null == type) {
            SessionUtils.sendFailureMessage(session, request, "请选择视频流的方式投屏还是图片流的方式");
            return;
        }
        ParamCheck width = ParamCheck.build().name("width").type("int").withMin(0).withMax(catchParam.getRealWidth());
        ParamCheck height = ParamCheck.build().name("height").type("int").withMin(0).withMax(catchParam.getRealHeight());
        ParamCheck quality = ParamCheck.build().name("quality").type("int").withMin(0).withMax(100);
        ParamCheck framerate = ParamCheck.build().name("framerate").type("int").withMin(0).withMax(200);
        ParamCheck rotation = ParamCheck.build().name("rotation").type("int").rangeSelection().withIntValue(Arrays.asList(0, 1, 2, 3));
        Map<String, Object> commonParam = null;
        try {
            commonParam = checkParam(data, false, width, height, quality, framerate,rotation);
        } catch (Exception e) {
            logger.error("参数校验失败！", e);
            if (e instanceof ParamMistakeException) {
                SessionUtils.sendFailureMessage(session, request, e.getMessage());
                return;
            }
        }
        int widthUse = (int) commonParam.get("width");
        catchParam.setWidth(widthUse);
        int heightUse = (int) commonParam.get("height");
        catchParam.setHeight(heightUse);
        int qualityUse = (int) commonParam.get("quality");
        catchParam.setQuality(qualityUse);
        int frameRateUse = (int) commonParam.get("framerate");
        catchParam.setFrameRate(frameRateUse);
        int rotationUse = (int) commonParam.get("rotation");
        catchParam.setRotation(rotationUse);
        if ("image".equals(type)) {
            logger.debug("接收到开启屏幕指令=====>width：{} height:{} quality:{} frameRate:{},rotation:{}", widthUse, heightUse, qualityUse, frameRateUse, rotationUse);
            AndroidScreenResponseThread responseThread = AndroidDeviceManager.getInstance().getScreenThread(adbDevice.getSerial());
            if (null == responseThread || responseThread.isInterrupted() || !responseThread.isAlive()) {
                //之前未开启过设备屏幕获取线程则新建
                logger.debug("设备【{}】新建屏幕获取线程------",serial);
                responseThread = new AndroidScreenResponseThread(adbDevice);
                responseThread.startSendScreenToWeb(session,catchParam);
                responseThread.setScreenOnRequest(session,request);
                responseThread.start();
                AndroidDeviceManager.getInstance().saveDeviceScreenThread(adbDevice.getSerial(), responseThread);
            } else {
                //之前开启过则复用原屏幕获取线程
                logger.debug("设备【{}】复用原屏幕获取线程=====",serial);
                responseThread.startSendScreenToWeb(session,catchParam);
                responseThread.setScreenOnRequest(session,request);
            }
        } else if ("video".equals(type)) {
            //视频流专属的参数
            convertWidthAndHeight();
            ParamCheck display = ParamCheck.build().name("display").type("int").rangeSelection().withIntValue(Arrays.asList(0, 1, 2, 3));
            Map<String, Object> params = null;
            try {
                params = checkParam(data, false, display);
            } catch (Exception e) {
                logger.error("参数校验失败！", e);
                if (e instanceof ParamMistakeException) {
                    SessionUtils.sendFailureMessage(session, request, e.getMessage());
                    return;
                }
            }
            int displayUse = (int) params.get("display");
            catchParam.setDisplay(displayUse);
            if (null != videoScreenResponseThread && !videoScreenResponseThread.isInterrupted() && videoScreenResponseThread.isAlive()) {
                logger.info("设备【{}】的视频流的线程已经存在，先退出重新弄一个",adbDevice.getSerial());
                videoScreenResponseThread.stopAndExit();
                while (videoScreenResponseThread.isAlive()) {
                    logger.info("等待上一个视频流线程退出干净中。。。。");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            videoScreenResponseThread = new AndroidVideoScreenResponseThread(session, adbDevice, catchParam);
            videoScreenResponseThread.setScreenOnRequest(request);
            videoScreenResponseThread.start();
        } else {
            SessionUtils.sendFailureMessage(session, request, "只支持图片流或者视频流的方式投屏");
            return;
        }

    }

    private void convertWidthAndHeight() {
        int width = catchParam.getWidth();
        int height = catchParam.getHeight();
        Integer realWidth = catchParam.getRealWidth();
        Integer realHeight = catchParam.getRealHeight();
        logger.debug("转换前的宽：{}，高：{}",width,height);
        if (catchParam.getRealWidth() < catchParam.getRealHeight()) {  //宽小于高
            double rate = realHeight.doubleValue() / realWidth.doubleValue();
            int pre = realWidth;   //前一个数
            int suf = realWidth / 2; //后一个数
            while (suf > width && suf > 320) {
                pre = suf;
                suf = suf / 2;
            }
            if ((pre - width < width - suf) || suf <= 320) {
                width = pre;
            } else {
                width = suf;
            }
            height = Double.valueOf(width * rate).intValue();
            if (height % 2 == 1) {
                height = height + 1;
            }
        } else {
            int pre = realHeight;   //前一个数
            int suf = realHeight / 2; //后一个数
            double rate = realWidth.doubleValue() / realHeight.doubleValue();
            while (suf > height && suf > 320) {
                pre = suf;
                suf = suf / 2;
            }
            if ((pre - height < height - suf) || suf <= 320) {
                height = pre;
            } else {
                height = suf;
            }
            width = Double.valueOf(height * rate).intValue();
            if (width % 2 == 1) {
                width = width + 1;
            }
        }
        logger.debug("转换前的宽：{}，高：{}",width,height);
        catchParam.setVideoWidth(width);
        catchParam.setVideoHeight(height);
    }

    @Override
    public void stopAndExit() {
        AndroidDeviceManager.getInstance().stopWebScreen(adbDevice.getSerial(),session);
        if (null != videoScreenResponseThread) {
            videoScreenResponseThread.stopAndExit();
        }
        if (null != asyncAgentSession) {
            asyncAgentSession.closeSilence();
        }
        if (null != syncAgentSession) {
            syncAgentSession.closeSilence();
        }
        if (null != androidPerfThread) {
            androidPerfThread.stopThread();
        }
        if (null != androidLogThread) {
            if (tenantId == null) {
                androidLogThread.killLog();
            } else {
                androidLogThread.stopLog(tenantId);
            }
        }
        if (StringUtils.isNotBlank(logPath)) {
            File logFile = new File(logPath);
            if (null != logFile && logFile.exists()) {
                logFile.delete();
                logger.debug("临时日志文件已删除");
            }
        }
        cmdQueues.clear();
        interrupt();
    }


    public void sendScreenChange(Map<String, Object> data) {
        int rotation = (int) data.get("rotation");
        int realWidth = (int) data.get("realWidth");
        int realHeight = (int) data.get("realHeight");
        catchParam.setRealWidth(realWidth);
        catchParam.setRealHeight(realHeight);
        catchParam.setRotation(rotation);
        data.put("width", catchParam.getWidth());
        data.put("height", catchParam.getHeight());
        SessionUtils.sendMessageInitiative(session, ResponseCmd.DISPLAY_CHANGE, adbDevice.getSerial(), data, "设备的屏幕转了一下");
    }

    public void sendWifiChange(Map<String, Object> data) {
        SessionUtils.sendMessageInitiative(session, ResponseCmd.WIFI_CHANGE, adbDevice.getSerial(), data, "设备的wifi状态改变了");
    }

    private void asyncSendCommand(ProtocolCommand command){
        if (null != asyncAgentSession) {
            asyncAgentSession.send(command);
        } else {
            logger.warn("设备【{}】的agent程序异常，稍后再试............",adbDevice.getSerial());
        }
    }
}
