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


import net.northking.cctp.upperComputer.deviceManager.utils.OutputLineCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

public class Python3 {

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

    public static final String PYTHON_PATH = "/usr/bin/python3";


    public boolean debugPrintProgramOutput = true;

    public static final ArrayList<DaemonRunner> allRunners = new ArrayList<>();

    private static final AtomicInteger runnerCount = new AtomicInteger(1);

    private static Python3 instance;

    /**
     * 设备的CPU架构
     */
    public static String ARCH = "arm64";


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

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            synchronized (allRunners) {
                logger.debug("开始清理python3的daemon，共" + allRunners.size());
                allRunners.forEach((DaemonRunner::stop));
                logger.debug("python3的daemon清理完毕");
            }
        }));

        String arch = System.getProperty("os.arch");
        if (arch.equals("aarch64")) {
            ARCH = "arm64";
        } else {
            ARCH = "x86_64";
        }
    }




    public String getModuleName() {
        throw new RuntimeException("不是一个模块，请在Python3模块封装中调用");
    }

    public boolean checkEnvironment() {
        File pythonFile = new File(PYTHON_PATH);
        if (!pythonFile.exists()) {
            logger.error("未找到python3，它应该存在于：" + PYTHON_PATH);
            return false;
        }
        if (!pythonFile.canExecute()) {
            logger.error("没有执行权限：" + PYTHON_PATH);
            return false;
        }
        String pythonVersion = getPythonVersion();
        if (pythonVersion == null) {
            logger.error("无法获取python版本号");
            return false;
        }
        if (!pythonVersion.startsWith("Python 3.")) {
            logger.error("不兼容的python版本，需要python3，实际：" + pythonVersion);
            return false;
        }
        return true;
    }

    public String[] createModuleCommand(ArrayList<String> args) {
        return createModuleCommand(args.toArray(new String[0]));
    }

    /**
     * 创建python模块运行命令
     *
     * @param args 传递给python模块的参数
     * @return 完整的python模块执行命令
     */
    public String[] createModuleCommand(String... args) {
        String[] command = new String[args.length + 5];
        command[0] = "arch";
        command[1] = "-" + ARCH;
        command[2] = PYTHON_PATH;
        command[3] = "-m";
        command[4] = getModuleName();
        System.arraycopy(args, 0, command, 5, args.length);
        return command;
    }

    /**
     * 执行一个python模块
     *
     * @param args           模块的参数
     * @param outputReceiver 输出接收，如果不需要输出可以传null
     * @return 是否执行成功
     */
    public boolean runModule(ArrayList<String> args, OutputReceiver outputReceiver) {
        return runModule(createModuleCommand(args), outputReceiver);
    }

    /**
     * 执行一个python模块
     *
     * @param moduleCommand  完整命令行
     * @param outputReceiver 输出接收，如果不需要输出可以传null
     * @return 是否执行成功
     */
    public boolean runModule(String[] moduleCommand, OutputReceiver outputReceiver) {
        for (int i = 0; i < moduleCommand.length; i++) {
            if (moduleCommand[i] == null) {
                logger.error("启动Python3时发生输入输出错误，参数：" + Arrays.toString(moduleCommand) + "，第" + (i + 1) + "个参数为null");
                return false;
            } else {
                moduleCommand[i] = moduleCommand[i].trim();
            }
        }
        String moduleName = moduleCommand[2];

        ProcessBuilder processBuilder = new ProcessBuilder(moduleCommand);
        processBuilder.redirectErrorStream(true);
        Process process;
        try {
            process = processBuilder.start();
        } catch (IOException e) {
            logger.error("启动" + moduleName + "时发生输入输出错误，参数：" + Arrays.toString(moduleCommand), e);
            return false;
        }
        BufferedReader bufferReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        boolean hasException = false;
        do {
            try {
                String line = bufferReader.readLine();
                if (line == null) {
                    break;
                }
                if (debugPrintProgramOutput) {
                    logger.debug(line);
                }
                if (outputReceiver != null) {
                    outputReceiver.onLine(line);
                }
            } catch (IOException e) {
                logger.error("执行" + moduleName + "过程中发生IO错误", e);
                hasException = true;
                break;
            }
        } while (!Thread.currentThread().isInterrupted());

        try {
            bufferReader.close();
        } catch (IOException e) {
            logger.warn("关闭" + moduleName + "输出读取流时发生IO错误", e);
        }

        try {
            int code = process.waitFor();
            if (code == 0) {
                return !hasException;
            } else {
                logger.warn(moduleName + "非正常退出，状态码：" + code + "，命令行:" + Arrays.toString(moduleCommand));
                return true;
            }
        } catch (InterruptedException ignored) {

        }
        return false;
    }


    /**
     * 获得macOS的python版本
     *
     * @return 版本字符串
     */
    private static String getPythonVersion() {
        ProcessBuilder processBuilder = new ProcessBuilder(PYTHON_PATH, "--version");
        Process process;
        try {
            process = processBuilder.start();
        } catch (IOException e) {
            logger.error("启动python获取版本时发生IO错误", e);
            return null;
        }
        String versionLine = null;
        BufferedReader bufferReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        do {
            try {
                String line = bufferReader.readLine();
                if (line == null) {
                    break;
                }
                if (line.startsWith("Python ")) {
                    versionLine = line;
                }
            } catch (IOException e) {
                logger.error("读取python版本时发生IO错误");
                break;
            }
        } while (Thread.currentThread().isInterrupted());
        try {
            bufferReader.close();
        } catch (IOException e) {
            logger.warn("关闭python版本读取流时发生IO错误", e);
        }
        try {
            process.waitFor();
        } catch (InterruptedException ignored) {

        }
        return versionLine;
    }


    public static class OutputReceiver {
        private final ArrayList<String> allOutput = new ArrayList<>();

        /**
         * 字符计数
         */
        private int outputCharCount = 0;


        public void onLine(String line) {
            outputCharCount += line.length();
            allOutput.add(line);
        }

        public ArrayList<String> getAllOutput() {
            return allOutput;
        }

        public String getHoleOutput(boolean withLineBreak) {
            int cap = outputCharCount;
            if (withLineBreak) cap += allOutput.size();
            StringBuilder sb = new StringBuilder(cap);
            for (String s : allOutput) {
                sb.append(s);
                if (withLineBreak) {
                    sb.append("\n");
                }
            }
            return sb.toString();
        }
    }

    /**
     * 需要持续运行的python操作类
     */
    public class DaemonRunner implements Runnable {

        private final int id;

        /**
         * pymobiledevice3的每一行回调
         * <br>
         * 如果回调了一个null，则表示程序退出了
         */
        private final OutputLineCallback outputLineCallback;
        private final boolean redirectErrorOutput;
        private final String[] args;

        private Process process = null;
        private final Thread thread = new Thread(this);


        public DaemonRunner(OutputLineCallback outputLineCallback, boolean redirectErrorOutput, ArrayList<String> args) {
            this(outputLineCallback, redirectErrorOutput, args.toArray(new String[0]));
        }

        public DaemonRunner(OutputLineCallback outputLineCallback, boolean redirectErrorOutput, String... args) {
            this.id = runnerCount.getAndIncrement();
            this.outputLineCallback = outputLineCallback;
            this.redirectErrorOutput = redirectErrorOutput;
            this.args = args;
            thread.setName("Python3-Daemon-" + id);
        }

        public void start() {
            if (!thread.isAlive()) {
                thread.start();
                synchronized (allRunners) {
                    allRunners.add(this);
                }
            }
        }

        public void stop() {
            if (process != null) {
                process.destroy();
                try {
                    int code = process.waitFor();
                    logger.info("主动结束pymobiledevice3的Daemon[" + id + "]，状态码：" + code);
                } catch (InterruptedException ignored) {

                }
            }
        }

        @Override
        public void run() {
            String[] command = createModuleCommand(args);
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            processBuilder.redirectErrorStream(redirectErrorOutput);
            try {
                process = processBuilder.start();
            } catch (IOException e) {
                logger.error("启动" + getModuleName() + "时发生输入输出错误，参数：" + Arrays.toString(args), e);
                return;
            }
            BufferedReader bufferReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            do {
                try {
                    String line = bufferReader.readLine();
                    if (line == null) {
                        break;
                    }
                    if (getInstance().debugPrintProgramOutput) {
                        logger.debug(line);
                    }
                    outputLineCallback.onLine(line);
                } catch (IOException e) {
                    logger.error("执行" + getModuleName() + "过程中发生IO错误", e);
                    break;
                }
            } while (!thread.isInterrupted());
            try {
                bufferReader.close();
            } catch (IOException e) {
                logger.warn("关闭" + getModuleName() + "输出读取流时发生IO错误", e);
            }

            try {
                int code = process.waitFor();
                if (code != 0) {
                    logger.warn(getModuleName() + "非正常退出，状态码：" + code + "，参数:" + Arrays.toString(args));
                }
            } catch (InterruptedException ignored) {

            }
            process = null;
            outputLineCallback.onLine(null);
            synchronized (allRunners) {
                allRunners.remove(this);
            }
        }
    }
}
