package net.northking.cctp.upperComputer.deviceManager.utils;

import com.dd.plist.NSDictionary;
import com.dd.plist.NSObject;
import com.dd.plist.PropertyListParser;
import net.northking.cctp.upperComputer.constants.WebDriverAgentState;
import net.northking.cctp.upperComputer.deviceManager.AbstractDeviceManager;
import net.northking.cctp.upperComputer.deviceManager.UpperComputerManager;
import net.northking.cctp.upperComputer.driver.usbmuxd.AppleDevice;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;

/**
 * macOS上xcodebuild命令的调用封装
 */
public class XcodeBuild {

    private static Logger logger = LoggerFactory.getLogger(AbstractDeviceManager.class);

    public static final double MIN_SYSTEM_VERSION = 14;

    /**
     * iOS开始使用CoreDevice框架的版本
     */
    public static final int IOS_CORE_DEVICE_VERSION = 17;

    public static final String XCODE_BUILD_PATH = "/usr/bin/xcodebuild";

    public static final String WEB_DRIVER_AGENT_PROJECT_NAME = "WebDriverAgent.xcodeproj";

    public static final String WEB_DRIVER_AGENT_PROJECT_SCHEME = "WebDriverAgentRunner";

    private static XcodeBuild instance = null;

    public static XcodeBuild getInstance() {
        if (instance == null) {
            instance = new XcodeBuild();
        }
        return instance;
    }

    /**
     * WDA项目地址
     */
    private String webDriverAgentProjectPath = null;

    /**
     * 配置好Xcode的用户名称
     * <br>
     * 需要使用里面的开发者帐号信息
     */
    private String xcodeUserName = null;

    /**
     * 当前选择的Xcode版本
     */
    private String currentXcodeVersion = null;

    /**
     * xcode编译生成的WEB_DRIVER_AGENT_PROJECT_SCHEME_sdkname sdkversion-aarch.xctestrun(WebDriverAgentRunner_iphoneos**.0-arm64.xctestrun)
     * <br>
     * 此值通过执行buildWda方法赋值
     */
    private String defaultXctestPath = null;

    /**
     * xcode编译生成的WEB_DRIVER_AGENT_PROJECT_SCHEME_sdkname sdkversion-aarch.xctestrun(WebDriverAgentRunner_iphoneos16.0-arm64.xctestrun)
     * <br>
     * 此值通过执行buildSdk16Wda方法赋值
     */
    private String sdk16XctestPath = null;

    /**
     * 独立日志保存路径，与log4j无关连，如果为null，则不进行相关逻辑单独的日志保存
     * <br>
     * 此效果通常用于开发和排查错误时使用
     */
    private String logPath = null;

    /**
     * 是否将xcodebuild构建wda的输出输出到日志上
     */
    public boolean printBuildOutput = true;

    /**
     * 是否将wda的运行输出输出到日志上
     */
    public boolean printWdaOutput = true;

    /**
     * 持有设备的WebDriverAgentRunner
     * <br>
     * 多线程使用注意锁
     */
    private final HashMap<String, WebDriverAgentRunner> runnerMap = new HashMap<>();

