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

import net.northking.cctp.upperComputer.utils.ProcessCmdUtils;
import net.northking.cctp.upperComputer.utils.SessionUtils;
import net.northking.cctp.upperComputer.webSocket.entity.CmdRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.websocket.Session;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author: WuJinQuan
 * @date:2021-09-11 10:15
 **/
public class AndroidPerfThread extends Thread  {
    private final static Logger logger = LoggerFactory.getLogger(AndroidPerfThread.class);

    private Double oldUpFlow = null;
    private Double oldDownFlow = null;
    private Double sendFlow = 0.0;
    private Double revFlow = 0.0;
    private boolean b = true;
    float oldFps = 0f;
    private String serial ;
    private String PackageName;
    private String pid;
    private Session session;
    private CmdRequest cmdRequest;

    public AndroidPerfThread(Session session,CmdRequest cmdRequest){
        this.session = session;
        this.cmdRequest = cmdRequest;
    }

    public void init(String serial, String packageName) {
        b = true;
        setName("AndroidPerfThread-"+serial);
        this.serial = serial;
        this.PackageName = packageName;
        startApp(serial, PackageName);
        this.pid= PID(serial, PackageName);
    }

    @Override
    public void run() {
        Map<String, Object> perfMap = new HashMap<>(8);
        Long start;
        Long end;
        Long time;
        while (b) {
            try {
                start=System.currentTimeMillis();
                getCPU(serial, PackageName, perfMap);
                getMemory(serial, PackageName, perfMap);
                getFlow(serial, pid, perfMap, PackageName);
                getFps(serial, PackageName, perfMap);
                SessionUtils.sendContinuousData(session, cmdRequest, perfMap, "获取实时性能数据成功");
                end=System.currentTimeMillis();
                time=end-start;
                if (time<1000){
                    Thread.sleep(1000-time);
                }
            } catch (Exception e) {
                logger.error("获取安卓性能数据异常",e);
            }
        }
    }

    public void stopThread(){
        b = false;
    }

    private void startApp(String serial, String PackageName) {
        Process proc;
        String packageStr = null;
        try {
            List<String> cmdList = new ArrayList<>();
            cmdList.add("adb");
            cmdList.add("-s");
            cmdList.add(serial);
            cmdList.add("shell");
            cmdList.add("monkey");
            cmdList.add("-p");
            cmdList.add(PackageName);
            cmdList.add("-v -v -v");
            cmdList.add("1");
            cmdList.add("|");
            cmdList.add("grep");
            cmdList.add("component=");
            proc = ProcessCmdUtils.execCmd(cmdList);
            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 (!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);
                if (proc.waitFor() != 0) {
                }
            }
            logger.debug("[" +serial+ "]启动应用，包名:{}",PackageName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String PID(String serial, String PackageName) {

        Process proc = null;
        String str3 = null;
        try {
            List<String> cmdList = new ArrayList<>();
            cmdList.add("adb");
            cmdList.add("-s");
            cmdList.add(serial);
            cmdList.add("shell");
            cmdList.add("pgrep");
            cmdList.add("-o");
            cmdList.add(PackageName);
            logger.debug("执行查找pid命令...");
            proc = ProcessCmdUtils.execCmd(cmdList);
            if (proc.waitFor() != 0) {
            }
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    proc.getInputStream()));
            StringBuffer stringBuffer = new StringBuffer();
            String line;
            while ((line = in.readLine()) != null) {
                stringBuffer.append(line + " ");
            }
            String str1 = stringBuffer.toString();
            String PID = str1;
            PID = PID.trim();
            logger.debug("pid数值：{}",PID);
            str3 = PID;
        } catch (Exception e) {
            logger.error("获取Pid失败", e);
        } finally {
            try {
                if (null!=proc) {
                    proc.destroy();
                }
            } catch (Exception e2) {
            }
        }
        return str3;
    }

