package com.cmvr.quic.gateway.protocol;

import io.netty.buffer.ByteBuf;

/** QUIC edge v1固定64字节媒体DATAGRAM头。 */
public final class DatagramHeader {

    public static final int MAGIC = 0x434d5144;
    public static final int PROTOCOL_VERSION = 1;
    public static final int HEADER_BYTES = 64;
    public static final int FLAG_KEY_FRAME = 1;
    public static final int FLAG_DISCONTINUITY = 1 << 2;
    public static final int ALLOWED_FLAGS = FLAG_KEY_FRAME | FLAG_DISCONTINUITY;

    private final int kind;
    private final int flags;
    private final int fragmentIndex;
    private final int fragmentCount;
    private final int payloadSize;
    private final long trackId;
    private final long codecGenerationToken;
    private final long sessionEpoch;
    private final long packetSequence;
    private final long frameSequence;
    private final long captureTimestampUs;
    private final int frameSize;
    private final int fragmentOffset;

    private DatagramHeader(int kind, int flags, int fragmentIndex, int fragmentCount,
                           int payloadSize, long trackId, long codecGenerationToken,
                           long sessionEpoch, long packetSequence, long frameSequence,
                           long captureTimestampUs, int frameSize, int fragmentOffset) {
        this.kind = kind;
        this.flags = flags;
        this.fragmentIndex = fragmentIndex;
        this.fragmentCount = fragmentCount;
        this.payloadSize = payloadSize;
        this.trackId = trackId;
        this.codecGenerationToken = codecGenerationToken;
        this.sessionEpoch = sessionEpoch;
        this.packetSequence = packetSequence;
        this.frameSequence = frameSequence;
        this.captureTimestampUs = captureTimestampUs;
        this.frameSize = frameSize;
        this.fragmentOffset = fragmentOffset;
    }

    public static DatagramHeader decode(ByteBuf datagram) {
        int base = datagram.readerIndex();
        int totalBytes = datagram.readableBytes();
        if (totalBytes < HEADER_BYTES) {
            throw new IllegalArgumentException("媒体DATAGRAM不足64字节");
        }
        if (datagram.getInt(base) != MAGIC) {
            throw new IllegalArgumentException("媒体DATAGRAM magic不匹配");
        }
        if (datagram.getUnsignedByte(base + 4) != PROTOCOL_VERSION) {
            throw new IllegalArgumentException("媒体DATAGRAM协议版本不支持");
        }
        int kind = datagram.getUnsignedByte(base + 5);
        int flags = datagram.getUnsignedShort(base + 6);
        int headerSize = datagram.getUnsignedShort(base + 8);
        int fragmentIndex = datagram.getUnsignedShort(base + 10);
        int fragmentCount = datagram.getUnsignedShort(base + 12);
        int payloadSize = datagram.getUnsignedShort(base + 14);
        long frameSizeUnsigned = datagram.getUnsignedInt(base + 56);
        long offsetUnsigned = datagram.getUnsignedInt(base + 60);
        if (headerSize != HEADER_BYTES || (flags & ~ALLOWED_FLAGS) != 0 || kind < 1 || kind > 2
                || fragmentCount == 0 || fragmentCount > 8192 || fragmentIndex >= fragmentCount
                || payloadSize != totalBytes - HEADER_BYTES || frameSizeUnsigned == 0
                || frameSizeUnsigned > Integer.MAX_VALUE || offsetUnsigned > frameSizeUnsigned
                || payloadSize > frameSizeUnsigned - offsetUnsigned) {
            throw new IllegalArgumentException("媒体DATAGRAM头字段不合法");
        }
        return new DatagramHeader(kind, flags, fragmentIndex, fragmentCount, payloadSize,
                datagram.getUnsignedInt(base + 16), datagram.getUnsignedInt(base + 20),
                datagram.getLong(base + 24), datagram.getLong(base + 32),
                datagram.getLong(base + 40), datagram.getLong(base + 48),
                (int) frameSizeUnsigned, (int) offsetUnsigned);
    }

    public int getKind() { return kind; }
    public int getFlags() { return flags; }
    public int getFragmentIndex() { return fragmentIndex; }
    public int getFragmentCount() { return fragmentCount; }
    public int getPayloadSize() { return payloadSize; }
    public long getTrackId() { return trackId; }
    public long getCodecGenerationToken() { return codecGenerationToken; }
    public long getSessionEpoch() { return sessionEpoch; }
    public long getPacketSequence() { return packetSequence; }
    public long getFrameSequence() { return frameSequence; }
    public long getCaptureTimestampUs() { return captureTimestampUs; }
    public int getFrameSize() { return frameSize; }
    public int getFragmentOffset() { return fragmentOffset; }
}
