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

package io.netty.channel.socket.oio;

import io.netty.util.internal.logging.InternalLoggerFactory;
import io.netty.channel.ChannelConfig;
import java.net.NetworkInterface;
import java.io.IOException;
import io.netty.channel.ChannelPromise;
import io.netty.channel.ChannelFuture;
import io.netty.util.internal.StringUtil;
import java.nio.channels.UnresolvedAddressException;
import io.netty.buffer.ByteBufUtil;
import java.nio.channels.NotYetConnectedException;
import io.netty.channel.AddressedEnvelope;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.RecvByteBufAllocator;
import io.netty.util.internal.PlatformDependent;
import java.util.Locale;
import java.net.SocketTimeoutException;
import java.net.InetAddress;
import java.util.List;
import java.net.InetSocketAddress;
import io.netty.channel.ChannelOption;
import io.netty.channel.socket.DatagramChannelConfig;
import java.net.DatagramSocket;
import java.net.SocketException;
import io.netty.util.internal.EmptyArrays;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import java.net.SocketAddress;
import java.net.DatagramPacket;
import java.net.MulticastSocket;
import io.netty.channel.ChannelMetadata;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.oio.AbstractOioMessageChannel;

@Deprecated
public class OioDatagramChannel extends AbstractOioMessageChannel implements DatagramChannel
{
    private static final InternalLogger logger;
    private static final ChannelMetadata METADATA;
    private static final String EXPECTED_TYPES;
    private final MulticastSocket socket;
    private final OioDatagramChannelConfig config;
    private final DatagramPacket tmpPacket;
    
    private static MulticastSocket newSocket() {
        try {
            return new MulticastSocket((SocketAddress)null);
        }
        catch (final Exception e) {
            throw new ChannelException("failed to create a new socket", e);
        }
    }
    
    public OioDatagramChannel() {
        this(newSocket());
    }
    
    public OioDatagramChannel(final MulticastSocket socket) {
        super(null);
        this.tmpPacket = new DatagramPacket(EmptyArrays.EMPTY_BYTES, 0);
        boolean success = false;
        try {
            socket.setSoTimeout(1000);
            socket.setBroadcast(false);
            success = true;
        }
        catch (final SocketException e) {
            throw new ChannelException("Failed to configure the datagram socket timeout.", e);
        }
        finally {
            if (!success) {
                socket.close();
            }
        }
        this.socket = socket;
        this.config = new DefaultOioDatagramChannelConfig(this, socket);
    }
    
    @Override
    public ChannelMetadata metadata() {
        return OioDatagramChannel.METADATA;
    }
    
    @Override
    public DatagramChannelConfig config() {
        return this.config;
    }
    
    @Override
    public boolean isOpen() {
        return !this.socket.isClosed();
    }
    
    @Override
    public boolean isActive() {
        return this.isOpen() && ((this.config.getOption(ChannelOption.DATAGRAM_CHANNEL_ACTIVE_ON_REGISTRATION) && this.isRegistered()) || this.socket.isBound());
    }
    
    @Override
    public boolean isConnected() {
        return this.socket.isConnected();
    }
    
    @Override
    protected SocketAddress localAddress0() {
        return this.socket.getLocalSocketAddress();
    }
    
    @Override
    protected SocketAddress remoteAddress0() {
        return this.socket.getRemoteSocketAddress();
    }
    
    @Override
    protected void doBind(final SocketAddress localAddress) throws Exception {
        this.socket.bind(localAddress);
    }
    
    @Override
    public InetSocketAddress localAddress() {
        return (InetSocketAddress)super.localAddress();
    }
    
    @Override
    public InetSocketAddress remoteAddress() {
        return (InetSocketAddress)super.remoteAddress();
    }
    
