package com.zksy.audio.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.logging.Logger;

public class ProtocolUtils {
    private static final Logger logger = Logger.getLogger(ProtocolUtils.class.getName());
    /**
     * 将2000年起始的时间戳转换为LocalDateTime
     */
    static LocalDateTime convertToDateTime(int timestamp) {
        try {
            // 定义基准时间：2000年1月1日 00:00:00
            LocalDateTime baseTime = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
            // 累加时间戳（秒数）
            return baseTime.plusSeconds(timestamp);
        } catch (Exception e) {
            return null;
        }
    }
    // 获取当前时间并转换为2000年起始的时间戳
    static int getCurrentTimestampSince2000() {
        LocalDateTime now = LocalDateTime.now(ZoneOffset.of("+8"));
        LocalDateTime baseTime = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
        // 计算时间差（秒数）
        return (int) java.time.Duration.between(baseTime, now).getSeconds();
    }
    public static int calculateCRC16(byte[] data) {
        int crc = 0xFFFF;
        for (byte b : data) {
            crc ^= (b & 0xFF);
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x0001) != 0) crc = (crc >> 1) ^ 0xA001;
                else crc >>= 1;
            }
        }
        int high = (crc & 0xFF00) >> 8;
        int low = crc & 0x00FF;
        return (high << 8) | low;
    }

    public static byte calculateXorCheck(byte[] data) {
        byte check = 0x00;
        for (byte b : data) check ^= b;
        return check;
    }

    /**
     * 构建上报回应包（对应帧类型 0x31）
     * 结构：
     * [系统识别码(3)] [整帧长度(2)] [包序号(1)] [帧类型0x31(1)]
     * [源地址长度(1)] [源地址(n)] [目的地址长度(1)] [目的地址(m)] [帧校验(1)]
     *
     * 示例（文档第11页）：
     * 12 34 56 00 16 81 31 0B 00 00 00 00 00 10 0B 13 81 23 45 67 80 D5
     */
    public static byte[] buildReportAckPacket(byte[] originalFrame, byte packetSequence) {
        int srcLenIndex = 7;
        int srcLen = originalFrame[srcLenIndex] & 0xFF;
        int srcByteLen = (srcLen + 1) / 2;
        int srcStart = srcLenIndex + 1;
        int srcEnd = srcStart + srcByteLen;

        int dstLenIndex = srcEnd;
        int dstLen = originalFrame[dstLenIndex] & 0xFF;
        int dstByteLen = (dstLen + 1) / 2;
        int dstStart = dstLenIndex + 1;
        int dstEnd = dstStart + dstByteLen;

        byte[] response = new byte[3 + 2 + 1 + 1 + 1 + dstByteLen + 1 + srcByteLen + 1];
        int idx = 0;

        // 系统识别码 12 34 56
        response[idx++] = 0x12;
        response[idx++] = 0x34;
        response[idx++] = 0x56;

        // 长度（待填充）
        int totalLen = response.length;
        response[idx++] = (byte) ((totalLen >> 8) & 0xFF);
        response[idx++] = (byte) (totalLen & 0xFF);

        // 包序号（与原帧一致）
        response[idx++] = packetSequence;

        // 帧类型：上报回应
        response[idx++] = 0x31;

        // 源地址 = 原帧的目的地址
        response[idx++] = (byte) dstLen;
        System.arraycopy(originalFrame, dstStart, response, idx, dstByteLen);
        idx += dstByteLen;

        // 目的地址 = 原帧的源地址
        response[idx++] = (byte) srcLen;
        System.arraycopy(originalFrame, srcStart, response, idx, srcByteLen);
        idx += srcByteLen;

        // 异或校验（从系统识别码到帧内容）
        byte xor = calculateXorCheck(Arrays.copyOfRange(response, 0, idx));
        response[idx++] = xor;

        return response;
    }

    /**
     * 构建结束通讯回应包（对应帧类型 0x34）
     * 结构：
     * [系统识别码(3)] [整帧长度(2)] [包序号(0x80)] [帧类型0x34(1)]
     * [源地址长度(1)] [源地址(n)] [目的地址长度(1)] [目的地址(m)] [帧校验(1)]
     *
     * 示例（文档第12页）：
     * 12 34 56 00 16 80 34 0B 13 81 23 45 67 80 0B 00 00 00 00 00 10 D1
     */
    public static byte[] buildShutdownAckPacket(byte[] originalFrame) {
        // 步骤1：动态提取地址信息（对齐validateMessage）
        int[] addrInfo = extractAddrInfo(originalFrame);
        int srcLenFlag = addrInfo[0];
        int srcAddrByteLen = addrInfo[1];
        int srcAddrStart = addrInfo[2];
        int srcAddrEnd = addrInfo[3];
        int destLenFlag = addrInfo[4];
        int destAddrByteLen = addrInfo[5];
        int destAddrStart = addrInfo[6];
        int destAddrEnd = addrInfo[7];

        // 步骤2：提取地址字节
        byte[] srcAddr = Arrays.copyOfRange(originalFrame, srcAddrStart, srcAddrEnd);
        byte[] destAddr = Arrays.copyOfRange(originalFrame, destAddrStart, destAddrEnd);

        // 步骤3：构造应答帧
        int totalLen = 3 + 2 + 1 + 1 + 1 + destAddrByteLen + 1 + srcAddrByteLen + 1;
        byte[] response = new byte[totalLen];
        int idx = 0;

        // 系统识别码
        response[idx++] = 0x12;
        response[idx++] = 0x34;
        response[idx++] = 0x56;

        // 长度
        response[idx++] = (byte) ((totalLen >> 8) & 0xFF);
        response[idx++] = (byte) (totalLen & 0xFF);

        // 包序号0x80 + 帧类型0x34
        response[idx++] = (byte) 0x80;
        response[idx++] = 0x34;

        // 源地址=原目的地址（动态）
        response[idx++] = (byte) destLenFlag;
        System.arraycopy(destAddr, 0, response, idx, destAddrByteLen);
        idx += destAddrByteLen;

        // 目的地址=原源地址（动态）
        response[idx++] = (byte) srcLenFlag;
        System.arraycopy(srcAddr, 0, response, idx, srcAddrByteLen);
        idx += srcAddrByteLen;

        // 异或校验
        byte xor = calculateXorCheck(Arrays.copyOfRange(response, 0, idx));
        response[idx++] = xor;

        return response;
    }
    public static byte[] buildCustomReplyFrame(byte[] request) throws IOException {
        // 步骤1：动态提取地址信息（对齐validateMessage）
        int[] addrInfo = extractAddrInfo(request);
        int srcLenFlag = addrInfo[0];
        int srcAddrByteLen = addrInfo[1]; // 原源地址长度 m
        int srcAddrStart = addrInfo[2];
        int srcAddrEnd = addrInfo[3];
        int destLenFlag = addrInfo[4];
        int destAddrByteLen = addrInfo[5]; // 原目的地址长度 n
        int destAddrStart = addrInfo[6];
        int destAddrEnd = addrInfo[7];

        // 步骤2：提取地址字节
        byte[] srcAddr = Arrays.copyOfRange(request, srcAddrStart, srcAddrEnd);
        byte[] destAddr = Arrays.copyOfRange(request, destAddrStart, destAddrEnd);

        // 步骤3：固定字段提取
        byte[] sysId = Arrays.copyOfRange(request, 0, 3); // 系统识别码（3字节）
        byte packetSeq = request[5]; // 包序号（1字节）
        byte frameType = request[6]; // 帧类型0x31（1字节）

        // 帧长度字段值 = 系统识别码（3字节）+2(自身) + 1(包序号) + 1(帧类型) + 1(源地址标识) + n(源地址) + 1(目的地址标识) + m(目的地址)
        int frameLenValue = 3 + 2 + 1 + 1 + 1 + destAddrByteLen + 1 + srcAddrByteLen+1;

        // 步骤4：构造帧体（直接写入正确长度，无需占位回填）
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bos.write(sysId); // 系统识别码（3字节）
        // 写入帧长度字段（包含自身2字节，高字节在前）
        bos.write((byte) ((frameLenValue >> 8) & 0xFF));
        bos.write((byte) (frameLenValue & 0xFF));

        // 步骤5：地址交换（动态长度）
        bos.write(packetSeq);          // 包序号
        bos.write(frameType);          // 帧类型
        bos.write(destLenFlag);        // 应答源地址标识=原目的标识
        bos.write(destAddr);           // 应答源地址=原目的地址（n字节）
        bos.write(srcLenFlag);         // 应答目的地址标识=原源标识
        bos.write(srcAddr);            // 应答目的地址=原源地址（m字节）

        // 步骤6：计算异或校验（仅计算不含校验位的帧体）
        byte[] tempFrame = bos.toByteArray();
        byte xor = calculateXorCheck(tempFrame);

        // 步骤7：拼接最终帧（帧体 + 异或校验位）
        ByteArrayOutputStream finalBos = new ByteArrayOutputStream();
        finalBos.write(tempFrame);
        finalBos.write(xor);

        return finalBos.toByteArray();
    }
    public static byte[] buildEndReplyFrame(byte[] request) throws IOException {
        // 步骤1：动态提取地址信息（对齐validateMessage）
        int[] addrInfo = extractAddrInfo(request);
        int srcLenFlag = addrInfo[0];
        int srcAddrByteLen = addrInfo[1]; // 源地址长度 n
        int srcAddrStart = addrInfo[2];
        int srcAddrEnd = addrInfo[3];
        int destLenFlag = addrInfo[4];
        int destAddrByteLen = addrInfo[5]; // 目的地址长度 m
        int destAddrStart = addrInfo[6];
        int destAddrEnd = addrInfo[7];

        // 步骤2：提取地址字节
        byte[] srcAddr = Arrays.copyOfRange(request, srcAddrStart, srcAddrEnd);
        byte[] destAddr = Arrays.copyOfRange(request, destAddrStart, destAddrEnd);

        // 步骤3：固定字段提取
        byte[] sysId = Arrays.copyOfRange(request, 0, 3); // 系统识别码（3字节）
        byte packetSeq = request[5]; // 包序号（1字节）

        // 步骤4：时间戳（4字节）
        long secondsSince2000 = getSecondsSince2000();
        byte[] timestamp = new byte[4];
        timestamp[0] = (byte) ((secondsSince2000 >> 24) & 0xFF);
        timestamp[1] = (byte) ((secondsSince2000 >> 16) & 0xFF);
        timestamp[2] = (byte) ((secondsSince2000 >> 8) & 0xFF);
        timestamp[3] = (byte) (secondsSince2000 & 0xFF);

        // ========== 核心修正：计算包含自身2字节的帧长度字段值 ==========
        // 帧长度字段值 = 2(自身) + 1(包序号) + 1(帧类型) + 1(源地址标识) + n(源地址) + 1(目的地址标识) + m(目的地址) + 4(时间戳)
        int frameLenValue =3+ 2 + 1 + 1 + 1 + srcAddrByteLen + 1 + destAddrByteLen + 4+1;
        // 拆分高低字节
        byte lenHigh = (byte) ((frameLenValue >> 8) & 0xFF);
        byte lenLow = (byte) (frameLenValue & 0xFF);

        // 步骤5：构造内容区（包序号到时间戳，不含长度字段）
        ByteArrayOutputStream content = new ByteArrayOutputStream();
        content.write(0X80);  // 包序号（1字节）
        content.write(0x34);       // 帧类型0x34（1字节）
        content.write(destLenFlag);// 目的地址标识（1字节）
        content.write(destAddr);   // 目的地址（m字节）
        content.write(srcLenFlag); // 源地址标识（1字节）srcLenFlag
        content.write(srcAddr);    // 源地址（n字节）
        content.write(timestamp);  // 时间戳（4字节）
        byte[] contentBytes = content.toByteArray();

        // 步骤6：拼帧（系统识别码 + 长度字段 + 内容区）
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bos.write(sysId);      // 系统识别码（3字节）
        bos.write(lenHigh);    // 长度高字节（包含自身2字节的总长度）
        bos.write(lenLow);     // 长度低字节
        bos.write(contentBytes);

        // 步骤7：计算并添加异或校验（仅计算不含校验位的帧体）
        byte[] noCheck = bos.toByteArray();
        byte xor = calculateXorCheck(noCheck);
        bos.write(xor);

        byte[] result = bos.toByteArray();
        System.out.println("结束帧应答: " + printHex(result));
        return result;
    }

    private static long getSecondsSince2000() {
        LocalDateTime base = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
        LocalDateTime now = LocalDateTime.now(ZoneOffset.of("+8"));
        return Duration.between(base, now).getSeconds();
    }
    private static String printHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) sb.append(String.format("%02X ", b));
        return sb.toString().trim();
    }
    /**
     * 核心工具方法：根据地址标识计算地址字节长度
     * 规则：标识值转10进制 → 奇数+1 → 除以2
     */
    private static int calculateAddrByteLength(int lenFlag) {
        int temp = lenFlag;
        // 奇数则+1
        if (temp % 2 != 0) {
            temp += 1;
        }
        // 除以2得到字节长度
        return temp / 2;
    }
    /**
     * 核心工具：正向解析源/目的地址信息（贴合实际报文结构）
     * 逻辑：
     * 1. 正向定位源地址标识(索引7)、源地址、目的地址标识、目的地址
     * 2. 找 0x01 2C 验证地址段结束位置，确保解析合法
     * @return 数组格式：[srcLenFlag, srcAddrByteLen, srcAddrStart, srcAddrEnd, destLenFlag, destAddrByteLen, destAddrStart, destAddrEnd]
     */
    private static int[] extractAddrInfo(byte[] request) {
        if (request == null || request.length < 25) { // 适配实际报文最小长度（82字节）
            throw new IllegalArgumentException("请求帧长度不足，无法解析地址信息，长度=" + (request == null ? 0 : request.length));
        }

        // ========== 步骤1：正向定位核心索引（贴合协议固定结构） ==========
        int srcLenIndex = 7; // 源地址标识固定在索引7
        int srcLenFlag = request[srcLenIndex] & 0xFF;
        int srcAddrByteLen = calculateAddrByteLength(srcLenFlag);
        int srcAddrStart = srcLenIndex + 1;
        int srcAddrEnd = srcAddrStart + srcAddrByteLen;

        // 目的地址标识 = 源地址结束索引
        int destLenIndex = srcAddrEnd;
        int destLenFlag = request[destLenIndex] & 0xFF;
        int destAddrByteLen = calculateAddrByteLength(destLenFlag);
        int destAddrStart = destLenIndex + 1;
        int destAddrEnd = destAddrStart + destAddrByteLen;

        // ========== 步骤2：验证地址段结束位置（找 0x01 2C） ==========
        int deviceCodeIndex = destAddrEnd;
        // 校验设备编码+功能码是否为 0x01 2C（地址段后必须紧跟）
        if (deviceCodeIndex + 1 >= request.length
                || request[deviceCodeIndex] != 0x01
                || request[deviceCodeIndex + 1] != 0x2C) {
            throw new IllegalArgumentException("地址段结束后未找到 0x01 2C 序列，地址解析异常：设备编码索引=" + deviceCodeIndex);
        }

        // ========== 步骤3：范围最终校验 ==========
        // 源地址范围校验
        if (srcAddrStart < 8 || srcAddrEnd > destLenIndex) {
            throw new IllegalArgumentException(
                    String.format("源地址范围异常：标识索引=%d 标识值=%d 计算长度=%d 起始=%d 结束=%d",
                            srcLenIndex, srcLenFlag, srcAddrByteLen, srcAddrStart, srcAddrEnd));
        }
        // 目的地址范围校验
        if (destAddrStart < (destLenIndex + 1) || destAddrEnd > deviceCodeIndex) {
            throw new IllegalArgumentException(
                    String.format("目的地址范围异常：标识索引=%d 标识值=%d 计算长度=%d 起始=%d 结束=%d",
                            destLenIndex, destLenFlag, destAddrByteLen, destAddrStart, destAddrEnd));
        }

        return new int[]{
                srcLenFlag, srcAddrByteLen, srcAddrStart, srcAddrEnd,
                destLenFlag, destAddrByteLen, destAddrStart, destAddrEnd
        };
    }

}
