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

import io.netty.bootstrap.ServerBootstrap;
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.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
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 Jt1078MediaStreamServer implements InitializingBean, DisposableBean {

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

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

    private EventLoopGroup boss;
    private EventLoopGroup workers;
    private Channel serverChannel;

    public Jt1078MediaStreamServer(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 bossTf = r -> new Thread(r, "jt1078-media-boss");
        ThreadFactory wkTf = r -> new Thread(r, "jt1078-media-worker");
        this.boss = new NioEventLoopGroup(1, bossTf);
        this.workers = new NioEventLoopGroup(
                workerThreads == 0 ? Runtime.getRuntime().availableProcessors() : workerThreads,
                wkTf);

        ServerBootstrap b = new ServerBootstrap();
        b.group(boss, workers)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ch.pipeline()
                                .addLast("rtp", new Jt1078RtpPacketDecoder(maxPacketLength))
                                .addLast("archive", new Jt1078MediaStreamHandler(archiveService, malformedPublisher));
                    }
                });
        this.serverChannel = b.bind(port).sync().channel();
        log.info("[jt1078] media stream server listening on :{}", port);
    }

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