package org.chenyang.http.impl.conn;

import org.chenyang.http.HttpException;
import org.chenyang.http.HttpHost;
import org.chenyang.http.HttpRequest;
import org.chenyang.http.conn.params.ConnRouteParams;
import org.chenyang.http.conn.routing.HttpRoute;
import org.chenyang.http.conn.routing.HttpRoutePlanner;
import org.chenyang.http.conn.scheme.Scheme;
import org.chenyang.http.conn.scheme.SchemeRegistry;
import org.chenyang.http.protocol.HttpContext;
import org.chenyang.http.util.Args;
import org.chenyang.http.util.Asserts;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.Proxy.Type;
import java.util.List;

/**
 * @deprecated
 */
@Deprecated
public class ProxySelectorRoutePlanner implements HttpRoutePlanner {
    protected final SchemeRegistry schemeRegistry;
    protected ProxySelector proxySelector;

    public ProxySelectorRoutePlanner(SchemeRegistry schreg, ProxySelector prosel) {
        Args.notNull(schreg, "SchemeRegistry");
        this.schemeRegistry = schreg;
        this.proxySelector = prosel;
    }

    public ProxySelector getProxySelector() {
        return this.proxySelector;
    }

    public void setProxySelector(ProxySelector prosel) {
        this.proxySelector = prosel;
    }

    public HttpRoute determineRoute(HttpHost target, HttpRequest request, HttpContext context) throws HttpException {
        Args.notNull(request, "HTTP request");
        HttpRoute route = ConnRouteParams.getForcedRoute(request.getParams());
        if (route != null) {
            return route;
        } else {
            Asserts.notNull(target, "Target host");
            InetAddress local = ConnRouteParams.getLocalAddress(request.getParams());
            HttpHost proxy = this.determineProxy(target, request, context);
            Scheme schm = this.schemeRegistry.getScheme(target.getSchemeName());
            boolean secure = schm.isLayered();
            if (proxy == null) {
                route = new HttpRoute(target, local, secure);
            } else {
                route = new HttpRoute(target, local, proxy, secure);
            }

            return route;
        }
    }

    protected HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context) throws HttpException {
        ProxySelector psel = this.proxySelector;
        if (psel == null) {
            psel = ProxySelector.getDefault();
        }

        if (psel == null) {
            return null;
        } else {
            URI targetURI = null;

            try {
                targetURI = new URI(target.toURI());
            } catch (URISyntaxException usx) {
                throw new HttpException("Cannot convert host to URI: " + target, usx);
            }

            List<Proxy> proxies = psel.select(targetURI);
            Proxy p = this.chooseProxy(proxies, target, request, context);
            HttpHost result = null;
            if (p.type() == Type.HTTP) {
                if (!(p.address() instanceof InetSocketAddress)) {
                    throw new HttpException("Unable to handle non-Inet proxy address: " + p.address());
                }

                InetSocketAddress isa = (InetSocketAddress) p.address();
                result = new HttpHost(this.getHost(isa), isa.getPort());
            }

            return result;
        }
    }

    protected String getHost(InetSocketAddress isa) {
        return isa.isUnresolved() ? isa.getHostName() : isa.getAddress().getHostAddress();
    }

    protected Proxy chooseProxy(List<Proxy> proxies,
                                HttpHost target,
                                HttpRequest request,
                                HttpContext context) {
        if ((proxies == null) || proxies.isEmpty()) {
            throw new IllegalArgumentException
                    ("Proxy list must not be empty.");
        }
        Proxy result = null;
        // check the list for one we can use
        for (int i = 0; (result == null) && (i < proxies.size()); i++) {
            Proxy p = proxies.get(i);
            switch (p.type()) {
                case DIRECT:
                case HTTP:
                    result = p;
                    break;
                case SOCKS:
                    // SOCKS hosts are not handled on the route level.
                    // The socket may make use of the SOCKS host though.
                    break;
            }
        }
        if (result == null) {
            //@@@ log as warning or info that only a socks proxy is available?
            // result can only be null if all proxies are socks proxies
            // socks proxies are not handled on the route planning level
            result = Proxy.NO_PROXY;
        }
        return result;
    }
}
