package com.twitter.whiskey.net;

import com.twitter.whiskey.nio.RunLoop;
import com.twitter.whiskey.util.Origin;

import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @author Michael Schore
 */
public class WhiskeyClient {
    final private ClientConfiguration configuration;
    final private HashMap<Origin, SessionManager> managers = new HashMap<>();
    final private ConcurrentHashMap<Origin, Origin> aliases = new ConcurrentHashMap<>();

    public WhiskeyClient() {
        this(new ClientConfiguration.Builder().create());
    }

    public WhiskeyClient(ClientConfiguration configuration) {

        this.configuration = configuration;
    }

    public ResponseFuture submit(Request request) {

        final RequestOperation operation = new RequestOperation(this, request);
        queue(operation);

        long timeout = request.getTimeout();
        if (timeout > 0) {
            // TODO: add cancellation of timeouts
            Runnable timeoutOperation = new Runnable() {
                @Override
                public void run() {
                    operation.fail(new TimeoutException("request timed out"));
                }
            };
            timeout = Math.max(1, TimeUnit.MILLISECONDS.convert(timeout, request.getTimeoutUnit()));
            RunLoop.instance().schedule(timeoutOperation, timeout, TimeUnit.MILLISECONDS);
        }

        return operation;
    }

    void queue(final RequestOperation operation) {

        Origin requestOrigin = new Origin(operation.getCurrentRequest().getUrl());
        Origin aliasedOrigin = aliases.get(requestOrigin);
        final Origin origin = aliasedOrigin != null ? aliasedOrigin : requestOrigin;

        RunLoop.instance().execute(new Runnable() {
            @Override
            public void run() {

                SessionManager manager = managers.get(origin);
                if (manager == null) {
                    manager = new SessionManager(origin, configuration);
                    managers.put(origin, manager);
                }

                manager.queue(operation);
            }
        });
        RunLoop.instance().startThread();
    }

    public void addAlias(Origin alias, Origin origin) {
        aliases.put(alias, origin);
    }

    // TODO: rename to shutdown and terminate similar to ExecutorService interface
    /**
     * Attempt to gracefully cancel all in-flight requests and close all open connections.
     */
    public void close() {

    }

    /**
     * Immediately terminate all connections and fail in-flight requests.
     */
    public void kill() {

    }
}

@@ -698,33 +698,32 @@
 onfiguration) %7B%0A
-%0A
         this.con
@@ -749,32 +749,326 @@
 uration;%0A    %7D%0A%0A
+    /**%0A     * Submits a request for execution. A new %7B@link RunLoop%7D will be started%0A     * if necessary, and new connections established where required.%0A     *%0A     * @param request the request to be executed%0A     * @return a ResponseFuture tracking progress of the submitted request%0A     */%0A
     public Respo
@@ -1796,24 +1796,228 @@
 ion;%0A    %7D%0A%0A
+    /**%0A     * Enqueues a %7B@link RequestOperation%7D on the appropriate %7B@link SessionManager%7D.%0A     *%0A     * May be called more than once on a single operation due to redirects and%0A     * retries.%0A     */%0A
     void que
@@ -2745,32 +2745,805 @@
 hread();%0A    %7D%0A%0A
+    /**%0A     * Adds an alias from one origin to another.%0A     *%0A     * Requests with a hostname matching the alias will instead be sent over%0A     * a connection to the target origin. Host headers will remain unchanged.%0A     * If the target origin contains a raw IP address, X.509 certificate%0A     * validation for a new connection will be performed using the hostname%0A     * of the alias. Otherwise the connection will use the properties of the%0A     * target origin for certificate validation.%0A     *%0A     * Note: this is an advanced feature allowing more than one domain to%0A     * be multiplexed over a single connection and/or DNS to be bypassed,%0A     * and has security ramifications that should be well understood before%0A     * use in a production application.%0A     */%0A
     public void 
@@ -3572,22 +3572,22 @@
  Origin 
-origin
+target
 ) %7B%0A    
@@ -3613,106 +3613,641 @@
 as, 
-origin);%0A    %7D%0A%0A    // TODO: rename to
+target);%0A    %7D%0A%0A    /**%0A     * Removes an alias to another origin.%0A     */%0A    public void removeAlias(Origin alias) %7B%0A        aliases.remove(alias);%0A    %7D%0A%0A    /**%0A     * @return true if this client has been
  shut
+ 
 down
- and terminate similar to ExecutorService interface
+.%0A     */%0A    public boolean isShutdown() %7B%0A        return false;%0A    %7D%0A%0A    /**%0A     * @return true if all outstanding requests are completed and connections have%0A     *         closed following shutdown.%0A     */%0A    public boolean isTerminated() %7B%0A        return false;%0A    %7D%0A%0A%0A    /**%0A     * Attempt to complete all submitted requests, but stop accepting new requests.%0A     */%0A    public void shutdown() %7B%0A    %7D%0A
 %0A   
@@ -4370,19 +4370,24 @@
 oid 
-close
+shutdownNow
 () %7B%0A
-%0A
     
@@ -4499,18 +4499,22 @@
 oid 
-kill
+terminate
 () %7B%0A
-%0A
     

Document WhiskeyClient