package net.northking.cctp.upperComputer.driver.adb;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 提供运行Adb Server的能力
 */
public class AdbServerRunner {
    /**
     * adb可执行程序的路径
     */
    private String adbExecutePath = null;

    /**
     * 打印adbd运行输出
     * <p>
     * 修改此值立即生效
     */
    public static final boolean PRINT_ADB_SERVER_OUTPUT = true;

    /**
     * 运行状态
     */
    private AdbServerStatus status = AdbServerStatus.NOT_RUN;

    /**
     * 状态监听
     */
    private StatusListener mStatusListener = null;

    /**
     * adb server运行进程
     */
    private Process mAdbdProcess = null;


    /**
     * adb server 输出信息处理线程
     */
    private AdbInputThread mAdbInputThread = null;

    /**
     * adb server 错误信息处理线程
     */
    private AdbErrorOutputThread mAdbErrorOutputThread = null;

    private static final Logger log = LoggerFactory.getLogger(AdbServerRunner.class);

    public AdbServerRunner() throws AdbNotFoundException {
        this(null);
    }

    public AdbServerRunner(String adbPath) throws AdbNotFoundException {
        if (adbPath == null || adbPath.isEmpty()) {
            log.info("未指定adb路径，自动探测");
            if (!autoDetectedAdbExecutePath()) {
                log.error("未找到有效adb可执行文件");
                throw new AdbNotFoundException();
            }
        } else {
            File file = new File(adbPath);
            if (file.isFile() && file.canExecute()) {
                adbExecutePath = adbPath;
            } else {
                log.error("提供的adb:" + adbPath + "不是一个可执行文件或没有执行权限");
                throw new AdbNotFoundException();
            }
        }
        log.info("使用adb:" + adbExecutePath);
    }

    /**
     * 开始执行
     * <p>
     * 此方法不会阻塞当前线程
     *
     * @throws IOException 执行adb可执行程序时发生io异常
     */
    public void start() throws IOException {
        if (status == AdbServerStatus.RUNNING)
            throw new IllegalStateException("adb server [" + adbExecutePath + "] 已经正在运行");
        Process process;
        try {
            Runtime.getRuntime().exec(adbExecutePath + " kill-server");
            process = Runtime.getRuntime().exec(adbExecutePath + " nodaemon server");
            mAdbdProcess = process;
            status = AdbServerStatus.RUNNING;
            StatusListener statusListener = mStatusListener;
            if (statusListener != null) {
                statusListener.onRunning();
            }
        } catch (IOException ioException) {
            log.error("执行" + adbExecutePath + "程序时发生io异常");
            throw ioException;
        }

        //error和input正好相反
        BufferedReader errorReader = new BufferedReader(new InputStreamReader(mAdbdProcess.getErrorStream()));
        BufferedReader inputReader = new BufferedReader(new InputStreamReader(mAdbdProcess.getInputStream()));
        mAdbInputThread = new AdbInputThread(errorReader);
        mAdbErrorOutputThread = new AdbErrorOutputThread(inputReader);

        try {
            process.waitFor();
            processExit();
        } catch (InterruptedException ignore) {
            Thread.currentThread().interrupt();
        }

    }

    /**
     * 杀死adb server进程
     * <p>
     * 通常不会这么操作，通常应该给adb server指令让其正常退出。
     */
    public void kill() {
        Process process = mAdbdProcess;
        if (process != null) {
            process.destroy();
        }
    }

    /**
     * 获得当前状态监听器
     *
     * @return 状态监听器
     */
    public StatusListener getStatusListener() {
        return mStatusListener;
    }

    /**
     * 设置状态监听器
     *
     * @param mStatusListener 状态监听器
     */
    public void setStatusListener(StatusListener mStatusListener) {
        this.mStatusListener = mStatusListener;
    }

    private void processExit() {
        int exitValue = mAdbdProcess.exitValue();

        status = AdbServerStatus.NOT_RUN;

        AdbInputThread outputThread = mAdbInputThread;
        mAdbInputThread = null;
        if (outputThread != null) {
            outputThread.interrupt();
        }

        AdbErrorOutputThread errorThread = mAdbErrorOutputThread;
        mAdbErrorOutputThread = null;
        if (errorThread != null) {
            errorThread.interrupt();
        }

        StatusListener statusListener = mStatusListener;
        if (statusListener != null) {
            statusListener.onStop(exitValue);
        }

        log.warn("Adb Server进程退出，退出码：" + exitValue);
    }

    /**
     * 自动探测adb可执行程序位置
     */
    private boolean autoDetectedAdbExecutePath() {
        boolean isWindows = System.getProperty("os.name").contains("Windows");
        String adbExecuteFileName = isWindows ? "adb.exe" : "adb";
        for (String pathDir : System.getenv("PATH").split(File.pathSeparator)) {
            File targetFile = new File(pathDir, adbExecuteFileName);
            if (targetFile.isFile() && targetFile.canExecute()) {
                adbExecutePath = targetFile.getAbsolutePath();
                return true;
            }
        }
        return false;
    }

    /**
     * 找不到有效的adb执行程序异常
     */
    public static class AdbNotFoundException extends Exception {
        public AdbNotFoundException() {
            super("未指定adb路径且无法从PATH中找到adb可执行程序");
        }
    }

    static class AdbInputThread extends Thread {
        private final BufferedReader mBufferReader;

        AdbInputThread(BufferedReader bufferedReader) {
            mBufferReader = bufferedReader;
            setName("AdbServerOutput");
            start();
        }

        @Override
        public void run() {
            while (isAlive() && !isInterrupted()) {
                try {
                    if (PRINT_ADB_SERVER_OUTPUT) {
                        String line = mBufferReader.readLine();
                        if (line != null && !line.isEmpty()) {
                            log.info(line);
                        }
                    }
                } catch (IOException e) {
                    log.error("线程" + getName() + "发生io错误，无法读取adb server输出");
                    interrupt();
                }
            }
        }
    }


    static class AdbErrorOutputThread extends Thread {
        private final BufferedReader mBufferReader;

        AdbErrorOutputThread(BufferedReader bufferedReader) {
            mBufferReader = bufferedReader;
            //:号前多一个空格，和普通输出对齐
            setName("AdbServerError ");
            start();
        }

        @Override
        public void run() {
            while (isAlive() && !isInterrupted()) {
                try {
                    if (PRINT_ADB_SERVER_OUTPUT) {
                        String line = mBufferReader.readLine();
                        if (line != null && !line.isEmpty()) {
                            log.error(line);
                        }
                    }
                } catch (IOException e) {
                    log.error("线程" + getName() + "发生io错误，无法读取adb server错误输出");
                    interrupt();
                }
            }
        }
    }

    /**
     * 状态监听
     */
    interface StatusListener {

        /**
         * 开始运行
         */
        void onRunning();

        /**
         * 进程运行结束
         *
         * @param exitCode 退出码
         */
        void onStop(int exitCode);
    }

    /**
     * AdbServer的运行状态
     */
    enum AdbServerStatus {
        /**
         * 未运行
         */
        NOT_RUN,

        /**
         * 正在运行
         */
        RUNNING;
    }


}
