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

import com.alibaba.fastjson.JSON;
import net.northking.cctp.upperComputer.deviceManager.UpperComputerManager;
import net.northking.cctp.upperComputer.driver.adb.Adb;
import net.northking.cctp.upperComputer.driver.adb.AdbDevice;
import net.northking.cctp.upperComputer.driver.adb.AdbTransport;
import net.northking.cctp.upperComputer.driver.agent.AndroidAgentSession;
import net.northking.cctp.upperComputer.driver.agent.command.RunningAppProcessInfo;
import net.northking.cctp.upperComputer.driver.agent.command.RunningAppProcessInfoCommand;
import net.northking.cctp.upperComputer.enums.FileBusinessTypeEnum;
import net.northking.cctp.upperComputer.entity.Attachment;
import net.northking.cctp.upperComputer.entity.DebuggerDeviceInfo;
import net.northking.cctp.upperComputer.exception.ParamMistakeException;
import net.northking.cctp.upperComputer.utils.HttpUtils;
import net.northking.cctp.upperComputer.utils.SpringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AndroidDeviceInfoByPackageThread extends Thread {

    private DebuggerDeviceInfo info;

    private OutputStream cpuOutPutStream;

    private String cpuFileName;

    private OutputStream memoOutPutStream;

    private String memoFileName;

    private OutputStream flowOutPutStream;

    private String flowFileName;

    private Adb adb;

    private AdbDevice adbDevice;

    private DecimalFormat format = new DecimalFormat("0.00");

    private String cpuShell;

    private final String SHELL_MEMO = "dumpsys meminfo %s | grep TOTAL";

    private final String FLOW_SHELL = "cat /proc/%s/net/dev";

    private List<String> appProcessId;

    private Long oldDownFlow = null;

    private boolean exit = false;

    private Logger logger = LoggerFactory.getLogger(AndroidDeviceInfoByPackageThread.class);

    public AndroidDeviceInfoByPackageThread(DebuggerDeviceInfo info) {
        this.info = info;
        try {
            adb = new Adb();
            ArrayList<AdbDevice> adbDevices = adb.listDevices();
            logger.debug("per共有{}台设备", adbDevices.size());
            if (!CollectionUtils.isEmpty(adbDevices)) {
                for (AdbDevice adbDevice : adbDevices) {
                    if (info.getDeviceId().equals(adbDevice.getSerial())) {
                        this.adbDevice = adbDevice;
                    }
                }
            }
            if (null == this.adbDevice) {
                throw new ParamMistakeException("当前设备不在线");
            }
            appProcessId = getPid();
            if (CollectionUtils.isEmpty(appProcessId)) {
                throw new ParamMistakeException(String.format("当前应用【{}】未启动", info.getAppPackage()));
            }
            cpuShell = "top -b -o %CPU,ARGS -n 1 -q -d 1 | grep " + info.getAppPackage() + " |awk '{print $1}'";
            logger.debug("执行获取cpu命令：{}", cpuShell);
        } catch (IOException e) {
            logger.error("adb 连接不上", e);
            throw new ParamMistakeException("当前设备无法连接adb服务");
        }
        String stfPath = SpringUtils.getProperties("nk.mobile-computer.stfPath");
        File dir = new File(stfPath + "/perInfoTmp/");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        try {
            cpuFileName = dir.getAbsolutePath() + "/" + info.getCaseId() + "_app_cpu.txt";
            memoFileName = dir.getAbsolutePath() + "/" + info.getCaseId() + "_app_memo.txt";
            flowFileName = dir.getAbsolutePath() + "/" + info.getCaseId() + "_app_flow.txt";
            cpuOutPutStream = new FileOutputStream(cpuFileName);
            cpuOutPutStream.write("0".getBytes(StandardCharsets.UTF_8));
            cpuOutPutStream.flush();
            memoOutPutStream = new FileOutputStream(memoFileName);
            memoOutPutStream.write("0".getBytes(StandardCharsets.UTF_8));
            memoOutPutStream.flush();
            flowOutPutStream = new FileOutputStream(flowFileName);
            flowOutPutStream.write("0".getBytes(StandardCharsets.UTF_8));
            flowOutPutStream.flush();
        } catch (IOException e) {
            logger.error("创建应用数据文件失败", e);
            throw new ParamMistakeException("创建应用数据文件失败");
        }

    }

    @Override
    public void run() {
        logger.info("开始任务Id【{}】的cpu、memo、flow数据的获取", info.getCaseId());
        Float cpuRate = 0f;
        Float memo = 0f;
        Float flow = 0f;
        Long start;
        Long end;
        Long time;
        while (!exit) {
            try {
                start = System.currentTimeMillis();
                cpuRate = getCPU();
                cpuOutPutStream.write(("," + cpuRate).getBytes(StandardCharsets.UTF_8));
                cpuOutPutStream.flush();
                memo = getMemory();
                memoOutPutStream.write(("," + memo).getBytes(StandardCharsets.UTF_8));
                memoOutPutStream.flush();
                flow = getFlow();
                flowOutPutStream.write(("," + flow).getBytes(StandardCharsets.UTF_8));
                flowOutPutStream.flush();
                end = System.currentTimeMillis();
                time = end - start;
                if (time < 1000) {
                    Thread.sleep(1000 - time);
                }
            } catch (InterruptedException exit) {
                break;
            } catch (Exception e) {
                logger.error("获取安卓性能数据异常", e);
            }
            logger.debug("收集设备【{}】应用【{}】数据中。。。。。,任务Id：{}", info.getDeviceId(), info.getAppPackage(), info.getCaseId());
        }
        logger.info("收集设备【{}】应用【{}】数据结束,任务id:{}。。。。。。", info.getDeviceId(), info.getAppPackage(), info.getCaseId());
    }

    public String stopThread() {
        logger.info("收到设备【{}】应用【{}】性能数据停止请求,任务id:{}。。。。。。", info.getDeviceId(), info.getAppPackage(), info.getCaseId());
        exit = true;
        try {
            cpuOutPutStream.close();
            memoOutPutStream.close();
            flowOutPutStream.close();
        } catch (IOException e) {
            logger.error("文件流关闭异常", e);
        }
        logger.debug("文件流关闭");
        String stfPath = UpperComputerManager.getInstance().getApplicationPath();
        File dir = new File(stfPath + "/perInfoTmp/");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File file = new File(dir.getAbsolutePath() + "/" + info.getCaseId() + "_app.txt");
        byte[] data = new byte[4096];
        int length = 0;
        File cpuFile = new File(cpuFileName);
        File memoFile = new File(memoFileName);
        File flowFile = new File(flowFileName);
        FileOutputStream fileOutputStream = null;
        FileInputStream cpuFileInputStream = null;
        FileInputStream memoFileInputStream = null;
        FileInputStream flowFileInputStream = null;
        try {
            fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write("{\"cpuList\":[".getBytes(StandardCharsets.UTF_8));
            fileOutputStream.flush();
            cpuFileInputStream = new FileInputStream(cpuFile);
            while ((length = cpuFileInputStream.read(data, 0, data.length)) > 0) {
                fileOutputStream.write(data, 0, length);
                fileOutputStream.flush();
            }
            fileOutputStream.write("],\"memoList\":[".getBytes(StandardCharsets.UTF_8));
            fileOutputStream.flush();
            memoFileInputStream = new FileInputStream(memoFile);
            while ((length = memoFileInputStream.read(data, 0, data.length)) > 0) {
                fileOutputStream.write(data, 0, length);
                fileOutputStream.flush();
            }
            fileOutputStream.write("],\"flowList\":[".getBytes(StandardCharsets.UTF_8));
            flowFileInputStream = new FileInputStream(flowFile);
            while ((length = flowFileInputStream.read(data, 0, data.length)) > 0) {
                fileOutputStream.write(data, 0, length);
                fileOutputStream.flush();
            }
            fileOutputStream.write("]}".getBytes(StandardCharsets.UTF_8));
            fileOutputStream.flush();
        } catch (IOException e) {
            logger.error("性能数据保存失败", e);
            return "";
        }
        //上传
        String serverAddr = SpringUtils.getProperties("nk.mobile-computer.serverAddr");
        String publicUploadAddr = SpringUtils.getProperties("nk.mobile-computer.publicUploadAddr");
        Attachment attachment = null;
        try {
            logger.info("开始上传android应用{}性能文件，任务信息：{}", info.getAppPackage(),  JSON.toJSONString(info));
            attachment = HttpUtils.upload(serverAddr + publicUploadAddr, file.getAbsolutePath(), info.getTenantId(), info.getCaseId(), FileBusinessTypeEnum.MOBILE_TASK_APP_PERFORMANCE.getCode());
            logger.info("任务Id：{}，上传应用的数据成功！文件id:{}", info.getCaseId(),attachment.getUrlPath());
            return attachment.getUrlPath();
        } catch (Exception e) {
            logger.warn("任务Id：{}，上传应用数据失败！", info.getCaseId(), e);
            return "";
        } finally {
            if (null != cpuFileInputStream) {
                try {
                    cpuFileInputStream.close();
                } catch (IOException e) {
                    logger.error("关闭文件流失败，e,{}",e);
                }
            }
            if (null != memoFileInputStream) {
                try {
                    memoFileInputStream.close();
                } catch (IOException e) {
                    logger.error("关闭文件流失败，e,{}",e);
                }
            }
            if (null != flowFileInputStream) {
                try {
                    flowFileInputStream.close();
                } catch (IOException e) {
                    logger.error("关闭文件流失败，e,{}",e);
                }
            }

            if (cpuFile.exists()) {
                cpuFile.delete();
            }
            if (memoFile.exists()) {
                memoFile.delete();
            }
            if (flowFile.exists()) {
                flowFile.delete();
            }
            if (null != fileOutputStream) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    logger.error("关闭文件流失败，e,{}",e);
                }
            }
        }

    }

    private List<String> getPid() {
        List<String> pids = null;
        AndroidAgentSession agentSession = null;
        try {
            agentSession = new AndroidAgentSession(adbDevice, false);
            agentSession.start();
            List<RunningAppProcessInfo> runningAppProcessInfos = agentSession.send(RunningAppProcessInfoCommand.getInstance());
            if (!CollectionUtils.isEmpty(runningAppProcessInfos)) {
                pids = new ArrayList<>();
                for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcessInfos) {
                    List<String> pkgList = runningAppProcessInfo.getPkgList();
                    for (String name : pkgList) {
                        if (info.getAppPackage().equals(name)) {
                            int pid = runningAppProcessInfo.getPid();
                            pids.add(pid + "");
                            logger.debug("pid数值：{}", pid);
                        }
                    }

                }
            }
        } catch (Exception e) {
            logger.error("获取Pid失败", e);
        }finally {
            if (null != agentSession) {
                agentSession.closeSilence();
            }
        }
        return pids;
    }

    private float getMemory() {
        float heap = 0f;
        AdbTransport transport = null;

        String memoShell = String.format(SHELL_MEMO, info.getAppPackage());
        transport = adb.shell(adbDevice, memoShell);
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(transport.getInputStream()));
            StringBuffer stringBuffer = new StringBuffer();
            String line = null;
            int i = 0;
            while ((line = in.readLine()) != null) {
                stringBuffer.append(line + " ");
                i++;
            }
            String str1 = stringBuffer.toString();
            if (StringUtils.hasText(str1)) {
                Pattern pattern = Pattern.compile("[0-9]{1,20}");
                Matcher m = pattern.matcher(str1);
                ArrayList al = new ArrayList();
                while (m.find()) {
                    al.add(m.group(0));
                }
                if (!CollectionUtils.isEmpty(al)) {
                    heap = Float.valueOf(al.get(0).toString());
                    heap = heap / 1024f;
                    String result = this.format.format(heap);
                    return Float.parseFloat(result);
                }
            }
        } catch (Exception e) {
            logger.error("内存获取失败，请检查设备是否连接", e);
        } finally {
            if (null != transport) {
                try {
                    transport.close();
                } catch (IOException e) {
                    logger.error("关闭文件流失败，e,{}",e);
                }
            }
        }
        return heap;
    }

    private float getCPU() {
        float value = 0.0f;
        AdbTransport transport = null;
        try {
            transport = adb.shell(adbDevice, cpuShell);
            InputStream inputStream = transport.getInputStream();
            BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
            String line = null;
            int i = 0;
            while ((line = in.readLine()) != null) {
                value = value + Float.valueOf(line);
                i++;
            }
            String temp = format.format(value / i);
            value = Float.parseFloat(temp);
        } catch (Exception e) {
            logger.error("获取cpu异常", e);
        } finally {
            if (null != transport) {
                try {
                    transport.close();
                } catch (IOException e) {
                    logger.error("关闭文件流失败，e,{}",e);
                }
            }
        }
        return value;

    }

    private float getFlow() {
        float downFlow = 0f;
        if (CollectionUtils.isEmpty(appProcessId)) {
            logger.warn("设备【{}】在任务【{}】中采集包【】性能数据未获取到应用启动的pid，可能应用已经退出了，退出采集..............",info.getDeviceId(),info.getCaseId(),info.getAppPackage());
//            stopThread();
//            appProcessId = getPid();
        }
        if (CollectionUtils.isEmpty(appProcessId)) {
            return 0f;
        }
        String flowShell = String.format(FLOW_SHELL, appProcessId.get(0));
        AdbTransport transport = null;
        BufferedReader in = null;
        try {
            transport = adb.shell(adbDevice, flowShell);
            in = new BufferedReader(new InputStreamReader(transport.getInputStream()));
            String line = null;
            Long revFlowTmp = 0l;
            while ((line = in.readLine()) != null) {
                if (line.trim().startsWith("lo:") || line.trim().startsWith("Inter-") || line.trim().startsWith("face")) {
                    continue;
                } else if (line.contains("No such file or directory")) {  //应用被清除
                    logger.warn(line);
                    appProcessId = null;
                    return 0f;
                } else {
                    String[] data = line.trim().split("\\s+");
                    String receive = data[1];
                    receive = receive.trim();
                    BigDecimal receiveDecimal = new BigDecimal(receive);
                    revFlowTmp += receiveDecimal.longValue();
                }
            }
            logger.info("最终读取流量的数据为：{}", revFlowTmp);
            if (oldDownFlow == null) {  //第一次获取流量数据
                oldDownFlow = revFlowTmp;
            } else {
                float realDownFlow = revFlowTmp - oldDownFlow;
                if (realDownFlow >= 0) {
                    realDownFlow = realDownFlow / 1024f / 1024f;
                } else {
                    realDownFlow = 0;
                }
                oldDownFlow = revFlowTmp;
                downFlow = realDownFlow;
            }

        } catch (
                Exception e) {
            logger.error("获取上下行流量失败", e);
        } finally {
            if (transport!=null) {
                try{
                    transport.close();
                }catch (IOException e){
                    logger.error("关闭数据流出错");
                }
            }
            if (in!=null) {
                try{
                    in.close();
                }catch (IOException e){
                    logger.error("关闭数据流出错");
                }
            }

        }

        String temp = format.format(downFlow);
        downFlow = Float.parseFloat(temp);
        return downFlow;
    }
}
