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

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import java.util.concurrent.ThreadFactory;

public final class Jt1078UdpMediaStreamServer implements InitializingBean, DisposableBean {

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

    private final int port;
    private final int workerThreads;
    private final int maxPacketLength;
    private final Jt1078MediaArchiveService archiveService;
    private final Jt1078MalformedRtpEventPublisher malformedPublisher;

    private EventLoopGroup workers;
    private Channel serverChannel;

    public Jt1078UdpMediaStreamServer(int port,
                                      int workerThreads,
                                      int maxPacketLength,
                                      Jt1078MediaArchiveService archiveService,
                                      Jt1078MalformedRtpEventPublisher malformedPublisher) {
        this.port = port;
        this.workerThreads = workerThreads;
        this.maxPacketLength = maxPacketLength;
        this.archiveService = archiveService;
        this.malformedPublisher = malformedPublisher;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ThreadFactory wkTf = r -> new Thread(r, "jt1078-media-udp-worker");
        int threads = workerThreads == 0 ? Runtime.getRuntime().availableProcessors() : workerThreads;
        this.workers = new NioEventLoopGroup(threads, wkTf);

        Bootstrap b = new Bootstrap();
        b.group(workers)
                .channel(NioDatagramChannel.class)
                .option(ChannelOption.SO_BROADCAST, false)
                .option(ChannelOption.SO_RCVBUF, Math.max(1024 * 1024, maxPacketLength * 2))
                .handler(new ChannelInitializer<DatagramChannel>() {
                    @Override
                    protected void initChannel(DatagramChannel ch) {
                        ch.pipeline()
                                .addLast("rtp", new Jt1078UdpRtpPacketDecoder(maxPacketLength))
                                .addLast("archive", new Jt1078ReceivedMediaStreamHandler(archiveService, malformedPublisher));
                    }
                });
        this.serverChannel = b.bind(port).sync().channel();
        log.info("[jt1078] udp media stream server listening on :{} workers={} maxPacketLength={}",
                port, threads, maxPacketLength);
    }

    @Override
    public void destroy() {
        try {
            if (serverChannel != null) serverChannel.close().sync();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            if (workers != null) workers.shutdownGracefully();
            log.info("[jt1078] udp media stream server stopped");
        }
    }
}
