package com.ruoyi.web.core.nfid;

import com.ruoyi.common.utils.HexUtil;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

public class NFIDFrameParser {

    // 帧头标识
    public static final byte FRAME_HEADER = (byte) 0x5A;

    // 最大数据长度
    private static final int MAX_DATA_LENGTH = 1024;

    // 最小帧长度(帧头1 + 控制字4 + 长度2 + 校验2 = 9字节)
    private static final int MIN_FRAME_LENGTH = 9;

    // CRC16-CCITT多项式(X16 + X15 + X2 + 1)
    private static final int CRC16_POLY = 0x1021;
    private static final int CRC16_INIT = 0x0000;

    // 添加帧类型判断方法
    //判断是否待读取
    public static boolean isReadResponseRequiredFrame(NFIDFrame frame) {
        // 判断条件：数据长度在9-20字节之间
        return frame.getDataLength() >= 9 && frame.getDataLength() < 20;
    }

    // 添加响应帧生成方法
    public static byte[] generateReadResponseFrame(int index) {
        // 5A0001021000080000000X01020006ED08
        // 其中X是1-4的循环索引
        String hexStr = String.format("5A0001021000080000000%d01020006ED08", index);
        return HexUtil.hexToBytes(hexStr);
    }

    //判断是否为链接状态确认信息
    public static boolean isStatusResponseRequiredFrame(NFIDFrame frame) {
        // 判断条件：数据长度在4字节
        return frame.getDataLength() == 4;
    }

    // 判断是否为读写器信息
    public static boolean isDeviceInfoFrame(NFIDFrame frame) {
        // 判断条件：数据长度在70-90字节之间
        return frame.getDataLength() >= 70;
    }

    // 判断是否为标签信息
    public static boolean isTagInfoFrame(NFIDFrame frame) {
        // 判断条件：数据长度
        List<Parameter> parameters = frame.getParameters();
        return parameters.size() == 1 && parameters.get(0).getLength() == 12;
    }

    /**
     * 解析接收到的数据
     * @param data 接收到的原始字节数组
     * @return 解析出的完整帧列表
     */
    public static List<NFIDFrame> parseFrames(byte[] data) {
        List<NFIDFrame> frames = new ArrayList<>();
        ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);

        while (buffer.remaining() >= MIN_FRAME_LENGTH) {
            // 查找帧头
            int startPos = buffer.position();
            if (buffer.get() != FRAME_HEADER) {
                continue; // 不是帧头，继续查找
            }

            // 标记帧开始位置
            int frameStart = startPos;

            try {
                // 读取控制字(4字节)
                int controlWord = buffer.getInt();

                // 检查RS485标志位(假设第31位是RS485标志位)
                boolean hasRS485Address = (controlWord & 0x80000000) != 0;

                // 读取设备地址(如果有)
                byte deviceAddress = 0;
                if (hasRS485Address) {
                    if (buffer.remaining() < 1) {
                        break; // 数据不足
                    }
                    deviceAddress = buffer.get();
                }

                // 读取数据长度(2字节)
                if (buffer.remaining() < 2) {
                    break; // 数据不足
                }
                int dataLength = buffer.getShort() & 0xFFFF;

                // 检查数据长度是否合法
                if (dataLength > MAX_DATA_LENGTH) {
                    // 非法长度，跳过这个帧头
                    buffer.position(frameStart + 1);
                    continue;
                }

                // 检查是否有足够的数据(数据 + 校验码)
                if (buffer.remaining() < dataLength + 2) {
                    break; // 数据不足
                }

                // 读取数据内容
                byte[] frameData = new byte[dataLength];
                buffer.get(frameData);

                // 读取校验码
                int receivedChecksum = buffer.getShort() & 0xFFFF;

                // 计算校验范围(从控制字到数据结束)
                int checksumStart = frameStart + 1;
                int checksumEnd = frameStart + 1 + (hasRS485Address ? 5 : 4) + 2 + dataLength;
                byte[] checksumData = new byte[checksumEnd - checksumStart];
                System.arraycopy(data, checksumStart, checksumData, 0, checksumData.length);

                // 计算CRC校验
                int calculatedChecksum = calculateCRC16(checksumData);

                // 校验通过则添加到结果列表
                if (receivedChecksum == calculatedChecksum) {
                    NFIDFrame frame = new NFIDFrame();
                    frame.setControlWord(controlWord);
                    frame.setHasRS485Address(hasRS485Address);
                    frame.setDeviceAddress(deviceAddress);
                    frame.setDataLength(dataLength);
                    frame.setData(frameData);
                    frame.setChecksum(receivedChecksum);
                    frames.add(frame);
                } else {
                    // 校验失败，跳过这个帧头继续查找
                    buffer.position(frameStart + 1);
                }

            } catch (Exception e) {
                // 发生异常，跳过这个帧头继续查找
                buffer.position(frameStart + 1);
            }
        }

