package net.i2p.client.streaming.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.SocketChannel;

import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.client.streaming.I2PSocketOptions;

/**
 * Bridge to I2PSocket.
 *
 * This extends Socket to make porting apps easier.
 * Methods throw IOExceptions like Sockets do, rather than returning
 * null for some methods.
 *
 * StandardSockets are always bound, and always start out connected
 * (unless connectDelay is &gt; 0).
 * You may not create an unbound StandardSocket.
 * Create this through the SocketManager.
 *
 * Todo: Make public and add getPeerDestination() ?
 *
 * @author zzz
 * @since 0.8.4
 */
class StandardSocket extends Socket {
    private final I2PSocket _socket;

    StandardSocket(I2PSocket socket) {
        _socket = socket;
    }

    /**
     *  @throws UnsupportedOperationException always
     */
    @Override
    public void bind(SocketAddress bindpoint) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void close() throws IOException {
        if (_socket.isClosed())
            throw new IOException("Already closed");
        _socket.close();
    }

    /**
     *  @throws UnsupportedOperationException always
     */
    @Override
    public void connect(SocketAddress endpoint) {
        throw new UnsupportedOperationException();
    }

    /**
     *  @throws UnsupportedOperationException always
     */
    @Override
    public void connect(SocketAddress endpoint, int timeout) {
        throw new UnsupportedOperationException();
    }

    /**
     *  @return null always, unimplemented
     */
    @Override
    public SocketChannel getChannel() {
        //return _socket.getChannel();
        return null;
    }

    /**
     *  @return null always
     */
    @Override
    public InetAddress getInetAddress() {
        return null;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        InputStream rv = _socket.getInputStream();
        if (rv != null)
            return rv;
        throw new IOException("No stream");
    }

    @Override
    public boolean getKeepAlive() {
        ConnectionOptions opts = (ConnectionOptions) _socket.getOptions();
        if (opts == null)
            return false;
        return opts.getInactivityAction() == ConnectionOptions.INACTIVITY_ACTION_SEND;
    }

    /**
     *  @return null always
     */
    @Override
    public InetAddress getLocalAddress() {
        return null;
    }

    /**
     *  @return the port or 0 if unknown
     */
    @Override
    public int getLocalPort() {
        return _socket.getLocalPort();
    }

    /**
     *  @return an I2PSocketAddress as of 0.9.26; prior to that, returned null
     *  @since implemented in 0.9.26
     */
    @Override
    public SocketAddress getLocalSocketAddress() {
        return new I2PSocketAddress(_socket.getThisDestination(), _socket.getLocalPort());
    }

    /**
     *  @return false always
     */
    @Override
    public boolean getOOBInline() {
        return false;
    }

    @Override
    public OutputStream getOutputStream() throws IOException {
        OutputStream rv = _socket.getOutputStream();
        if (rv != null)
            return rv;
        throw new IOException("No stream");
    }

    /**
     *  @return the port or 0 if unknown
     */
    @Override
    public int getPort() {
        return _socket.getPort();
    }

    @Override
    public int getReceiveBufferSize() {
        ConnectionOptions opts = (ConnectionOptions) _socket.getOptions();
        if (opts == null)
            return 64*1024;
        return opts.getInboundBufferSize();
    }

    /**
     *  @return an I2PSocketAddress as of 0.9.26; prior to that, threw UnsupportedOperationException
     *  @since implemented in 0.9.26
     */
    @Override
    public SocketAddress getRemoteSocketAddress() {
        return new I2PSocketAddress(_socket.getPeerDestination(), _socket.getPort());
    }

    /**
     *  @return false always
     */
    @Override
    public boolean getReuseAddress() {
        return false;
    }

    @Override
    public int getSendBufferSize() {
        ConnectionOptions opts = (ConnectionOptions) _socket.getOptions();
        if (opts == null)
            return 64*1024;
        return opts.getInboundBufferSize();
    }

    @Override
    public int getSoLinger() {
        I2PSocketOptions opts = _socket.getOptions();
        if (opts == null)
            return -1;
        return -1;  // fixme really?
    }

    @Override
    public int getSoTimeout() {
        I2PSocketOptions opts = _socket.getOptions();
        if (opts == null)
            return 0;
        long rv = opts.getReadTimeout();
        // Java Socket: 0 is forever, and we don't exactly have nonblocking
        if (rv > Integer.MAX_VALUE)
            rv = Integer.MAX_VALUE;
        else if (rv < 0)
            rv = 0;
        else if (rv == 0)
            rv = 1;
        return (int) rv;
    }

    /**
     *  @return false always
     */
    @Override
    public boolean getTcpNoDelay() {
        // No option yet. See ConnectionDataReceiver
        return false;
    }

    /**
     *  @return 0 always
     */
    @Override
    public int getTrafficClass() {
        return 0;
    }

    /**
     *  @return true always
     */
    @Override
    public boolean isBound() {
        return true;
    }

    @Override
    public boolean isClosed() {
        return _socket.isClosed();
    }

    @Override
    public boolean isConnected() {
        return !_socket.isClosed();
    }

    @Override
    public boolean isInputShutdown() {
        return _socket.isClosed();
    }

    @Override
    public boolean isOutputShutdown() {
        return _socket.isClosed();
    }

    /**
     *  @throws UnsupportedOperationException always
     */
    @Override
    public void sendUrgentData(int data) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setKeepAlive(boolean on) {
        ConnectionOptions opts = (ConnectionOptions) _socket.getOptions();
        if (opts == null)
            return;
        if (on)
            opts.setInactivityAction(ConnectionOptions.INACTIVITY_ACTION_SEND);
        else
            opts.setInactivityAction(ConnectionOptions.INACTIVITY_ACTION_NOOP);  // DISCONNECT?
    }

    /**
     *  @throws UnsupportedOperationException if on is true
     */
    @Override
    public void setOOBInline(boolean on) {
        if (on)
            throw new UnsupportedOperationException();
    }

    /**
     *  Does nothing.
     */
    @Override
    public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
    }

    /**
     *  Does nothing.
     */
    @Override
    public void setReceiveBufferSize(int size) {
    }

    /**
     *  Does nothing.
     */
    @Override
    public void setReuseAddress(boolean on) {
    }

    /**
     *  Does nothing.
     */
    @Override
    public void setSendBufferSize(int size) {
    }

    /**
     *  Does nothing.
     */
    @Override
    public void setSoLinger(boolean on, int linger) {
    }

    @Override
    public void setSoTimeout(int timeout) throws SocketException {
        I2PSocketOptions opts = _socket.getOptions();
        if (opts == null)
            throw new SocketException("No options");
        // Java Socket: 0 is forever
        if (timeout == 0)
            timeout = -1;
        opts.setReadTimeout(timeout);
    }

    /**
     *  Does nothing.
     */
    @Override
    public void setTcpNoDelay(boolean on) {
    }

    /**
     *  Does nothing.
     */
    @Override
    public void setTrafficClass(int tc) {
    }

    @Override
    public void shutdownInput() throws IOException {
        close();
    }

    @Override
    public void shutdownOutput() throws IOException {
        close();
    }

    @Override
    public String toString() {
        return _socket.toString();
    }
}
