/*
 * 文 件 名:  BaseFrame
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  协议帧基类
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/12/22
 */
package com.ruoyi.ems.charging.model;

import com.ruoyi.ems.charging.protocol.ProtocolConstants;
import com.ruoyi.ems.charging.utils.ByteUtils;
import com.ruoyi.ems.charging.utils.CRC16Utils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Data;

/**
 * 协议帧基类
 * 帧格式: 起始标志(1B) + 数据长度(1B) + 序列号域(2B) + 加密标志(1B) + 帧类型标志(1B) + 消息体(NB) + 帧校验域(2B)
 *
 * @author lvwenbin
 * @version [版本号, 2025/12/22]
 */
@Data
public abstract class BaseFrame {
    
    /**
     * 起始标志 (固定0x68)
     */
    protected byte startFlag = ProtocolConstants.START_FLAG;
    
    /**
     * 数据长度 (序列号域+加密标志+帧类型标志+消息体的字节数)
     */
    protected int dataLength;
    
    /**
     * 序列号域 (数据包发送顺序号)
     */
    protected int sequenceNo;
    
    /**
     * 加密标志 (0x00:不加密, 0x01:3DES)
     */
    protected byte encryptFlag = ProtocolConstants.ENCRYPT_NONE;
    
    /**
     * 帧类型标志
     */
    protected byte frameType;
    
    /**
     * 帧校验域 (CRC16)
     */
    protected int crc;
    
    /**
     * 获取帧类型码
     *
     * @return 帧类型码
     */
    public abstract byte getFrameType();
    
    /**
     * 编码消息体
     *
     * @param buf ByteBuf
     */
    protected abstract void encodeBody(ByteBuf buf);
    
    /**
     * 解码消息体
     *
     * @param buf ByteBuf
     */
    protected abstract void decodeBody(ByteBuf buf);
    
    /**
     * 编码完整帧
     *
     * @return 编码后的字节数组
     */
    public byte[] encode() {
        // 先编码消息体
        ByteBuf bodyBuf = Unpooled.buffer();
        encodeBody(bodyBuf);
        byte[] bodyBytes = new byte[bodyBuf.readableBytes()];
        bodyBuf.readBytes(bodyBytes);
        bodyBuf.release();
        
        // 计算数据长度 = 序列号域(2) + 加密标志(1) + 帧类型标志(1) + 消息体长度
        this.dataLength = 2 + 1 + 1 + bodyBytes.length;
        
        // 组装完整帧
        ByteBuf frameBuf = Unpooled.buffer();
        frameBuf.writeByte(startFlag);                          // 起始标志
        frameBuf.writeByte(dataLength);                         // 数据长度
        frameBuf.writeShortLE(sequenceNo);                      // 序列号域(低位在前)
        frameBuf.writeByte(encryptFlag);                        // 加密标志
        frameBuf.writeByte(getFrameType());                     // 帧类型标志
        frameBuf.writeBytes(bodyBytes);                         // 消息体
        
        // 计算CRC (从序列号域到数据域)
        byte[] crcData = new byte[dataLength];
        frameBuf.getBytes(2, crcData); // 从索引2开始读取(跳过起始标志和数据长度)
        this.crc = CRC16Utils.calculateCRC(crcData);
        
        frameBuf.writeShortLE(crc);                             // 帧校验域(低位在前)
        
        byte[] result = new byte[frameBuf.readableBytes()];
        frameBuf.readBytes(result);
        frameBuf.release();
        
        return result;
    }
    
    /**
     * 编码到ByteBuf
     *
     * @param buf ByteBuf
     */
    public void encode(ByteBuf buf) {
        byte[] encoded = encode();
        buf.writeBytes(encoded);
    }
    
    /**
     * 解码完整帧
     *
     * @param data 字节数组
     * @return 是否解码成功
     */
    public boolean decode(byte[] data) {
        ByteBuf buf = Unpooled.wrappedBuffer(data);
        try {
            return decode(buf);
        } finally {
            buf.release();
        }
    }
    
    /**
     * 解码完整帧
     *
     * @param buf ByteBuf
     * @return 是否解码成功
     */
    public boolean decode(ByteBuf buf) {
        if (buf.readableBytes() < 6) { // 最小帧长度
            return false;
        }
        
        // 读取帧头
        this.startFlag = buf.readByte();
        if (this.startFlag != ProtocolConstants.START_FLAG) {
            return false;
        }
        
        this.dataLength = buf.readByte() & 0xFF;
        
        // 检查数据长度是否合法
        if (buf.readableBytes() < dataLength + 2) { // 数据域 + CRC
            return false;
        }
        
        // 记录CRC校验起始位置
        int crcStartIndex = buf.readerIndex();
        
        this.sequenceNo = buf.readShortLE() & 0xFFFF;
        this.encryptFlag = buf.readByte();
        this.frameType = buf.readByte();
        
        // 解码消息体
        int bodyLength = dataLength - 4; // 减去序列号域(2)+加密标志(1)+帧类型标志(1)
        if (bodyLength > 0) {
            decodeBody(buf);
        }
        
        // 读取CRC
        this.crc = buf.readShortLE() & 0xFFFF;
        
        // 验证CRC
        buf.readerIndex(crcStartIndex);
        byte[] crcData = new byte[dataLength];
        buf.readBytes(crcData);
        int calculatedCrc = CRC16Utils.calculateCRC(crcData);
        
        // 跳过CRC字节
        buf.skipBytes(2);
        
        return calculatedCrc == this.crc;
    }
    
    /**
     * 获取帧类型名称
     *
     * @return 帧类型名称
     */
    public String getFrameTypeName() {
        return String.format("0x%02X", getFrameType() & 0xFF);
    }
    
    @Override
    public String toString() {
        return String.format("%s[seq=%d, type=%s]", 
            getClass().getSimpleName(), sequenceNo, getFrameTypeName());
    }
}