    @Override
    protected void doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) throws Exception {
        if (localAddress != null) {
            this.socket.bind(localAddress);
        }
        boolean success = false;
        try {
            this.socket.connect(remoteAddress);
            success = true;
        }
        finally {
            if (!success) {
                try {
                    this.socket.close();
                }
                catch (final Throwable t) {
                    OioDatagramChannel.logger.warn("Failed to close a socket.", t);
                }
            }
        }
    }
    
    @Override
    protected void doDisconnect() throws Exception {
        this.socket.disconnect();
    }
    
    @Override
    protected void doClose() throws Exception {
        this.socket.close();
    }
    
    @Override
    protected int doReadMessages(final List<Object> buf) throws Exception {
        final DatagramChannelConfig config = this.config();
        final RecvByteBufAllocator.Handle allocHandle = this.unsafe().recvBufAllocHandle();
        final ByteBuf data = config.getAllocator().heapBuffer(allocHandle.guess());
        boolean free = true;
        try {
            this.tmpPacket.setAddress(null);
            this.tmpPacket.setData(data.array(), data.arrayOffset(), data.capacity());
            this.socket.receive(this.tmpPacket);
            final InetSocketAddress remoteAddr = (InetSocketAddress)this.tmpPacket.getSocketAddress();
            allocHandle.lastBytesRead(this.tmpPacket.getLength());
            buf.add(new io.netty.channel.socket.DatagramPacket(data.writerIndex(allocHandle.lastBytesRead()), this.localAddress(), remoteAddr));
            free = false;
            return 1;
        }
        catch (final SocketTimeoutException e) {
            return 0;
        }
        catch (final SocketException e2) {
            if (!e2.getMessage().toLowerCase(Locale.US).contains("socket closed")) {
                throw e2;
            }
            return -1;
        }
        catch (final Throwable cause) {
            PlatformDependent.throwException(cause);
            return -1;
        }
        finally {
            if (free) {
                data.release();
            }
        }
    }
    
    @Override
    protected void doWrite(final ChannelOutboundBuffer in) throws Exception {
        while (true) {
            final Object o = in.current();
            if (o == null) {
                break;
            }
            SocketAddress remoteAddress;
            ByteBuf data;
            if (o instanceof AddressedEnvelope) {
                final AddressedEnvelope<ByteBuf, SocketAddress> envelope = (AddressedEnvelope<ByteBuf, SocketAddress>)o;
                remoteAddress = envelope.recipient();
                data = envelope.content();
            }
            else {
                data = (ByteBuf)o;
                remoteAddress = null;
            }
            final int length = data.readableBytes();
            try {
                if (remoteAddress != null) {
                    this.tmpPacket.setSocketAddress(remoteAddress);
                }
                else {
                    if (!this.isConnected()) {
                        throw new NotYetConnectedException();
                    }
                    this.tmpPacket.setAddress(null);
                }
                if (data.hasArray()) {
                    this.tmpPacket.setData(data.array(), data.arrayOffset() + data.readerIndex(), length);
                }
                else {
                    this.tmpPacket.setData(ByteBufUtil.getBytes(data, data.readerIndex(), length));
                }
                this.socket.send(this.tmpPacket);
                in.remove();
            }
            catch (final Exception e) {
                in.remove(e);
            }
        }
    }
    
    private static void checkUnresolved(final AddressedEnvelope<?, ?> envelope) {
        if (envelope.recipient() instanceof InetSocketAddress && ((InetSocketAddress)envelope.recipient()).isUnresolved()) {
            throw new UnresolvedAddressException();
        }
    }
    
    @Override
    protected Object filterOutboundMessage(final Object msg) {
        if (msg instanceof io.netty.channel.socket.DatagramPacket) {
            checkUnresolved((AddressedEnvelope<?, ?>)msg);
            return msg;
        }
        if (msg instanceof ByteBuf) {
            return msg;
        }
        if (msg instanceof AddressedEnvelope) {
            final AddressedEnvelope<Object, SocketAddress> e = (AddressedEnvelope<Object, SocketAddress>)msg;
            checkUnresolved(e);
            if (e.content() instanceof ByteBuf) {
                return msg;
            }
        }
        throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg) + OioDatagramChannel.EXPECTED_TYPES);
    }
    
    @Override
    public ChannelFuture joinGroup(final InetAddress multicastAddress) {
        return this.joinGroup(multicastAddress, this.newPromise());
    }
    
    @Override
    public ChannelFuture joinGroup(final InetAddress multicastAddress, final ChannelPromise promise) {
        this.ensureBound();
        try {
            this.socket.joinGroup(multicastAddress);
            promise.setSuccess();
        }
        catch (final IOException e) {
            promise.setFailure((Throwable)e);
        }
        return promise;
    }
    
    @Override
    public ChannelFuture joinGroup(final InetSocketAddress multicastAddress, final NetworkInterface networkInterface) {
        return this.joinGroup(multicastAddress, networkInterface, this.newPromise());
    }
    
    @Override
    public ChannelFuture joinGroup(final InetSocketAddress multicastAddress, final NetworkInterface networkInterface, final ChannelPromise promise) {
        this.ensureBound();
        try {
            this.socket.joinGroup(multicastAddress, networkInterface);
            promise.setSuccess();
        }
        catch (final IOException e) {
            promise.setFailure((Throwable)e);
        }
        return promise;
    }
    
    @Override
    public ChannelFuture joinGroup(final InetAddress multicastAddress, final NetworkInterface networkInterface, final InetAddress source) {
        return this.newFailedFuture(new UnsupportedOperationException());
    }
    
    @Override
    public ChannelFuture joinGroup(final InetAddress multicastAddress, final NetworkInterface networkInterface, final InetAddress source, final ChannelPromise promise) {
        promise.setFailure((Throwable)new UnsupportedOperationException());
        return promise;
    }
    
    private void ensureBound() {
        if (!this.isActive()) {
            throw new IllegalStateException(DatagramChannel.class.getName() + " must be bound to join a group.");
        }
    }
    
    @Override
    public ChannelFuture leaveGroup(final InetAddress multicastAddress) {
        return this.leaveGroup(multicastAddress, this.newPromise());
    }
    
    @Override
    public ChannelFuture leaveGroup(final InetAddress multicastAddress, final ChannelPromise promise) {
        try {
            this.socket.leaveGroup(multicastAddress);
            promise.setSuccess();
        }
        catch (final IOException e) {
            promise.setFailure((Throwable)e);
        }
        return promise;
    }
    
    @Override
    public ChannelFuture leaveGroup(final InetSocketAddress multicastAddress, final NetworkInterface networkInterface) {
        return this.leaveGroup(multicastAddress, networkInterface, this.newPromise());
    }
    
    @Override
    public ChannelFuture leaveGroup(final InetSocketAddress multicastAddress, final NetworkInterface networkInterface, final ChannelPromise promise) {
        try {
            this.socket.leaveGroup(multicastAddress, networkInterface);
            promise.setSuccess();
        }
        catch (final IOException e) {
            promise.setFailure((Throwable)e);
        }
        return promise;
    }
    
    @Override
    public ChannelFuture leaveGroup(final InetAddress multicastAddress, final NetworkInterface networkInterface, final InetAddress source) {
        return this.newFailedFuture(new UnsupportedOperationException());
    }
    
    @Override
    public ChannelFuture leaveGroup(final InetAddress multicastAddress, final NetworkInterface networkInterface, final InetAddress source, final ChannelPromise promise) {
        promise.setFailure((Throwable)new UnsupportedOperationException());
        return promise;
    }
    
    @Override
    public ChannelFuture block(final InetAddress multicastAddress, final NetworkInterface networkInterface, final InetAddress sourceToBlock) {
        return this.newFailedFuture(new UnsupportedOperationException());
    }
    
    @Override
    public ChannelFuture block(final InetAddress multicastAddress, final NetworkInterface networkInterface, final InetAddress sourceToBlock, final ChannelPromise promise) {
        promise.setFailure((Throwable)new UnsupportedOperationException());
        return promise;
    }
    
    @Override
    public ChannelFuture block(final InetAddress multicastAddress, final InetAddress sourceToBlock) {
        return this.newFailedFuture(new UnsupportedOperationException());
    }
    
    @Override
    public ChannelFuture block(final InetAddress multicastAddress, final InetAddress sourceToBlock, final ChannelPromise promise) {
        promise.setFailure((Throwable)new UnsupportedOperationException());
        return promise;
    }
    
    static {
        logger = InternalLoggerFactory.getInstance(OioDatagramChannel.class);
        METADATA = new ChannelMetadata(true);
        EXPECTED_TYPES = " (expected: " + StringUtil.simpleClassName(io.netty.channel.socket.DatagramPacket.class) + ", " + StringUtil.simpleClassName(AddressedEnvelope.class) + '<' + StringUtil.simpleClassName(ByteBuf.class) + ", " + StringUtil.simpleClassName(SocketAddress.class) + ">, " + StringUtil.simpleClassName(ByteBuf.class) + ')';
    }
}
