package com.lingniu.ingest.protocol.jt1078;

import com.lingniu.ingest.protocol.jt1078.media.Jt1078RtpPacket;
import com.lingniu.ingest.protocol.jt1078.media.Jt1078RtpPacketDecoder;
import com.lingniu.ingest.protocol.jt1078.media.Jt1078MalformedRtpPacket;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class Jt1078RtpPacketDecoderTest {

    @Test
    void decodesCompleteTcpRtpPacketAndKeepsPayloadOnly() {
        EmbeddedChannel channel = new EmbeddedChannel(new Jt1078RtpPacketDecoder(1024));

        channel.writeInbound(Unpooled.wrappedBuffer(packet(7, "013800138000", 2, 3, 1, 123456789L,
                new byte[]{1, 2, 3, 4})));

        Jt1078RtpPacket packet = channel.readInbound();
        assertThat(packet.sequence()).isEqualTo(7);
        assertThat(packet.sim()).isEqualTo("013800138000");
        assertThat(packet.channelId()).isEqualTo(2);
        assertThat(packet.dataType()).isEqualTo(3);
        assertThat(packet.packetType()).isEqualTo(1);
        assertThat(packet.timestamp()).isEqualTo(123456789L);
        assertThat(packet.payload()).containsExactly(1, 2, 3, 4);
    }

    @Test
    void waitsForFragmentedPayloadBeforeEmittingPacket() {
        byte[] packet = packet(8, "013800138000", 1, 0, 0, 1L, new byte[]{9, 8, 7});
        EmbeddedChannel channel = new EmbeddedChannel(new Jt1078RtpPacketDecoder(1024));

        channel.writeInbound(Unpooled.wrappedBuffer(packet, 0, packet.length - 1));
        assertThat((Object) channel.readInbound()).isNull();
        channel.writeInbound(Unpooled.wrappedBuffer(packet, packet.length - 1, 1));

        Jt1078RtpPacket decoded = channel.readInbound();
        assertThat(decoded.payload()).containsExactly(9, 8, 7);
    }

    @Test
    void oversizedTcpRtpPacketEmitsMalformedCandidateInsteadOfClosingPipeline() {
        byte[] packet = packet(9, "013800138000", 1, 0, 0, 1L, new byte[]{1, 2, 3, 4});
        EmbeddedChannel channel = new EmbeddedChannel(new Jt1078RtpPacketDecoder(30));

        channel.writeInbound(Unpooled.wrappedBuffer(packet));

        Jt1078MalformedRtpPacket malformed = channel.readInbound();
        assertThat(malformed.transport()).isEqualTo("tcp");
        assertThat(malformed.sim()).isEqualTo("013800138000");
        assertThat(malformed.reason()).contains("too large");
        assertThat(malformed.rawBytes()).containsExactly(packet);
        assertThat(malformed.packetLength()).isEqualTo(packet.length);
        assertThat((Object) channel.readInbound()).isNull();
    }

    static byte[] packet(int sequence, String sim, int channelId, int dataType, int packetType,
                         long timestamp, byte[] payload) {
        byte[] out = new byte[28 + payload.length];
        out[0] = 0x30;
        out[1] = 0x31;
        out[2] = 0x63;
        out[3] = 0x64;
        out[4] = (byte) (sequence >>> 8);
        out[5] = (byte) sequence;
        byte[] bcd = bcd(sim);
        System.arraycopy(bcd, 0, out, 6, bcd.length);
        out[12] = (byte) channelId;
        out[13] = (byte) ((dataType << 4) | (packetType & 0x0F));
        for (int i = 0; i < 8; i++) {
            out[14 + i] = (byte) (timestamp >>> (56 - i * 8));
        }
        out[26] = (byte) (payload.length >>> 8);
        out[27] = (byte) payload.length;
        System.arraycopy(payload, 0, out, 28, payload.length);
        return out;
    }

    private static byte[] bcd(String digits) {
        byte[] out = new byte[6];
        for (int i = 0; i < out.length; i++) {
            int hi = Character.digit(digits.charAt(i * 2), 10);
            int lo = Character.digit(digits.charAt(i * 2 + 1), 10);
            out[i] = (byte) ((hi << 4) | lo);
        }
        return out;
    }
}
