package org.nl.config.lucene.remote.impl;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.Future;
import org.nl.config.lucene.remote.AbstraceServer;
import org.nl.config.lucene.remote.coder.LogProviderHandler;

import java.net.SocketAddress;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

/*
 * @author ZZQ
 * @Date 2024/1/22 10:01
 */
public class RemoteServer extends AbstraceServer {

    private static EventLoopGroup boss  = new NioEventLoopGroup();
    private static EventLoopGroup worker  = new NioEventLoopGroup();

    public RemoteServer(SocketAddress address) {
        super(address);
    }

    // 非阻塞IO线程组
    @Override
    public void doOpen() {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap
                .group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                .childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline()
                                .addLast("client-idle-handler", new IdleStateHandler(500, 0, 0, MILLISECONDS))
                                .addLast(new LengthFieldBasedFrameDecoder(8089, 0, 2, 0, 2))
                                .addLast(new LengthFieldPrepender(2))
                                .addLast("encode",new StringDecoder())
                                .addLast(new LogProviderHandler());
                    }
                });
        server = bootstrap;
    }

    @Override
    public void doDestroy(){
        Future<?> bossGroupShutdownFuture = boss.shutdownGracefully();
        Future<?> workerGroupShutdownFuture = worker.shutdownGracefully();
        bossGroupShutdownFuture.syncUninterruptibly();
        workerGroupShutdownFuture.syncUninterruptibly();
    }

    @Override
    public void doConnect() {
        ChannelFuture future = server.bind(address);
        boolean ret = future.awaitUninterruptibly(3000, MILLISECONDS);
        if (ret && future.isSuccess()) {
            Channel newChannel = future.channel();
            if (channel != null) {
                channel.close();
                channel = newChannel;
            }
        } else if (future.cause() != null) {
            Throwable cause = future.cause();
            cause.printStackTrace();
        }
    }

}