    private XcodeBuild() {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            synchronized (runnerMap) {
                logger.debug("开始清理WebDriverAgentRunner，共：" + runnerMap.size());
                runnerMap.values().forEach(WebDriverAgentRunner::stop);
                logger.debug("WebDriverAgentRunner清理完毕");
            }
        }));
    }

    /**
     * 切换Xcode版本
     *
     * @param targetVersionXcodeAppPath 目标版本Xcode.app路径，需要提供到Xcode.app结尾
     * @return 是否切换成功
     */
    public synchronized boolean switchXcodeVersion(String targetVersionXcodeAppPath) {
        if (!targetVersionXcodeAppPath.endsWith("Xcode.app")) {
            logger.error("切换的版本的Xcode路径应为Xcode.app结尾");
            return false;
        }
        File appDir = new File(targetVersionXcodeAppPath);
        if (!appDir.exists()) {
            logger.error("指定的Xcode路径不存在：" + appDir.getAbsolutePath());
            return false;
        }
        if (!appDir.canRead()) {
            logger.error("指定的Xcode路径无法读取：" + appDir.getAbsolutePath());
            return false;
        }
        File versionFile = new File(appDir.getAbsolutePath() + "/Contents/version.plist");
        if (!versionFile.exists()) {
            logger.error("指定Xcode路径不存在版本信息文件");
            return false;
        }
        if (!versionFile.canRead()) {
            logger.error("指定Xcode路径下的版本信息无法读取");
            return false;
        }
        NSObject obj;
        try {
            obj = PropertyListParser.parse(versionFile);
        } catch (Exception e) {
            logger.error("读取并解析Xcode版本信息文件时发生错误", e);
            return false;
        }
        if (!(obj instanceof NSDictionary)) {
            logger.error("版本信息解析不符合期望格式：\n" + obj.toXMLPropertyList());
            return false;
        }
        NSDictionary versionInfo = (NSDictionary) obj;
        if (!versionInfo.containsKey("CFBundleShortVersionString")) {
            logger.error("无法获取指定Xcode版本名");
            return false;
        }

        File selectPath = new File(appDir.getAbsolutePath() + "/Contents/Developer");
        if (!selectPath.exists()) {
            logger.error("无法切换Xcode到指定位置的版本，目标位置Contents/Developer不存在");
            return false;
        }

        ProcessBuilder pb = new ProcessBuilder("xcode-select", "-s", selectPath.getAbsolutePath());
        Process xcodeSelectProgress = null;
        try {
            xcodeSelectProgress = pb.start();
            int exitValue = xcodeSelectProgress.waitFor();
            if (exitValue != 0) {
                logger.error("xcode-select退出码为" + exitValue + "，版本切换失败");
                return false;
            }
        } catch (IOException e) {
            logger.error("执行xcode-select切换Xcode版本时发生错误", e);
        } catch (InterruptedException e) {
            logger.error("中断执行xcode-select");
            xcodeSelectProgress.destroy();
            return false;
        }

        currentXcodeVersion = versionInfo.get("CFBundleShortVersionString").toString();

        logger.info("Xcode版本切换为：" + currentXcodeVersion);
        return true;
    }

    /**
     * 构建WebDriverAgent
     * <br>
     * 如果出现CodeSign相关错误，则为未解锁钥匙串
     *
     * @param outputPath 构建输出目录
     * @return 构建结果xctestrun路径, null为
     */
    public synchronized String buildWda(String keychainPassword, String outputPath) {
        if (StringUtils.isBlank(xcodeUserName)) {
            logger.error("未指定Xcode用户名，无法构建WebDriverAgent");
            return null;
        }

        if (StringUtils.isBlank(webDriverAgentProjectPath)) {
            logger.error("未指定WebDriverAgent项目路径");
            return null;
        }

        File outputFileDir = new File(outputPath);
        if (!outputFileDir.exists()) {
            if (!outputFileDir.mkdirs()) {
                logger.error("创建构建WebDriverAgent输出目录失败");
                return null;
            }
        }
        if (!outputFileDir.isDirectory()) {
            logger.error("指定WebDriverAgent的构建输出路径不是一个文件夹");
            return null;
        }
        if (!outputFileDir.canWrite()) {
            logger.error("指定WebDriverAgent的构建输出路径无法写入");
            return null;
        }
        if (!outputFileDir.canRead()) {
            logger.error("指定WebDriverAgent的构建输出路径无法读取");
            return null;
        }

        try {
            Runtime.getRuntime().exec("chown " + xcodeUserName + ":staff " + outputFileDir.getAbsolutePath());
        } catch (IOException e) {
            logger.error("无法修改输出目录所有者");
            return null;
        }

        StringBuilder sb = new StringBuilder();
        sb.append("#/bin/sh\n")
                .append("security unlock-keychain -p \"")
                .append(keychainPassword)
                .append(String.format("\" /Users/%s/Library/Keychains/login.keychain;\n",xcodeUserName));

        List commandLine = Arrays.asList(
                XCODE_BUILD_PATH,
                "-project",
                webDriverAgentProjectPath + "/" + WEB_DRIVER_AGENT_PROJECT_NAME,
                "-scheme",
                WEB_DRIVER_AGENT_PROJECT_SCHEME,
                "-derivedDataPath",
                outputPath,
                "-sdk",
                "iphoneos",
                "-allowProvisioningUpdates",
                "build-for-testing");
        commandLine.forEach(text -> {
            sb.append(text);
            sb.append(" ");
        });
        sb.append(";");
        File buildScript = new File(UpperComputerManager.getInstance().getApplicationPath() + "/buildWda.sh");
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(buildScript);
            fileOutputStream.write(sb.toString().getBytes(StandardCharsets.UTF_8));
            Runtime.getRuntime().exec("chown " + xcodeUserName + ":staff " + buildScript.getAbsolutePath());
        } catch (IOException e) {
            logger.error("创建构建脚本时发生IO错误");
            if (!buildScript.delete()) {
                logger.error("删除" + buildScript.getAbsolutePath() + "失败");
            }
            return null;
        }
        if (!buildScript.setExecutable(true)) {
            logger.error("处理构建脚本权限失败");
            if (!buildScript.delete()) {
                logger.error("删除" + buildScript.getAbsolutePath() + "失败");
            }
            return null;
        }
        ProcessBuilder processBuilder = new ProcessBuilder(
                "sudo",
                "-u",
                xcodeUserName,
                "-s",
                buildScript.getAbsolutePath()
        );
        processBuilder.redirectErrorStream(true);
        Process process;
        try {
            process = processBuilder.start();
        } catch (IOException e) {
            logger.error("启动xcodebuild时发生IO异常", e);
            if (!buildScript.delete()) {
                logger.error("删除" + buildScript.getAbsolutePath() + "失败");
            }
            return null;
        }
        BufferedReader lineReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        FileOutputStream buildLogOutput = null;
        if (logPath != null) {
            try {
                buildLogOutput = new FileOutputStream(logPath + "/" + "BuildWda-" + getLogFileNameDateTime() + ".log");
            } catch (FileNotFoundException e) {
                logger.error("尝试写入构建WDA的独立日志时发生异常", e);
            }
        }
        boolean buildSuccessFlag = false;
        do {
            try {
                String line = lineReader.readLine();
                if (line == null) {
                    break;
                } else {
                    if (line.contains("TEST BUILD SUCCEEDED")) {
                        buildSuccessFlag = true;
                    }
                    if (printBuildOutput) {
                        logger.info("xcodebuild->" + line);
                    }
                    if (buildLogOutput != null) {
                        buildLogOutput.write(line.getBytes());
                        buildLogOutput.write(10);
                        buildLogOutput.write(13);
                    }
                }
            } catch (IOException e) {
                logger.error("执行xcodebuild时发生IO异常", e);
                if (!buildScript.delete()) {
                    logger.error("删除" + buildScript.getAbsolutePath() + "失败");
                }
                return null;
            }
        } while (!Thread.currentThread().isInterrupted());
        if (buildLogOutput != null) {
            try {
                buildLogOutput.close();
            } catch (IOException e) {
                logger.warn("关闭构建WDA独立日志写入时发生IO异常，路径" + logPath + "/" + "BuildWda-" + getLogFileNameDateTime() + ".log", e);
            }
        }
        try {
            int code = process.waitFor();
            if (code != 0) {
                logger.error("xcodebuild异常退出，状态码：" + code + "，请检查xcodebuild输出");
            }
        } catch (InterruptedException e) {
            logger.warn("线程中断");
            if (!buildScript.delete()) {
                logger.error("删除" + buildScript.getAbsolutePath() + "失败");
            }
            return null;
        }

        if (!buildScript.delete()) {
            logger.error("删除" + buildScript.getAbsolutePath() + "失败");
        }

        File xctestrunDir = new File(outputPath + "/Build/Products");
        File xctestrunFile = null;
        File[] xctestRunFiles = xctestrunDir.listFiles((dir, name) -> name.startsWith(WEB_DRIVER_AGENT_PROJECT_SCHEME) && name.endsWith(".xctestrun"));
        if (xctestRunFiles == null || xctestRunFiles.length == 0) {
            logger.error("未找到构建生成的xctestrun文件");
            return null;
        }
        xctestrunFile = xctestRunFiles[0];
        String xctestPath = xctestrunFile.getAbsolutePath();
        logger.info("构建结果：" + xctestPath);
        if (!buildSuccessFlag) {
            logger.error("构建失败");
            return null;
        }
        return xctestPath;
    }

    public void setDefaultXctestPath(String defaultXctestPath) {
        this.defaultXctestPath = defaultXctestPath;
    }

    public void setSdk16XctestPath(String sdk16XctestPath) {
        this.sdk16XctestPath = sdk16XctestPath;
    }

    public void setWebDriverAgentProjectPath(String webDriverAgentProjectPath) {
        this.webDriverAgentProjectPath = webDriverAgentProjectPath;
    }

    public void setLogPath(String logPath) {
        File logDir = new File(logPath);
        if (!logDir.exists()) {
            if (!logDir.mkdirs()) {
                logger.error("创建独立日志目录失败");
                return;
            }
        }
        if (!logDir.isDirectory()) {
            logger.error("指定的独立日志路径" + logDir.getAbsolutePath() + "实际上并不是目录");
            return;
        }
        if (!logDir.canWrite()) {
            logger.error("指定的独立日志路径" + logDir.getAbsolutePath() + "无法写入");
            return;
        }
        this.logPath = logPath;
    }

    public boolean checkEnvironment() {
        if (StringUtils.isBlank(xcodeUserName)) {
            logger.error("未配置Xcode用户");
            return false;
        }
        if (!checkSystem()) {
            logger.error("不支持的操作系统和CPU架构");
            return false;
        }
        logger.debug("环境检查通过");
        if (!checkProject()) {
            logger.error("WebDriverAgent项目检查失败");
            return false;
        }
        logger.debug("WebDriverAgent检查通过");
        return true;
    }

    public void setXcodeUserName(String xcodeUserName) {
        this.xcodeUserName = xcodeUserName;
        logger.debug("指定Xcode用户：" + xcodeUserName);
    }

    /**
     * 检查系统和指令集架构
     * <br>
     * 不是arm的MacOS不要再使用了，即使勉强可以用。
     *
     * @return 是否符合要求
     */
    private boolean checkSystem() {
        String osName = System.getProperty("os.name");
        if (!osName.startsWith("Mac")) {
            logger.error("系统名称检查不通过：" + osName);
            return false;
        }
        String osVersion = System.getProperty("os.version");
        try {
            Integer osVersionNumber = Integer.parseInt(osVersion.substring(0, osVersion.indexOf(".")));
            if (osVersionNumber >= MIN_SYSTEM_VERSION) {
                return true;
            } else {
                logger.error("系统版本号检查不通过：" + osVersionNumber);
                return false;
            }
        } catch (NumberFormatException e) {
            logger.error("检查系统版本时版本号解析出错：" + osVersion, e);
            return false;
        }
    }

    public String getCurrentXcodeVersion() {
        return currentXcodeVersion;
    }

    /**
     * 获得当前xcodebuild版本
     *
     * @return 版本号，null为获取失败
     */
    public String getCurrentXcodebuildVersion() {
        Process process;
        try {
            ProcessBuilder builder = new ProcessBuilder(XCODE_BUILD_PATH, "-version");
            builder.redirectErrorStream(true);
            process = builder.start();
        } catch (IOException e) {
            logger.error("执行xcodebuild -version时发生IO异常", e);
            return null;
        }
        InputStream inputStream = process.getInputStream();
        ByteArrayOutputStream bufferArray = new ByteArrayOutputStream();
        int count = 0;
        byte[] buffer = new byte[4096];
        do {
            try {
                count = inputStream.read(buffer);
            } catch (IOException e) {
                try {
                    inputStream.close();
                } catch (IOException ignored) {

                }
                return null;
            }
            if (count >= 1) {
                bufferArray.write(buffer, 0, count);
            }
        } while (count > 0);

        String xcodebuildReturn = bufferArray.toString();
        logger.debug("xcodebuild -version执行结果：\n" + xcodebuildReturn);
        String[] outputLines = xcodebuildReturn.split("\n");
        if (outputLines.length != 2) {
            logger.error("xcodebuild -version输出行数不符合预期");
            return null;
        }
        String firstLine = outputLines[0];
        if (!firstLine.startsWith("Xcode ") || firstLine.length() < 8) {
            logger.error("xcodebuild -version输出了预期之外的内容：" + firstLine);
            return null;
        }
        return firstLine.substring(6);
    }

    /**
     * 查询当前Xcode路径
     *
     * @return 绝对路径，null为查询失败
     */
    private String getCurrentXcodePath() {
        ProcessBuilder pb = new ProcessBuilder("/usr/bin/xcode-select", "-p");
        Process process;
        try {
            process = pb.start();
        } catch (IOException e) {
            logger.error("运行xcode-select查询当前Xcode路径时发生错误", e);
            return null;
        }
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = null;
        String path = null;
        do {
            try {
                line = reader.readLine();
            } catch (IOException e) {
                logger.error("读取xcode-select输出Xcode路径时发生IO错误");
                break;
            }
            if (line != null && line.endsWith("/Contents/Developer")) {
                path = line.substring(0, line.indexOf("/Contents/Developer"));
            }

        } while (line != null && !Thread.currentThread().isInterrupted());
        try {
            reader.close();
        } catch (IOException e) {
            logger.error("关闭xcode-select读取流时发生IO错误", e);
        }
        if (line == null) {
            logger.error("读取xcode-select输出Xcode路径失败，没有输出任何东西");
            return null;
        }

        return path;
    }

    /**
     * 检查给定的版本字符串的大版本与给定的大版本号的差
     *
     * @param versionString 版本字符串，通常有.，例如15.2
     * @param targetVersion 希望比较的版本
     * @return null则为处理失败，小于0则表示给定的版本字符串版本比给定的版本低，等于0则表示两者相等，大于0则表示高于给定的版本
     */
    private Integer checkVersionString(String versionString, int targetVersion) {
        int firstDotIndex = versionString.indexOf(".");
        if (firstDotIndex < 1) {
            logger.error("给定的版本字符串中没有找到.分隔符：" + versionString);
            return null;
        }
        String mainVersionString = versionString.substring(0, firstDotIndex);
        try {
            Integer mainVersion = Integer.parseInt(mainVersionString);
            return targetVersion - mainVersion;
        } catch (NumberFormatException e) {
            logger.error("处理版本字符串时发生数字格式错误：" + mainVersionString);
            return null;
        }
    }

    private boolean checkProject() {
        if (webDriverAgentProjectPath == null) {
            logger.error("未提供WebDriverAgent项目地址");
            return false;
        }
        File projectFile = new File(webDriverAgentProjectPath + "/" + WEB_DRIVER_AGENT_PROJECT_NAME);
        if (!projectFile.exists()) {
            logger.error("WebDriverAgent项目文件不存在，找不到：" + projectFile.getAbsolutePath());
            return false;
        }
        return true;
    }

    private String getLogFileNameDateTime() {
        Calendar calendar = Calendar.getInstance();
        return String.format("%s%d%d_%d-%d-%d",String.valueOf(calendar.get(Calendar.YEAR)), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
    }

    /**
     * 在指定设备上运行WebDriverAgent
     *
     * @param appleDevice 要运行WebDriverAgent的设备
     * @return 操作是否成功
     */
    public boolean runWebDriverAgent(AppleDevice appleDevice, OutputLineCallback callback) {
        boolean result = true;
        try {
            WebDriverAgentRunner runner;
            synchronized (runnerMap) {
                runner = runnerMap.get(appleDevice.getSerial());
                if (runner == null) {
                    runner = new WebDriverAgentRunner(appleDevice, callback);
                    runnerMap.put(appleDevice.getSerial(), runner);
                    runner.start();
                }
            }
        } catch (Exception e) {
            result = false;
            logger.error("尝试为设备" + appleDevice.getSerial() + "运行WebDriverAgent时发生错误", e);
        }
        return result;
    }

    /**
     * 获得指定设备的WebDriverAgent状态
     *
     * @param appleDevice 要获取状态的苹果设备
     * @return WebDriverAgent状态
     */
    public WebDriverAgentState getWebDriverAgentState(AppleDevice appleDevice) {
        WebDriverAgentRunner runner;
        synchronized (runnerMap) {
            runner = runnerMap.get(appleDevice.getSerial());
        }
        if (runner == null) {
            return WebDriverAgentState.NO_RUN;
        }
        if (runner.exitCode != null) {
            return WebDriverAgentState.EXIT;
        }
        if (runner.isFBServerReady) {
            return WebDriverAgentState.READY;
        }
        return WebDriverAgentState.STARTING;
    }

    /**
     * 停止指定设备的WebDriverAgent
     *
     * @param appleDevice 要停止运行WebDriverAgent的设备
     */
    public void stopWebDriverAgent(AppleDevice appleDevice) {
        WebDriverAgentRunner runner;
        synchronized (runnerMap) {
            runner = runnerMap.get(appleDevice.getSerial());
        }
        if (runner != null) {
            runner.stop();
        }
    }

    public String getDefaultXctestPath() {
        return defaultXctestPath;
    }

    public String getSdk16XctestPath() {
        return sdk16XctestPath;
    }

    public class WebDriverAgentRunner implements Runnable {

        /**
         * xcodebuild进程信息
         */
        private Process process = null;

        /**
         * xcodebuild的退出码
         */
        private Integer exitCode = null;
        private String logFilePath = null;
        private final Thread thread;
        private final AppleDevice appleDevice;

        private final String xctestPath;

        private final OutputLineCallback outputLineCallback;

        private boolean isFBServerReady = false;

        private final Object waitFbServerReadyLock = new Object();

        public WebDriverAgentRunner(AppleDevice appleDevice, OutputLineCallback outputLineCallback) {
            this.outputLineCallback = outputLineCallback;
            int productMainVersion = appleDevice.getProductMainVersion();
            if (productMainVersion < IOS_CORE_DEVICE_VERSION) {
                this.xctestPath = XcodeBuild.this.defaultXctestPath;
            } else {
                this.xctestPath = sdk16XctestPath;
            }
            if (xctestPath == null) {
                throw new IllegalStateException("未准备好iOS " + productMainVersion + "的xctest");
            }
            this.appleDevice = appleDevice;
            if (logPath != null) {
                logFilePath = logPath + "/" + "WDA-" + appleDevice.getConnectionDetail().serialNumber + "-" + getLogFileNameDateTime();
            }
            thread = new Thread(this);
            thread.setName("WDA-Runner-" + appleDevice.getConnectionDetail().serialNumber);
        }

        private void start() {
            if (thread.isAlive() || process != null) throw new IllegalStateException("已经启动");
            ProcessBuilder processBuilder = new ProcessBuilder(
                    "sudo",
                    "-u",
                    xcodeUserName,
                    "xcodebuild",
                    "test-without-building",
                    "-xctestrun",
                    xctestPath,
                    "-destination",
                    "id=" + appleDevice.getConnectionDetail().serialNumber
            );
            processBuilder.redirectErrorStream(true);
            try {
                process = processBuilder.start();
            } catch (IOException e) {
                logger.error("启动WDA时发生IO异常", e);
                return;
            }
            thread.start();
        }

        public void stop() {
            if (process != null) {
                process.destroy();
                try {
                    exitCode = process.waitFor();
                } catch (InterruptedException e) {
                    logger.warn("未能等待获取退出码：线程被中断");
                }
            }
            if (thread != null && !thread.isInterrupted()) {
                thread.interrupt();
            }
        }

        @Override
        public void run() {
            FileOutputStream logOutput = null;
            if (logFilePath != null) {
                try {
                    logOutput = new FileOutputStream(logFilePath);
                } catch (FileNotFoundException e) {
                    logger.error("尝试创建独立日志文件时发生异常", e);
                }
            }

            BufferedReader bufferReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            do {
                try {
                    String line = bufferReader.readLine();
                    if (line == null) {
                        break;
                    } else {
                        if (printWdaOutput) {
                            logger.info("WDA-" + appleDevice.getConnectionDetail().serialNumber + "->" + line);
                        }
                        if (!isFBServerReady && line.contains("ServerURLHere")) {
                            isFBServerReady = true;
                            synchronized (waitFbServerReadyLock) {
                                waitFbServerReadyLock.notifyAll();
                            }
                        }
                        if (logOutput != null) {
                            logOutput.write(line.getBytes());
                            logOutput.write(10);
                            logOutput.write(13);
                        }
                        if (outputLineCallback != null) {
                            try {
                                outputLineCallback.onLine(line);
                            } catch (Exception e) {
                                logger.error("行回调中发生错误", e);
                            }
                        }
                    }
                } catch (IOException e) {
                    logger.warn("读取WDA输出时发生IO异常，设备" + appleDevice.getConnectionDetail().serialNumber, e);
                    break;
                }
            } while (!thread.isInterrupted());

            try {
                exitCode = process.waitFor();
            } catch (InterruptedException ignored) {

            }

            if (logOutput != null) {
                try {
                    logOutput.close();
                } catch (IOException e) {
                    logger.warn("关闭WDA独立日志流时发生IO异常，路径" + logFilePath, e);
                }
            }
        }

        public Process getProcess() {
            return process;
        }

        public Integer getExitCode() {
            return exitCode;
        }

        public String getLogPath() {
            return logPath;
        }

        public void waitFbServer() {
            synchronized (waitFbServerReadyLock) {
                if (!isFBServerReady) {
                    try {
                        waitFbServerReadyLock.wait();
                    } catch (InterruptedException ignored) {
                    }
                }
            }

        }

    }


}
