// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.channel.local;

import io.netty.util.concurrent.SingleThreadEventExecutor;
import io.netty.channel.IoEvent;
import io.netty.util.concurrent.Future;
import io.netty.channel.AbstractChannel;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.IoEventLoop;
import io.netty.channel.ChannelPromise;
import java.net.SocketAddress;
import io.netty.channel.IoHandle;
import io.netty.channel.IoEventLoopGroup;
import io.netty.channel.SingleThreadEventLoop;
import io.netty.channel.EventLoop;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.PreferHeapByteBufAllocator;
import java.util.ArrayDeque;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.ServerChannelRecvByteBufAllocator;
import io.netty.channel.IoRegistration;
import java.util.Queue;
import io.netty.channel.ChannelConfig;
import io.netty.channel.AbstractServerChannel;

public class LocalServerChannel extends AbstractServerChannel
{
    private final ChannelConfig config;
    private final Queue<Object> inboundBuffer;
    private final Runnable shutdownHook;
    private IoRegistration registration;
    private volatile int state;
    private volatile LocalAddress localAddress;
    private volatile boolean acceptInProgress;
    
    public LocalServerChannel() {
        this.config = new DefaultChannelConfig((Channel)this, (RecvByteBufAllocator)new ServerChannelRecvByteBufAllocator()) {};
        this.inboundBuffer = new ArrayDeque<Object>();
        this.shutdownHook = new Runnable() {
            @Override
            public void run() {
                LocalServerChannel.this.unsafe().close(LocalServerChannel.this.unsafe().voidPromise());
            }
        };
        this.config().setAllocator(new PreferHeapByteBufAllocator(this.config.getAllocator()));
    }
    
    @Override
    public ChannelConfig config() {
        return this.config;
    }
    
    @Override
    public LocalAddress localAddress() {
        return (LocalAddress)super.localAddress();
    }
    
    @Override
    public LocalAddress remoteAddress() {
        return (LocalAddress)super.remoteAddress();
    }
    
    @Override
    public boolean isOpen() {
        return this.state < 2;
    }
    
    @Override
    public boolean isActive() {
        return this.state == 1;
    }
    
    @Override
    protected boolean isCompatible(final EventLoop loop) {
        return loop instanceof SingleThreadEventLoop || (loop instanceof IoEventLoopGroup && ((IoEventLoopGroup)loop).isCompatible(LocalServerUnsafe.class));
    }
    
    @Override
    protected SocketAddress localAddress0() {
        return this.localAddress;
    }
    
    @Override
    protected void doRegister(final ChannelPromise promise) {
        final EventLoop loop = this.eventLoop();
        if (loop instanceof IoEventLoop) {
            assert this.registration == null;
            ((IoEventLoop)loop).register((IoHandle)this.unsafe()).addListener(f -> {
                if (f.isSuccess()) {
                    this.registration = f.getNow();
                    promise.setSuccess();
                }
                else {
                    promise.setFailure(f.cause());
                }
            });
        }
        else {
            try {
                ((LocalServerUnsafe)this.unsafe()).registered();
            }
            catch (final Throwable cause) {
                promise.setFailure(cause);
                return;
            }
            promise.setSuccess();
        }
    }
    
    @Override
    protected void doBind(final SocketAddress localAddress) throws Exception {
        this.localAddress = LocalChannelRegistry.register(this, this.localAddress, localAddress);
        this.state = 1;
    }
    
    @Override
    protected void doClose() throws Exception {
        if (this.state <= 1) {
            if (this.localAddress != null) {
                LocalChannelRegistry.unregister(this.localAddress);
                this.localAddress = null;
            }
            this.state = 2;
        }
    }
    
    @Override
    protected void doDeregister() throws Exception {
        final EventLoop loop = this.eventLoop();
        if (loop instanceof IoEventLoop) {
            final IoRegistration registration = this.registration;
            if (registration != null) {
                this.registration = null;
                registration.cancel();
            }
        }
        else {
            ((LocalServerUnsafe)this.unsafe()).unregistered();
        }
    }
    
    @Override
    protected void doBeginRead() throws Exception {
        if (this.acceptInProgress) {
            return;
        }
        final Queue<Object> inboundBuffer = this.inboundBuffer;
        if (inboundBuffer.isEmpty()) {
            this.acceptInProgress = true;
            return;
        }
        this.readInbound();
    }
    
    LocalChannel serve(final LocalChannel peer) {
        final LocalChannel child = this.newLocalChannel(peer);
        if (this.eventLoop().inEventLoop()) {
            this.serve0(child);
        }
        else {
            this.eventLoop().execute(new Runnable() {
                @Override
                public void run() {
                    LocalServerChannel.this.serve0(child);
                }
            });
        }
        return child;
    }
    
    private void readInbound() {
        final RecvByteBufAllocator.Handle handle = this.unsafe().recvBufAllocHandle();
        handle.reset(this.config());
        final ChannelPipeline pipeline = this.pipeline();
        do {
            final Object m = this.inboundBuffer.poll();
            if (m == null) {
                break;
            }
            pipeline.fireChannelRead(m);
        } while (handle.continueReading());
        handle.readComplete();
        pipeline.fireChannelReadComplete();
    }
    
    protected LocalChannel newLocalChannel(final LocalChannel peer) {
        return new LocalChannel(this, peer);
    }
    
    private void serve0(final LocalChannel child) {
        this.inboundBuffer.add(child);
        if (this.acceptInProgress) {
            this.acceptInProgress = false;
            this.readInbound();
        }
    }
    
    @Override
    protected AbstractUnsafe newUnsafe() {
        return new LocalServerUnsafe();
    }
    
    private class LocalServerUnsafe extends AbstractUnsafe implements LocalIoHandle
    {
        @Override
        public void close() {
            this.close(this.voidPromise());
        }
        
        @Override
        public void handle(final IoRegistration registration, final IoEvent event) {
        }
        
        @Override
        public void connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
            this.safeSetFailure(promise, new UnsupportedOperationException());
        }
        
        @Override
        public void registered() {
            ((SingleThreadEventExecutor)LocalServerChannel.this.eventLoop()).addShutdownHook(LocalServerChannel.this.shutdownHook);
        }
        
        @Override
        public void unregistered() {
            ((SingleThreadEventExecutor)LocalServerChannel.this.eventLoop()).removeShutdownHook(LocalServerChannel.this.shutdownHook);
        }
        
        @Override
        public void closeNow() {
            this.close(this.voidPromise());
        }
    }
}
