package com.lingniu.ingest.protocol.gb32960.codec;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.lingniu.ingest.codec.BccChecksum;

import java.util.List;

/**
 * Netty 层的帧分帧器：从字节流里提取一条完整的 32960 帧。
 *
 * <p>支持两种起始符：{@code 0x23 0x23 (##)} 2016 版，{@code 0x24 0x24 ($$)} 2025 版。
 * 只负责拆包粘包，不做业务解析。具体字段解析交给 {@link Gb32960MessageDecoder}。
 *
 * <p>开启 DEBUG 日志可以看到每次读到的字节、每条产出的完整帧以及因长度/起始符不匹配被丢弃的字节数。
 */
public class Gb32960FrameDecoder extends ByteToMessageDecoder {

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

    private static final int HEADER_LEN = 24; // 起始(2) + cmd(1) + rsp(1) + vin(17) + enc(1) + len(2)
    private static final int MAX_FRAME = 8 * 1024;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        if (log.isDebugEnabled() && in.readableBytes() > 0) {
            int peek = Math.min(in.readableBytes(), 64);
            log.debug("[gb32960] frame-decoder rx bytes={} head={}",
                    in.readableBytes(),
                    ByteBufUtil.hexDump(in, in.readerIndex(), peek));
        }
        while (in.readableBytes() >= HEADER_LEN) {
            int startIdx = findStart(in);
            if (startIdx < 0 || startIdx > in.writerIndex() - HEADER_LEN) {
                int drop = Math.max(0, in.readableBytes() - 1);
                if (drop > 0) {
                    log.debug("[gb32960] frame-decoder skip {} bytes (no start symbol)", drop);
                    in.skipBytes(drop);
                }
                return;
            }
            if (startIdx != in.readerIndex()) {
                int drop = startIdx - in.readerIndex();
                log.debug("[gb32960] frame-decoder skip {} leading bytes before start", drop);
                in.skipBytes(drop);
            }
            int dataLen = in.getUnsignedShort(in.readerIndex() + 22);
            int frameLen = frameLength(in, in.readerIndex(), dataLen);
            if (frameLen > MAX_FRAME) {
                log.warn("[gb32960] frame-decoder oversized frame len={} peer={}, skip start",
                        frameLen, ctx.channel().remoteAddress());
                in.skipBytes(2);
                continue;
            }
            if (in.readableBytes() < frameLen) return;

            byte[] frame = new byte[frameLen];
            in.readBytes(frame);
            if (log.isDebugEnabled()) {
                log.debug("[gb32960] frame-decoder emit len={} start={} peer={}",
                        frameLen,
                        String.format("%02x%02x", frame[0], frame[1]),
                        ctx.channel().remoteAddress());
            }
            out.add(frame);
        }
    }

    private static int frameLength(ByteBuf in, int readerIndex, int dataLen) {
        int standardLen = HEADER_LEN + dataLen + 1;
        int command = in.getUnsignedByte(readerIndex + 2);
        if (command != 0x05 || dataLen != 41) {
            return standardLen;
        }
        // 现场平台登入包存在"声明 41B、实际 53B"的兼容形态；只有扩展长度 BCC 成立时才按 53B 分帧，
        // 否则回退标准长度，避免误吞下一条帧。
        int extendedLen = HEADER_LEN + 53 + 1;
        if (in.readableBytes() < extendedLen) {
            return standardLen;
        }
        byte[] extended = new byte[extendedLen];
        in.getBytes(readerIndex, extended);
        if (BccChecksum.verify(extended, 2, extended.length - 3, extended[extended.length - 1])) {
            return extendedLen;
        }
        return standardLen;
    }

    /** 查找下一个合法起始位置：两字节连续 0x23 0x23 或 0x24 0x24。 */
    private static int findStart(ByteBuf in) {
        int limit = in.writerIndex() - 1;
        for (int i = in.readerIndex(); i < limit; i++) {
            byte b = in.getByte(i);
            if ((b == 0x23 || b == 0x24) && in.getByte(i + 1) == b) {
                return i;
            }
        }
        return -1;
    }
}