    private void getMemory(String serial, String PackageName, Map<String, Object> perfMap) {
        double Heap = 0;
        try {
            List<String> cmdList = new ArrayList<>();
            cmdList.add("adb");
            cmdList.add("-s");
            cmdList.add(serial);
            cmdList.add("shell");
            cmdList.add("dumpsys");
            cmdList.add("meminfo");
            cmdList.add(PackageName);
            cmdList.add("|");
            cmdList.add("grep");
            cmdList.add("TOTAL");
            logger.debug("执行获取内存命令...");
            Process proc = ProcessCmdUtils.execCmd(cmdList);
            try {
                if (proc.waitFor() != 0) {
                }
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        proc.getInputStream()));
                StringBuffer stringBuffer = new StringBuffer();
                String line = null;
                int i = 0;
                while ((line = in.readLine()) != null) {
                    stringBuffer.append(line + " ");
                    i ++;
                }
                String str1 = stringBuffer.toString();
                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));
                }
                Heap = Double.valueOf(al.get(0).toString());
                Heap = Heap / 1024;
                logger.debug("内存数据处理完成的结果：{}",Heap);
                perfMap.put("memory", String.format("%.2f", Heap));
            } catch (InterruptedException e) {
                logger.error("内存获取失败，请检查设备是否连接",e);
                perfMap.put("memory", "0");
            } finally {
                try {
                    proc.destroy();
                } catch (Exception e2) {
                }
            }
        } catch (Exception e) {
           logger.error("内存获取失败，请检查设备是否连接",e);
            perfMap.put("memory", "0");
            e.printStackTrace();
        }
    }

    private void getCPU(String serial, String PackageName, Map<String, Object> perfMap) {
        try {
            List<String> cmdList = new ArrayList<>();
            cmdList.add("adb");
            cmdList.add("-s");
            cmdList.add(serial);
            cmdList.add("shell");
            cmdList.add("top");
            cmdList.add("-b");
            cmdList.add("-o");
            cmdList.add("%CPU,ARGS");
            cmdList.add("-n 1");
            cmdList.add("-q");
            cmdList.add("-d 1");
            cmdList.add("|");
            cmdList.add("grep");
            cmdList.add(PackageName);
            cmdList.add("|awk \'{print $1}\'");
            logger.debug("执行获取cpu命令...");
            Process proc = ProcessCmdUtils.execCmd(cmdList);
            try {
                if (proc.waitFor() != 0) {
                    logger.error("exit value = " + proc.exitValue());
                }
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        proc.getInputStream()));
                StringBuffer stringBuffer = new StringBuffer();
                String line = null;
                float value = 0.0f;
                int i = 0;
                while ((line = in.readLine()) != null) {
                    value = value + Float.valueOf(line);
                    i++;
                }
                if (i==0) {
                    return;
                }
                value = value / i;
                logger.debug("cpu数据处理完成之后得到的结果:{}",value);
                String cpuValue=String.format("%.2f", value);
                if (!"NaN".equals(cpuValue)){
                    perfMap.put("cpu", cpuValue);
                }else {
                    perfMap.put("cpu","0");
                }

            } catch (Exception e) {
                logger.error("获取cpu异常",e);
                perfMap.put("cpu","0");
            } finally {
                try {
                    proc.destroy();
                } catch (Exception e2) {
                }
            }
        } catch (Exception e) {
            logger.error("获取cpu异常",e);
            perfMap.put("cpu","0");
        }

    }

    private void getFlow(String serial, String Pid, Map<String, Object> perfMap, String PackageName) {
        try {
            List<String> cmdList = new ArrayList<>();
            cmdList.add("adb");
            cmdList.add("-s");
            cmdList.add(serial);
            cmdList.add("shell");
            cmdList.add("cat");
            cmdList.add("/proc/net/dev");
            logger.debug("执行获取流量命令...");
            Process proc = ProcessCmdUtils.execCmd(cmdList);
            try {
                if (proc.waitFor() != 0) {
                    System.err.println("exit value = " + proc.exitValue());
                }
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        proc.getInputStream()));
                String line = null;
                while ((line = in.readLine()) != null) {
                    if (line.startsWith("wlan0:") || line.startsWith(" wlan0:")) {
                        logger.info(line);
                        break;
                    }
                }
                logger.info("最终读取流量的数据为：{}",line);
                String receive = "0";
                String send = "0";
                if (StringUtils.hasText(line)) {
                    String[] data = line.trim().split("\\s+");
                    receive = data[1];
                    receive = receive.trim();
                    send = data[9];
                    send = send.trim();
                }
                BigDecimal receiveDecimal = new BigDecimal(receive);
                BigDecimal sendDecimal = new BigDecimal(send);
                double tmp = 0.0;
                double sendFlowTmp = sendDecimal.doubleValue() / 1024;
                double revFlowTmp = receiveDecimal.doubleValue() / 1024;
                DecimalFormat numberFormat = new DecimalFormat("#.##");
                if (oldUpFlow == null && oldDownFlow == null) {  //第一次获取流量数据
                    oldUpFlow = new Double(sendFlowTmp);
                    oldDownFlow = new Double(revFlowTmp);
                    logger.debug("原来的上行流量和下行流量为空");
                    logger.debug("给原来的上行流量赋值为：{}",oldUpFlow);
                    logger.debug("给原来的下行流量赋值为：{}",oldDownFlow);
                    logger.debug("给原来的发送流量sendFlow赋值为：{}",sendFlow);
                    logger.debug("给原来的接受流量revFlow赋值为：{}",revFlow);
                } else {
                    logger.debug("原来的上行流量和下行流量不为空");
                    logger.info("现在的sendFlow:{},revFlow:{},oldUpFlow:{},oldDownFlow:{},sendFlowTmp:{},revFlowTmp:{}",sendFlow,revFlow,oldUpFlow,oldDownFlow,sendFlowTmp,revFlowTmp);
                    tmp = sendFlowTmp;
                    double realUpFlow = sendFlowTmp - oldUpFlow;
                    if (realUpFlow >= 0) {
                        sendFlow = realUpFlow;
                    }
                    oldUpFlow = tmp;
                    tmp = revFlowTmp;
                    double realDownFlow = revFlowTmp - oldDownFlow;
                    if (realDownFlow >= 0) {
                        revFlow = realDownFlow;
                    }
                    oldDownFlow = tmp;
                    logger.info("计算后的上行流量为：{}",sendFlow);
                    logger.info("计算后的下行流量为：{}",revFlow);
                }
                perfMap.put("upFlow", numberFormat.format(sendFlow));
                perfMap.put("downFlow", numberFormat.format(revFlow));
            } catch (InterruptedException e) {
                perfMap.put("upFlow", String.valueOf(0));
                perfMap.put("downFlow", String.valueOf(0));
                logger.error("获取上下行流量失败",e);
            } finally {
                try {
                    proc.destroy();
                } catch (Exception e2) {
                }
            }
        } catch (Exception e) {
            perfMap.put("upFlow", String.valueOf(0));
            perfMap.put("downFlow", String.valueOf(0));
            logger.error("获取上下行流量失败,流量——请检查设备是否连接",e);
        }

    }


    public void getFps(String serial, String PackageName,  Map<String,Object> perfMap) {
        long jumpingFrames = 0; //jank次数，跳帧数
        long totalFrames = 0;  //统计的总帧数
        float fps; //fps值
        float[] info = new float[2];
        int vsyncOverTimes = 0; // 垂直同步次数
        try {
            //先清屏
            List<String> cmdList = new ArrayList<>();
            cmdList.add("adb");
            cmdList.add("-s");
            cmdList.add(serial);
            cmdList.add("shell");
            cmdList.add("dumpsys");
            cmdList.add("SurfaceFlinger");
            cmdList.add("--latency-clear");
            logger.debug("执行清屏指令...");
            ProcessCmdUtils.execCmd(cmdList);
            //再获取
            List<String> cmdList1 = new ArrayList<>();
            cmdList1.add("adb");
            cmdList1.add("-s");
            cmdList1.add(serial);
            cmdList1.add("shell");
            cmdList1.add("dumpsys");
            cmdList1.add("gfxinfo");
            cmdList1.add(PackageName);
            logger.debug("执行获取fps指令...");
            Process process = ProcessCmdUtils.execCmd(cmdList1);
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            boolean flag = false;
            boolean nextFlag = false;
            while ((line = reader.readLine()) != null) {
                if (line.length() > 0) {
                    if (line.contains("Execute")) {
                        flag = true;
                        continue;
                    }
                    if (line.contains("View hierarchy")) {
                        break;
                    }

                    if (flag) {
                        Pattern pattern = Pattern.compile("\\d{1,2}.\\d{2}");
                        Matcher m = pattern.matcher(line);
                        ArrayList al = new ArrayList();
                        while (m.find()) {
                            al.add(m.group(0));
                        }
                        if (al.size() != 4) {
                            continue;
                        }
                        String[] times = line.trim().split("\\s+");
                        //计算一帧所花费的时间
                        float onceTime = Float.parseFloat(al.get(0).toString()) + Float.parseFloat(al.get(1).toString()) + Float.parseFloat(al.get(2).toString()) + Float.parseFloat(al.get(3).toString());
                        // System.out.println("onceTime"+onceTime);
                        totalFrames += 1; //统计总帧数
                        if (onceTime > 16.67) {//以Android定义的60FPS为标准
                            jumpingFrames += 1; // 统计跳帧jank数
                            //统计额外花费垂直同步脉冲的数量
                            BigDecimal bigDecimal = new BigDecimal(onceTime % 16.67);
                            if (bigDecimal.compareTo(BigDecimal.ZERO) == 0) {
                                vsyncOverTimes += onceTime / 16.67 - 1;
                            } else {
                                vsyncOverTimes += Math.floor(onceTime / 16.67); //向下取整即可
                            }
                        }
                    }
                }
            }
            if (totalFrames > 0) {
                double a = totalFrames;
                double b = vsyncOverTimes;
                fps = Math.round(a / (a + b) * 60);
                oldFps = fps;
                perfMap.put("fps", String.valueOf(fps));
            } else {
                perfMap.put("fps", "0");
                logger.error("【ERROR】无FPS信息，请确认手机正常连接或APP正常运行");
            }
        } catch (IOException e) {
            logger.error("【ERROR】无FPS信息，请确认手机正常连接或APP正常运行",e);
            perfMap.put("fps", "0");
        }
    }


}
