package com.lingniu.ingest.protocol.jt1078.media;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.MessageToMessageDecoder;

import java.net.InetSocketAddress;
import java.time.Instant;
import java.util.List;

public final class Jt1078UdpRtpPacketDecoder extends MessageToMessageDecoder<DatagramPacket> {

    private final int maxPacketLength;

    public Jt1078UdpRtpPacketDecoder(int maxPacketLength) {
        this.maxPacketLength = maxPacketLength;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) throws Exception {
        int packetLength = msg.content().readableBytes();
        if (packetLength > maxPacketLength) {
            out.add(malformed(msg, packetLength, "jt1078 udp rtp packet too large: " + packetLength));
            return;
        }
        if (packetLength < Jt1078RtpPacketParser.HEADER_LEN) {
            out.add(malformed(msg, packetLength, "jt1078 udp rtp packet too short: " + packetLength));
            return;
        }
        try {
            out.add(new Jt1078ReceivedRtpPacket(
                    Jt1078RtpPacketParser.parse(msg.content().slice()),
                    peer(msg.sender())));
        } catch (CorruptedFrameException | IndexOutOfBoundsException e) {
            out.add(malformed(msg, packetLength, e.getMessage()));
        }
    }

    private static Jt1078MalformedRtpPacket malformed(DatagramPacket msg, int packetLength, String reason) {
        byte[] raw = new byte[Math.max(0, msg.content().readableBytes())];
        msg.content().getBytes(msg.content().readerIndex(), raw);
        return new Jt1078MalformedRtpPacket(
                "udp",
                peer(msg.sender()),
                Jt1078RtpPacketParser.peekSim(msg.content().slice()),
                raw,
                packetLength,
                reason == null ? "" : reason,
                Instant.now());
    }

    private static String peer(InetSocketAddress sender) {
        if (sender == null) {
            return "";
        }
        return sender.getAddress().getHostAddress() + ":" + sender.getPort();
    }
}
