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

import com.alibaba.fastjson.JSON;
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.*;
import net.northking.cctp.upperComputer.driver.agent.command.data.BatteryData;
import net.northking.cctp.upperComputer.driver.agent.command.data.NetworkSpeed;
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 java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class AndroidDeviceAllInfoThread extends Thread {

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

    private DebuggerDeviceInfo info;

    private OutputStream cpuOutPutStream;

    private String cpuFileName;

    private OutputStream memoOutPutStream;

    private String memoFileName;

    private OutputStream flowOutPutStream;

    private String flowFileName;

    private OutputStream temperatureOutPutStream;

    private String temperatureFileName;

    private AndroidAgentSession agentSession;

    private AdbDevice adbDevice;

    private boolean exit = false;

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

    public AndroidDeviceAllInfoThread(DebuggerDeviceInfo info) {
        this.info = info;
        Adb adb = null;
        try {
            adb = new Adb();
            ArrayList<AdbDevice> adbDevices = adb.listDevices();
            logger.debug("all共有{}台设备", adbDevices.size());
            if (!CollectionUtils.isEmpty(adbDevices)) {
                for (AdbDevice adbDevice : adbDevices) {
                    if (info.getDeviceId().equals(adbDevice.getSerial())) {
                        this.adbDevice = adbDevice;
                        break;
                    }
                }
            }
            if (null == this.adbDevice) {
                throw new ParamMistakeException("当前设备未连接，监控设备性能失败");
            }
            agentSession = new AndroidAgentSession(adbDevice, true);
            agentSession.start();
        } catch (IOException e) {
            logger.error("无法创建设备连接", e);
            throw new ParamMistakeException("当前设备未连接，监控设备性能失败");
        }
        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() + "_all_cpu.txt";
            memoFileName = dir.getAbsolutePath() + "/" + info.getCaseId() + "_all_memo.txt";
            flowFileName = dir.getAbsolutePath() + "/" + info.getCaseId() + "_all_flow.txt";
            temperatureFileName = dir.getAbsolutePath() + "/" + info.getCaseId() + "_all_temperature.txt";
            cpuOutPutStream = new FileOutputStream(cpuFileName);
            cpuOutPutStream.write("0".getBytes(StandardCharsets.UTF_8));
            memoOutPutStream = new FileOutputStream(memoFileName);
            memoOutPutStream.write("0".getBytes(StandardCharsets.UTF_8));
            flowOutPutStream = new FileOutputStream(flowFileName);
            flowOutPutStream.write("0".getBytes(StandardCharsets.UTF_8));
            temperatureOutPutStream = new FileOutputStream(temperatureFileName);
            Float temperature = getInitTemperature(adb);
            logger.info("初始温度：{}", temperature);
            if (null == temperature) {
                temperatureOutPutStream.write("0".getBytes(StandardCharsets.UTF_8));
            } else {
                temperatureOutPutStream.write((temperature + "").getBytes(StandardCharsets.UTF_8));
            }
        } catch (IOException e) {
            logger.error("创建设备数据文件失败", e);
            throw new ParamMistakeException("创建设备数据文件失败");
        }


    }

    private Float getInitTemperature(Adb adb) {
        AdbTransport transport = adb.shell(this.adbDevice, "dumpsys battery");
        String line;
        BufferedReader reader = new BufferedReader(new InputStreamReader(transport.getInputStream()));
        Map<String, String> deviceProperties = new HashMap<String, String>();
        try {
            while ((line = reader.readLine()) != null) {
                if (line.contains("temperature")) {
                    String[] split = line.split(": ");
                    if (split.length == 2) {
                        float temperature = Float.parseFloat(split[1].trim()) / 10f;
                        return temperature;
                    }
                }
            }
        } catch (IOException e) {
            logger.warn("初始温度获取失败", e);
        } finally {
            try {
                transport.close();
            } catch (IOException e) {
                logger.error("关闭文件流出错，e,{}",e);
            }
            try {
                reader.close();
            } catch (IOException e){
                logger.error("关闭文件流出错，e,{}",e);
            }
        }
        return 0f;
    }

    @Override
    public void run() {
        agentSession.send(WatchCpuUsageCommand.getInstance());
        agentSession.send(WatchMemoryUsageCommand.getInstance());
        agentSession.send(WatchTrafficStatsNetworkSpeedCommand.getInstance());
        agentSession.send(WatchBatteryDetailCommand.getInstance());
        DataInput dataInput = agentSession.getDataInput();
        int failCount = 0;
        while (!exit && !isInterrupted()) {
            try {
                int commandId = dataInput.readInt();
                if (commandId == WatchCpuUsageCommand.getInstance().getCommandId()) {//cpu使用率
                    Float[] cpuInfo = WatchCpuUsageCommand.getInstance().buildReceive(dataInput);
                    float total = 0f;
                    for (Float cpu : cpuInfo) {
                        total += cpu;
                    }
                    float usageRate = total / cpuInfo.length;
                    String format = this.format.format(usageRate);
                    cpuOutPutStream.write(("," + format).getBytes(StandardCharsets.UTF_8));
                    cpuOutPutStream.flush();
                } else if (commandId == WatchMemoryUsageCommand.getInstance().getCommandId()) {
                    Long[] memoInfo = WatchMemoryUsageCommand.getInstance().buildReceive(dataInput);
                    float usageMemo = (memoInfo[0] - memoInfo[1]) / 1024f / 1024f / 1024f;
                    String format = this.format.format(usageMemo);
                    memoOutPutStream.write(("," + format).getBytes(StandardCharsets.UTF_8));
                    memoOutPutStream.flush();
                } else if (commandId == WatchTrafficStatsNetworkSpeedCommand.getInstance().getCommandId()) {
                    NetworkSpeed networkSpeed = WatchTrafficStatsNetworkSpeedCommand.getInstance().buildReceive(dataInput);
                    float flow = networkSpeed.downloadSpeed / 1024f / 1024f;
                    String format = this.format.format(flow);
                    flowOutPutStream.write(("," + format).getBytes(StandardCharsets.UTF_8));
                    flowOutPutStream.flush();
                } else if (commandId == WatchBatteryDetailCommand.getInstance().getCommandId()) {
                    BatteryData batteryData = WatchBatteryDetailCommand.getInstance().buildReceive(dataInput);
                    float temperature = batteryData.getTemperature() / 10f;
                    String format = this.format.format(temperature);
                    temperatureOutPutStream.write(("," + format).getBytes(StandardCharsets.UTF_8));
                    temperatureOutPutStream.flush();
                }
            } catch (EOFException e) {
                logger.error("设备【{}】读取性能数据停止异常：",info.getDeviceId(), e);
                exit = true;
                stopThread();
            } catch (IOException e) {
                logger.warn("读取数据失败", e);
                failCount++;
                if (failCount > 5) {
                    logger.error("设备【{}】get perf error > 3，中断读取：",info.getDeviceId(), e);
                    exit = true;
                    stopThread();
                }
            }
        }
        logger.info("获取手机【{}】的数据,任务id：{}结束。。。。。。。。。。。。。。。", info.getDeviceId(), info.getCaseId());
    }

    public String stopThread() {
        logger.info("收到设备【{}】的性能数据停止请求,任务id:{}。。。。。。", info.getDeviceId(), info.getCaseId());
        agentSession.send(CloseSessionCommand.getInstance());
        exit = true;
        try {
            cpuOutPutStream.close();
            memoOutPutStream.close();
            flowOutPutStream.close();
            temperatureOutPutStream.close();
        } catch (IOException e) {
            logger.error("文件流关闭异常", e);
        }
        String stfPath = SpringUtils.getProperties("nk.mobile-computer.stfPath");
        File dir = new File(stfPath + "/perInfoTmp/");
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File file = new File(dir.getAbsolutePath() + "/" + info.getCaseId() + "_all.txt");
        byte[] data = new byte[4096];
        int length = 0;
        File cpuFile = new File(cpuFileName);
        File memoFile = new File(memoFileName);
        File flowFile = new File(flowFileName);
        File temperatureFile = new File(temperatureFileName);
        FileOutputStream fileOutputStream = null;
        FileInputStream cpuFileInputStream = null;
        FileInputStream memoFileInputStream = null;
        FileInputStream flowFileInputStream = null;
        FileInputStream temperatureInputStream = 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("],\"temperatureList\":[".getBytes(StandardCharsets.UTF_8));
            temperatureInputStream = new FileInputStream(temperatureFile);
            while ((length = temperatureInputStream.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("开始上传安卓设备性能文件，任务信息：{}", JSON.toJSONString(info));
            attachment = HttpUtils.upload(serverAddr + publicUploadAddr, file.getAbsolutePath(), info.getTenantId(), info.getCaseId(), FileBusinessTypeEnum.MOBILE_TASK_DEVICE_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 (null != temperatureInputStream) {
                try {
                    temperatureInputStream.close();
                } catch (IOException e) {
                    logger.error("关闭文件流出错，e,{}",e);
                }
            }
            if (cpuFile.exists()) {
                cpuFile.delete();
            }
            if (memoFile.exists()) {
                memoFile.delete();
            }
            if (flowFile.exists()) {
                flowFile.delete();
            }
            if (temperatureFile.exists()) {
                temperatureFile.delete();
            }
            if (null != fileOutputStream) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    logger.error("关闭文件流出错，e,{}",e);
                }
            }
        }

    }
}
