package net.md_5.bungee.netty;

import net.md_5.bungee.compress.PacketCompressor;
import net.md_5.bungee.compress.PacketDecompressor;
import net.md_5.bungee.protocol.PacketWrapper;
import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import lombok.Getter;
import net.md_5.bungee.protocol.MinecraftDecoder;
import net.md_5.bungee.protocol.MinecraftEncoder;
import net.md_5.bungee.protocol.Protocol;

public class ChannelWrapper
{

    private final Channel ch;
    private volatile boolean closed;

    public ChannelWrapper(ChannelHandlerContext ctx)
    {
        this.ch = ctx.channel();
    }

    public void setProtocol(Protocol protocol)
    {
        ch.pipeline().get( MinecraftDecoder.class ).setProtocol( protocol );
        ch.pipeline().get( MinecraftEncoder.class ).setProtocol( protocol );
    }

    public void setVersion(int protocol)
    {
        ch.pipeline().get( MinecraftDecoder.class ).setProtocolVersion( protocol );
        ch.pipeline().get( MinecraftEncoder.class ).setProtocolVersion( protocol );
    }

    public void write(Object packet)
    {
        if ( !isClosed() )
        {
            if ( packet instanceof PacketWrapper )
            {
                ( (PacketWrapper) packet ).setReleased( true );
                ch.writeAndFlush( ( (PacketWrapper) packet ).buf ).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
            } else
            {
                ch.writeAndFlush( packet ).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
            }
            ch.flush();
        }
    }

    public void close()
    {
        if ( !isClosed() )
        {
            closed = true;
            ch.flush();
            ch.close();
        }
    }

    /**
     * Send the given packet, then close the connection
     *
     * @param packet the packet to send before closing
     */
    public void close(Object packet) {
        if (!isClosed()) {
            closed = true;
            ch.writeAndFlush(packet).addListeners(
                    ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE,
                    ChannelFutureListener.CLOSE);
        }
    }

    public boolean isClosed() {
        return closed || !ch.isActive();
    }

    public void addBefore(String baseName, String name, ChannelHandler handler)
    {
        Preconditions.checkState( ch.eventLoop().inEventLoop(), "cannot add handler outside of event loop" );
        ch.pipeline().flush();
        ch.pipeline().addBefore( baseName, name, handler );
    }

    public Channel getHandle()
    {
        return ch;
    }

    public void setCompressionThreshold(int compressionThreshold)
    {
        if ( ch.pipeline().get( PacketCompressor.class ) == null && compressionThreshold != -1 )
        {
            addBefore( PipelineUtils.PACKET_ENCODER, "compress", new PacketCompressor() );
        }
        if ( compressionThreshold != -1 )
        {
            ch.pipeline().get( PacketCompressor.class ).setThreshold( compressionThreshold );
        } else
        {
            ch.pipeline().remove( "compress" );
        }

        if ( ch.pipeline().get( PacketDecompressor.class ) == null && compressionThreshold != -1 )
        {
            addBefore( PipelineUtils.PACKET_DECODER, "decompress", new PacketDecompressor() );
        }
        if ( compressionThreshold == -1 )
        {
            ch.pipeline().remove( "decompress" );
        }
    }
}

@@ -1462,70 +1462,27 @@
 .buf
- ).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE
+, ch.voidPromise() 
 );%0A 
@@ -1557,110 +1557,43 @@
 cket
- ).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);%0A            %7D%0A            ch.flush();
+, ch.voidPromise() );%0A            %7D
 %0A   
@@ -2042,37 +2042,16 @@
 steners(
-%0A                    
 ChannelF
@@ -2094,28 +2094,8 @@
 URE,
-%0A                   
  Cha

Use void promises like old BungeeCord except for closing with a kick packet