package com.lingniu.ingest.protocol.jsatl12.inbound;

import com.lingniu.ingest.api.pipeline.RawFrame;
import com.lingniu.ingest.identity.VehicleIdentityResolver;
import com.lingniu.ingest.protocol.jsatl12.codec.Jsatl12FrameDecoder;
import com.lingniu.ingest.sink.archive.ArchiveStore;
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;
import java.util.function.Consumer;

/**
 * 苏标报警附件 Netty 服务端。独立端口（默认 7612）。
 */
public final class Jsatl12NettyServer implements InitializingBean, DisposableBean {

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

    private final int port;
    private final int workerThreads;
    private final int maxFrameLength;
    private final ArchiveStore archive;
    private final Jsatl12JtMessageBridge jtMessageBridge;
    private final Jsatl12AttachmentTracker attachmentTracker;
    private final VehicleIdentityResolver identityResolver;
    private final Consumer<RawFrame> dataChunkDispatcher;

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

    public Jsatl12NettyServer(int port,
                              int workerThreads,
                              int maxFrameLength,
                              ArchiveStore archive,
                              Jsatl12JtMessageBridge jtMessageBridge,
                              Jsatl12AttachmentTracker attachmentTracker,
                              Consumer<RawFrame> dataChunkDispatcher) {
        this(port, workerThreads, maxFrameLength, archive, jtMessageBridge, attachmentTracker, null, dataChunkDispatcher);
    }

    public Jsatl12NettyServer(int port,
                              int workerThreads,
                              int maxFrameLength,
                              ArchiveStore archive,
                              Jsatl12JtMessageBridge jtMessageBridge,
                              Jsatl12AttachmentTracker attachmentTracker,
                              VehicleIdentityResolver identityResolver,
                              Consumer<RawFrame> dataChunkDispatcher) {
        this.port = port;
        this.workerThreads = workerThreads;
        this.maxFrameLength = maxFrameLength;
        this.archive = archive;
        this.jtMessageBridge = jtMessageBridge;
        this.attachmentTracker = attachmentTracker == null ? new Jsatl12AttachmentTracker() : attachmentTracker;
        this.identityResolver = identityResolver;
        this.dataChunkDispatcher = dataChunkDispatcher;
    }

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

        ServerBootstrap b = new ServerBootstrap();
        b.group(boss, workers)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 256)
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ch.pipeline()
                                .addLast("frame", new Jsatl12FrameDecoder(maxFrameLength))
                                .addLast("dispatch", new Jsatl12ChannelHandler(
                                        archive, jtMessageBridge, attachmentTracker, identityResolver, dataChunkDispatcher));
                    }
                });
        this.serverChannel = b.bind(port).sync().channel();
        log.info("jsatl12 Netty server listening on :{} workers={} maxFrameLength={}",
                port, threads, maxFrameLength);
    }

    @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("jsatl12 Netty server stopped");
        }
    }
}
