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

import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import net.northking.cctp.upperComputer.deviceManager.HarmonyDeviceManager;
import net.northking.cctp.upperComputer.driver.harmony.hdc.HDCDevice;
import net.northking.cctp.upperComputer.driver.harmony.hdc.HDCSession;
import net.northking.cctp.upperComputer.driver.harmony.hdc.Hdc;
import net.northking.cctp.upperComputer.driver.harmony.ui.UiComponent;
import net.northking.cctp.upperComputer.driver.protocol.packet.CodecUtils;
import net.northking.cctp.upperComputer.driver.protocol.packet.ICommandData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 原生鸿蒙设备操作对象
 */
public class HarmonyDevice implements ICommandData {

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

    /**
     * 设备上临时存放UI节点树的路径
     */
    private static final String DEVICE_UI_TREE_PATH = "/data/local/tmp/ui.json";

    /**
     * 设备上临时存放屏幕截图的路径
     */
    private static final String DEVICE_SCREENSHOT_PATH = "/data/local/tmp/screen.png";

    /**
     * 启用隐藏测试模式的Shell
     * <br>
     * 启用后就可以执行kill/killall命令
     */
    private static final String ENABLE_TEST_MODE_SHELL = "param set persist.ace.testmode.enabled 1";

    private HDCDevice hdcDevice;

    public HarmonyDevice(HDCDevice hdcDevice) {
        this.hdcDevice = hdcDevice;

    }

    public HDCDevice getHdcDevice() {
        return hdcDevice;
    }

    public void setHdcDevice(HDCDevice hdcDevice) {
        this.hdcDevice = hdcDevice;
    }

    /**
     * 启用隐藏的测试模式
     * <br>
     * 启用后就可以执行kill/killall命令
     *
     * @return 是否启用成功
     */
    public boolean enableTestMode() {
        String outputText = "";
        try (HDCSession session = Hdc.getInstance().shell(hdcDevice, ENABLE_TEST_MODE_SHELL)) {
            outputText = session.readAllLines();
            log.debug("切换测试模式输出：{}", outputText);
        }
        boolean result = outputText.contains("Set parameter persist.ace.testmode.enabled 1 success");
        if (!result) {
            log.error("鸿蒙设备{}切换隐藏测试模式失败：{}", hdcDevice.getConnectKey(), outputText);
        }
        return result;
    }

    /**
     * 输入文本
     * <br>
     * 借助shell的uitest实现，实际为点击输入框、复制并粘贴指定文本
     *
     * @param x    输入框x坐标
     * @param y    输入框y坐标
     * @param text 要输入的文本
     * @return 是否操作成功
     */
    public boolean inputText(int x, int y, String text) {
        text = text.replace("\\", "\\\\")
                .replace("\"", "\\\"")
                .replace("`", "\\`")
//                .replace("'", "\\'")
                .replace("$", "\\$");
        String outputText = "";
        try (HDCSession session = Hdc.getInstance().shell(hdcDevice, "uitest uiInput inputText " + x + " " + y + " \"" + text + "\"")) {
            outputText = session.readAllLines();
            log.debug("设备{}在({},{}))文本输入{}输出{}", hdcDevice.getConnectKey(), x, y, text, outputText);
        }
        boolean result = outputText.contains("No Error");
        if (!result) {
            log.debug("设备{}在({},{}))文本输入失败{}输出{}", hdcDevice.getConnectKey(), x, y, text, outputText);
        }
        return result;
    }

    /**
     * 按键
     * <br>
     * 自动按下并抬起，支持组合键
     * <br>
     * 借助shell的实现
     */
    public boolean keyEvent(List<Integer> keyCode) {
        StringBuilder shellBuilder = new StringBuilder("uitest uiInput keyEvent");
        for (int k : keyCode) {
            shellBuilder.append(" ");
            shellBuilder.append(k);
        }
        String outputText = "";
        try (HDCSession session = Hdc.getInstance().shell(hdcDevice, shellBuilder.toString())) {
            outputText = session.readAllLines();
            log.debug("设备{}按键{}输出{}", hdcDevice.getConnectKey(), keyCode, outputText);
        }
        boolean result = outputText.contains("No Error");
        if (!result) {
            log.debug("设备{}按键失败{}，输出{}", hdcDevice.getConnectKey(), keyCode, outputText);
        }
        return result;
    }

