package android_serialport_api;

import com.blankj.utilcode.util.StringUtils;
import com.example.iot_controlhost.model.Device;
import com.example.iot_controlhost.model.sensor.Sensor;
import com.example.iot_controlhost.utils.DataUtils;
import com.example.iot_controlhost.utils.global.HardwareSetting;
import com.example.iot_controlhost.utils.log.MyLog;

import java.io.File;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class SerialPortUtil {
    /**
     * 发送的数据内容
     */
    public static String T = "T:";
    /**
     * 接收的数据内容
     */
    public static String R = "R:";
    /**
     * 串口通信类别-控制器
     */
    public final static String TYPE_CONTROLLER = "TYPE_CONTROLLER";
    /**
     * 串口通信类别-传感器
     */
    public final static String TYPE_SENSOR = "TYPE_SENSOR";
    /**
     * 使用SerialPortUtil的设备
     */
    private String type;
    private static SerialPortUtil sensorSP;
    private static SerialPortUtil controllerSP;
    private SerialPort serialPort;
    private InputStream inputStream;
    private OutputStream outputStream;
    private boolean isStart;

    private SerialPortUtil(String type) {
        this.type = type;
        isStart = false;
    }

    public static SerialPortUtil getInstance(String type) {
        if (Device.isIntegratedController() || TYPE_CONTROLLER.equals(type)) {
            if (controllerSP == null) {
                synchronized (SerialPortUtil.class) {
                    if (controllerSP == null) {
                        controllerSP = new SerialPortUtil(type);
                    }
                }
            }
            return controllerSP;
        } else {
            // 传感器
            if (sensorSP == null) {
                synchronized (SerialPortUtil.class) {
                    if (sensorSP == null) {
                        sensorSP = new SerialPortUtil(type);
                    }
                }
            }
            return sensorSP;
        }
    }

    /**
     * 开启串口
     * 使用设置好的端口号、波特率
     */
    public boolean openSerialPort() {
        String portName;
        String baud;
        if (TYPE_SENSOR.equals(type)) {
            portName = HardwareSetting.getSensorSerialCom();
            baud = HardwareSetting.getSensorSerialComBaud();
        } else {
            portName = HardwareSetting.getControllerSerialCom();
            baud = HardwareSetting.getControllerSerialBaud();
        }
        return openSerialPort(portName, baud);
    }

    /**
     * 开启串口
     * 自定义端口号、波特率
     */
    public boolean openSerialPort(String serialName, String baud) {
        if (StringUtils.isEmpty(serialName)) {
            isStart = false;
            if (TYPE_SENSOR.equals(type)) {
                MyLog.sensorError("传感器由于没有正确配置串口号导致串口打开失败");
            } else {
                MyLog.controllerError("控制器由于没有正确配置串口号导致串口打开失败");
            }
            return isStart;
        }
        try {
            serialPort = new SerialPort(new File(serialName), Integer.parseInt(baud), 0, 8, 1);
            inputStream = serialPort.getInputStream();
            outputStream = serialPort.getOutputStream();
            isStart = true;
            if (TYPE_SENSOR.equals(type)) {
                MyLog.sensor("传感器串口" + serialName + "打开成功");
            } else {
                MyLog.controller("控制器串口" + serialName + "打开成功");
            }
        } catch (IOException e) {
            e.printStackTrace();
            isStart = false;
            if (TYPE_SENSOR.equals(type)) {
                MyLog.sensorError("传感器串口<" + serialName + ">打开失败");
            } else {
                MyLog.controllerError("控制器串口<" + serialName + ">打开失败");
            }
        }
        return isStart;
    }

    /**
     * 关闭串口
     */
    public boolean close() {
        try {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
            if (serialPort != null) {
                serialPort.close();
            }
            isStart = false;
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 发送数据并等待接收响应
     *
     * @param device 设备类型 用于确定输出日志的类型
     * @param data   要发送的数据
     * @return 接收到的数据或空字符串（如果超时）
     */
    public String sendAndReceiveSerialData(Device device, String data) {
        boolean isSensor = false;
        if (!isStart || inputStream == null) {
            MyLog.appError("串口未打开但尝试发送数据");
            return null;
        }
        if (device instanceof Sensor) {
            //传感器在使用
            isSensor = true;
        }
        String readString = null;
        try {
            byte[] sendData = DataUtils.HexToByteArr(data);
            outputStream.write(sendData);
            outputStream.flush();
            T = "T:" + data;
            if (isSensor) {
                MyLog.sensor(T);
            } else {
                MyLog.controller(T);
            }
            long startTime = System.currentTimeMillis();
            //最多等待1s
            while (System.currentTimeMillis() - startTime < 1000) {
                int available = inputStream.available();
                if (available > 0) {
                    byte[] readData = new byte[available];
                    int size = inputStream.read(readData);
                    if (size > 0) {
                        byte[] readData2 = new byte[size];
                        for (int i = 0; i < size; i++) {
                            readData2[i] = readData[i];
                        }
                        readString = DataUtils.ByteArrToHex(readData2);
                        if (readString.startsWith(data.substring(0, 2))) {
                            //串口接收正确数据
                            //持续接收，直到接收到完整的数据，使用CRC校验readString后4位
                            long start = System.currentTimeMillis();
                            while (System.currentTimeMillis() - start < 1000 && !passCRC(readString)) {
                                available = inputStream.available();
                                if (available > 0) {
                                    readData = new byte[available];
                                    size = inputStream.read(readData);
                                    if (size > 0) {
                                        readData2 = new byte[size];
                                        for (int i = 0; i < size; i++) {
                                            readData2[i] = readData[i];
                                        }
                                        String newReadString = DataUtils.ByteArrToHex(readData2);
                                        readString = readString + newReadString;
                                        //继续接收，拼接新数据：
                                        if (isSensor) {
                                            MyLog.sensor("R,join:" + newReadString);
                                        } else {
                                            MyLog.controller("R,join:" + newReadString);
                                        }
                                    }
                                }
                            }
                            //串口接收正确完整数据：
                            R = "R:" + readString;
                            if (isSensor) {
                                MyLog.sensor(R);
                            } else {
                                MyLog.controller(R);
                            }
                            device.setFailTimes(0);
                            return readString;
                        } else {
                            //串口接收错误数据
                            R = "R,error:" + readString;
                            if (isSensor) {
                                MyLog.sensorError(R);
                            } else {
                                MyLog.controllerError(R);
                            }
                        }
                    }
                }
                Thread.sleep(100);
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        if (StringUtils.isEmpty(readString)) {
            R = "R:串口超时未接收数据";
            if (isSensor) {
                MyLog.sensorError(R);
            } else {
                MyLog.controllerError(R);
            }
        }
        //操作失败
        device.addFailTimes();
        return null;
    }

    /**
     * 发送无需设备返回的串口指令。
     * 例如部分广播控制命令只写入总线，不会回传响应帧。
     */
    public boolean sendSerialDataNoResponse(Device device, String data) {
        boolean isSensor = device instanceof Sensor;
        if (!isStart || outputStream == null) {
            MyLog.appError("串口未打开但尝试发送数据");
            return false;
        }
        try {
            byte[] sendData = DataUtils.HexToByteArr(data);
            outputStream.write(sendData);
            outputStream.flush();
            T = "T:" + data;
            R = "R:无返回";
            if (isSensor) {
                MyLog.sensor(T);
                MyLog.sensor(R);
            } else {
                MyLog.controller(T);
                MyLog.controller(R);
            }
            device.setFailTimes(0);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        device.addFailTimes();
        return false;
    }

    public String clearInputBuffer(long waitMs) {
        if (!isStart || inputStream == null) {
            return "";
        }
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        long endTime = System.currentTimeMillis() + Math.max(waitMs, 0);
        try {
            do {
                int available = inputStream.available();
                if (available > 0) {
                    byte[] readData = new byte[available];
                    int size = inputStream.read(readData);
                    if (size > 0) {
                        buffer.write(readData, 0, size);
                    }
                } else if (waitMs > 0) {
                    Thread.sleep(20);
                }
            } while (System.currentTimeMillis() < endTime);
        } catch (IOException | InterruptedException e) {
            if (e instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }
            e.printStackTrace();
        }
        byte[] readData = buffer.toByteArray();
        return readData.length == 0 ? "" : DataUtils.ByteArrToHex(readData);
    }

    public boolean isStart() {
        return isStart;
    }

    public void setStart(boolean start) {
        isStart = start;
    }

    /**
     * CRC校验 readString后4位是否正确
     *
     * @param readString 读取到的数据
     * @return 校验结果
     */
    private boolean passCRC(String readString) {
        try {
            String crc = readString.substring(readString.length() - 4);
            String data = readString.substring(0, readString.length() - 4);
            String realCRC = Device.getCRC(data);
            return crc.equals(realCRC);
        } catch (Exception e) {
            return false;
        }
    }
}


