package com.jscape.inet.proxy;

import com.jscape.util.net.ConnectionDescriptor;
import com.jscape.util.net.TransportAddress;
import com.jscape.util.net.connection.Connector;
import com.jscape.util.net.connection.SyncRawConnection;
import com.jscape.util.net.socket.SocketConnector;
import com.jscape.util.net.socket.SocketParameters;
import com.jscape.util.net.socket.SocketRawConnection;

import static com.jscape.util.Parameters.assertNotNull;

/**
 * @author Alex Usun
 */
public class ProxySocketConnector
        implements Connector<SyncRawConnection> {

    private final ProxyDescriptor proxyDescriptor;
    private final Proxy proxy;
    private final SocketConnector base;
    private final ConnectionDescriptor descriptor;

    public ProxySocketConnector(ProxyDescriptor proxyDescriptor, Proxy proxy, ConnectionDescriptor descriptor, SocketParameters socketParameters) {
        assertNotNull(proxyDescriptor);
        this.proxyDescriptor = proxyDescriptor;

        assertNotNull(proxy);
        this.proxy = proxy;

        assertNotNull(descriptor);
        this.descriptor = descriptor;

        this.base = new SocketConnector(new ConnectionDescriptor(descriptor.getLocalAddress(),
                                                                 new TransportAddress(this.proxyDescriptor.getHost(), this.proxyDescriptor.getPort()),
                                                                 descriptor.getTimeout()),
                                        socketParameters);
    }

    public TransportAddress remoteAddress() {
        return this.descriptor.getRemoteAddress();
    }

    @Override
    public SyncRawConnection connect()
            throws OperationException {
        SocketRawConnection connection = (SocketRawConnection) this.base.connect();

        try {
            this.proxy.openConnection(connection, this.descriptor.getRemoteAddress(), this.proxyDescriptor.getUsername(), this.proxyDescriptor.getPassword());
            connection.remoteAddress(this.descriptor.getRemoteAddress());

            return connection;
        } catch (Exception e) {
            connection.close();
            throw new OperationException(e);
        }
    }
}