    /**
     * 获取UI层级树
     *
     * @return UI层级树数据，如果内部没有数据则为获取失败
     */
    public ArrayList<UiComponent> getUiTree() {
        ArrayList list = new ArrayList<UiComponent>();
        try (HDCSession session = Hdc.getInstance().shell(hdcDevice, "uitest dumpLayout -p " + DEVICE_UI_TREE_PATH + " -i")) {
            String output;
            StringBuilder sb = new StringBuilder();
            while ((output = session.readLine()) != null) {
                sb.append(output);
            }
            output = sb.toString();
            if (output.contains("DumpLayout saved to:" + DEVICE_UI_TREE_PATH)) {
                File file = HarmonyDeviceManager.getWorkTmpFile(hdcDevice.getConnectKey() + "-ui.json");
                boolean fileResult = Hdc.getInstance().receiveFile(hdcDevice, DEVICE_UI_TREE_PATH, file);
                if (fileResult) {
                    try (FileReader reader = new FileReader(file)) {
                        list = HarmonyDeviceManager.gson.fromJson(reader, new TypeToken<ArrayList<UiComponent>>() {
                        }.getType());
                    } catch (IOException e) {
                        log.error("读取鸿蒙设备{}的UI数据文件时发生IO错误", hdcDevice.getConnectKey(), e);
                    } catch (JsonParseException e) {
                        log.error("解析鸿蒙设备{}的UI数据文件时发生错误", hdcDevice.getConnectKey(), e);
                    }
                    if (!file.delete()) {
                        log.warn("删除鸿蒙设备{}的UI数据文件失败", hdcDevice.getConnectKey());
                    }
                }
            }
        }
        return list;
    }

    /**
     * 屏幕截图<br>
     * 获得一张原始分辨率的无损png截图数据，耗时较长
     *
     * @return 截图png文件数据，0长度时为截图失败
     */
    public byte[] takeScreenshot() {
        byte[] result = new byte[0];
        try (HDCSession session = Hdc.getInstance().shell(hdcDevice, "uitest screenCap -p " + DEVICE_SCREENSHOT_PATH)) {
            String output;
            StringBuilder sb = new StringBuilder();
            while ((output = session.readLine()) != null) {
                sb.append(output);
            }
            output = sb.toString();
            if (output.contains("ScreenCap saved to " + DEVICE_SCREENSHOT_PATH)) {
                File file = HarmonyDeviceManager.getWorkTmpFile(hdcDevice.getConnectKey() + "-screenshot.png");
                boolean fileResult = Hdc.getInstance().receiveFile(hdcDevice, DEVICE_SCREENSHOT_PATH, file);
                if (fileResult) {
                    try (FileInputStream fis = new FileInputStream(file)) {
                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
                        byte[] buffer = new byte[1024];
                        int length;
                        while ((length = fis.read(buffer)) != -1) {
                            bos.write(buffer, 0, length);
                        }
                        result = bos.toByteArray();
                    } catch (IOException e) {
                        log.error("读取鸿蒙设备{}的屏幕截图临时文件时发生IO错误", hdcDevice.getConnectKey(), e);
                    }
                    if (!file.delete()) {
                        log.warn("删除鸿蒙设备{}的屏幕截图临时文件失败", hdcDevice.getConnectKey());
                    }
                }

            }
        }
        return result;
    }

    /**
     * 屏幕截图<br>
     * 获得一张原始分辨率的无损png截图数据，耗时较长
     *
     * @return 截图png文件数据，0长度时为截图失败
     */
    public File takeScreenshotForFile(String fileName) {
        boolean success = false;
        int time = 1;
        while (!success && time <= 5) {  //成功或者10秒
            try (HDCSession session = Hdc.getInstance().shell(hdcDevice, "uitest screenCap -p " + DEVICE_SCREENSHOT_PATH)) {
                String output;
                StringBuilder sb = new StringBuilder();
                while ((output = session.readLine()) != null) {
                    sb.append(output);
                }
                output = sb.toString();
                log.debug("设备【{}】第{}次截图到设备的输出:{}", hdcDevice.getConnectKey(),time,output);
                if (output.contains("ScreenCap saved to " + DEVICE_SCREENSHOT_PATH)) {
                    log.warn("设备【{}】第{}次截图成功............",hdcDevice.getConnectKey(),time);
                    success = true;
                    break;
                } else {
                    log.warn("设备【{}】第{}次截图失败，重新尝试............",hdcDevice.getConnectKey(),time);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        log.warn("设备【{}】不截图了", hdcDevice.getConnectKey(), e);
                        return null;
                    }
                    time++;
                }
            }
        }
        if (success) {
            File file = new File(fileName);
            boolean fileResult = Hdc.getInstance().receiveFile(hdcDevice, DEVICE_SCREENSHOT_PATH, file);
            if (fileResult) {
                return file;
            }
        } else {
            log.warn("设备【{}】截了5次图都没有一次成功过", hdcDevice.getConnectKey());
        }
        return null;
    }

    @Override
    public String toString() {
        return "HarmonyDevice{connectKey=" + hdcDevice.getConnectKey() + " status:" + hdcDevice.getConnectStatus() + "}";
    }

    @Override
    public void buildFromInput(DataInput dataInput) {
        throw new RuntimeException("不支持的操作，请勿调用此方法");
    }

    @Override
    public void encodeToOutput(DataOutput dataOutput) throws IOException {
        CodecUtils.writeText(dataOutput, hdcDevice.getConnectKey());
        CodecUtils.writeText(dataOutput, hdcDevice.getConnectType().name());
        CodecUtils.writeText(dataOutput, hdcDevice.getConnectStatus().name());
        CodecUtils.writeText(dataOutput, hdcDevice.getLocation());
    }
}