        return frames;
    }

    /**
     * 计算CRC16-CCITT校验值
     * @param data 要计算的数据
     * @return CRC16校验值
     */
    private static int calculateCRC16(byte[] data) {
        int crc = CRC16_INIT;

        for (byte b : data) {
            crc ^= (b & 0xFF) << 8;
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x8000) != 0) {
                    crc = (crc << 1) ^ CRC16_POLY;
                } else {
                    crc <<= 1;
                }
                crc &= 0xFFFF; // 确保保持16位
            }
        }

        return crc;
    }
    public static List<Parameter> parseParameters(byte[] data) {

        List<Parameter> parameters = new ArrayList<>();
        ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);

        while (buffer.remaining() > 0) {
            // 读取参数长度(2字节)
            if (buffer.remaining() < 2) break;
            int paramLength = buffer.getShort() & 0xFFFF;
            //System.out.println(paramLength);
            // 检查是否有足够的数据
            if (buffer.remaining() < paramLength) break;

            // 读取参数内容
            byte[] paramValue = new byte[paramLength];
            buffer.get(paramValue);
           // System.out.println(bytesToHex(paramValue));
            parameters.add(new Parameter( paramLength, paramValue));
        }

        return parameters;
    }

    /**
     * 参数数据结构
     */
    public static class Parameter {
        private final int length;
        private final byte[] value;

        public Parameter(int length, byte[] value) {

            this.length = length;
            this.value = value;
        }


        public int getLength() {
            return length;
        }

        public byte[] getValue() {
            return value;
        }

        @Override
        public String toString() {
            return String.format("Parameter{ Length=%d, Value=%s}",
                    length, HexUtil.bytesToHex(value));
        }

    }
    /**
     * NFID帧数据结构
     */
    public static class NFIDFrame {
        private int controlWord;         // 协议控制字(4字节)
        private boolean hasRS485Address;  // 是否有RS485地址
        private byte deviceAddress;       // 串行设备地址(1字节)
        private int dataLength;           // 数据长度(2字节)
        private byte[] data;              // 数据参数(N字节)
        private int checksum;            // 校验码(2字节)

        private List<Parameter> parameters; // 新增参数列表
        // getters and setters

        public List<Parameter> getParameters() {
            if (parameters == null && data != null) {
                parameters = parseParameters(data);
            }
            return parameters;
        }

        public int getControlWord() {
            return controlWord;
        }

        public void setControlWord(int controlWord) {
            this.controlWord = controlWord;
        }

        public boolean isHasRS485Address() {
            return hasRS485Address;
        }

        public void setHasRS485Address(boolean hasRS485Address) {
            this.hasRS485Address = hasRS485Address;
        }

        public byte getDeviceAddress() {
            return deviceAddress;
        }

        public void setDeviceAddress(byte deviceAddress) {
            this.deviceAddress = deviceAddress;
        }

        public int getDataLength() {
            return dataLength;
        }

        public void setDataLength(int dataLength) {
            this.dataLength = dataLength;
        }

        public byte[] getData() {
            return data;
        }

        public void setData(byte[] data) {
            this.data = data;
        }

        public int getChecksum() {
            return checksum;
        }

        public void setChecksum(int checksum) {
            this.checksum = checksum;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("NFIDFrame{\n");
            sb.append("  controlWord=0x").append(Integer.toHexString(controlWord)).append("\n");
            sb.append("  hasRS485Address=").append(hasRS485Address).append("\n");
            if (hasRS485Address) {
                sb.append("  deviceAddress=0x").append(String.format("%02X", deviceAddress & 0xFF)).append("\n");
            }
            sb.append("  dataLength=").append(dataLength).append("\n");
            sb.append("  checksum=0x").append(String.format("%04X", checksum)).append("\n");

            // 添加参数信息
            List<Parameter> params = getParameters();
            if (!params.isEmpty()) {
                sb.append("  parameters=[\n");
                for (Parameter param : params) {
                    sb.append("    ").append(param).append("\n");
                }
                sb.append("  ]\n");
            }

            sb.append("}");
            return sb.toString();
        }

    }
}