package com.proofpoint.http.client;

import com.google.common.annotations.Beta;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.Binder;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Scopes;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.proofpoint.configuration.ConfigurationModule.bindConfig;
import static java.util.concurrent.Executors.newFixedThreadPool;

@Beta
public class AsyncHttpClientModule implements Module
{
    private final String name;
    private final Class<? extends Annotation> annotation;
    private final List<Class<? extends Annotation>> aliases;

    public AsyncHttpClientModule(Class<? extends Annotation> annotation, Class<? extends Annotation>... aliases)
    {
        this(checkNotNull(annotation, "annotation is null").getSimpleName(), annotation, aliases);
    }

    public AsyncHttpClientModule(String name, Class<? extends Annotation> annotation, Class<? extends Annotation>... aliases)
    {
        checkNotNull(name, "name is null");
        checkNotNull(annotation, "annotation is null");
        this.annotation = annotation;
        this.name = name;

        if (aliases != null) {
            this.aliases = ImmutableList.copyOf(aliases);
        }
        else {
            this.aliases = Collections.emptyList();
        }
    }

    @Override
    public void configure(Binder binder)
    {
        // bind the configuration
        bindConfig(binder).annotatedWith(annotation).prefixedWith(name).to(HttpClientConfig.class);
        bindConfig(binder).annotatedWith(annotation).prefixedWith(name).to(AsyncHttpClientConfig.class);

        // Bind the datasource
        binder.bind(AsyncHttpClient.class).annotatedWith(annotation).toProvider(new HttpClientProvider(name, annotation)).in(Scopes.SINGLETON);

        // Bind aliases
        Key<AsyncHttpClient> key = Key.get(AsyncHttpClient.class, annotation);
        for (Class<? extends Annotation> alias : aliases) {
            binder.bind(AsyncHttpClient.class).annotatedWith(alias).to(key);
        }
    }

    private static class HttpClientProvider implements Provider<AsyncHttpClient>
    {
        private final String name;
        private final Class<? extends Annotation> annotation;
        private Injector injector;

        private HttpClientProvider(String name, Class<? extends Annotation> annotation)
        {
            this.name = name;
            this.annotation = annotation;
        }

        @Inject
        public void setInjector(Injector injector)
        {

            this.injector = injector;
        }

        @Override
        public AsyncHttpClient get()
        {
            ApacheHttpClient httpClient;
            try {
                HttpClientConfig config = injector.getInstance(Key.get(HttpClientConfig.class, annotation));
                httpClient = new ApacheHttpClient(config);
            }
            catch (IOException e) {
                throw Throwables.propagate(e);
            }

            AsyncHttpClientConfig asyncConfig = injector.getInstance(Key.get(AsyncHttpClientConfig.class, annotation));
            ExecutorService executor = newFixedThreadPool(asyncConfig.getWorkerThreads(), new ThreadFactoryBuilder().setNameFormat(name + "-http-client-%s").build());

            return new AsyncHttpClient(httpClient, executor);
        }
    }
}

@@ -459,16 +459,52 @@
 copes;%0A%0A
+import javax.annotation.PreDestroy;%0A
 import j
@@ -1850,42 +1850,8 @@
   %7B%0A
-        // bind the configuration%0A
     
@@ -2056,39 +2056,8 @@
 );%0A%0A
-        // Bind the datasource%0A
     
@@ -2155,22 +2155,16 @@
 rovider(
-name, 
 annotati
@@ -2190,25 +2190,24 @@
 LETON);%0A
-%0A
         
 // Bind 
@@ -2202,23 +2202,149 @@
     
-// Bind aliases
+binder.bind(ExecutorService.class).annotatedWith(annotation).toProvider(new ExecutorServiceProvider(name, annotation)).in(Scopes.SINGLETON);%0A
 %0A   
@@ -2598,26 +2598,31 @@
 c class 
-HttpClient
+ExecutorService
 Provider
@@ -2642,31 +2642,31 @@
 rovider%3C
-AsyncHttpClient
+ExecutorService
 %3E%0A    %7B%0A
@@ -2754,32 +2754,81 @@
 on%3E annotation;%0A
+        private ExecutorService executorService;%0A
         private 
@@ -2863,26 +2863,31 @@
 private 
-HttpClient
+ExecutorService
 Provider
@@ -3025,32 +3025,1171 @@
 ion;%0A        %7D%0A%0A
+        @PreDestroy%0A        public void stop()%0A        %7B%0A            if (executorService != null) %7B%0A                executorService.shutdownNow();%0A                executorService = null;%0A            %7D%0A        %7D%0A%0A        @Inject%0A        public void setInjector(Injector injector)%0A        %7B%0A%0A            this.injector = injector;%0A        %7D%0A%0A        @Override%0A        public ExecutorService get()%0A        %7B%0A            if (executorService == null) %7B%0A                AsyncHttpClientConfig asyncConfig = injector.getInstance(Key.get(AsyncHttpClientConfig.class, annotation));%0A                executorService = newFixedThreadPool(asyncConfig.getWorkerThreads(), new ThreadFactoryBuilder().setDaemon(true).setNameFormat(name + %22-http-client-%25s%22).build());%0A            %7D%0A            return executorService;%0A        %7D%0A    %7D%0A%0A    private static class HttpClientProvider implements Provider%3CAsyncHttpClient%3E%0A    %7B%0A        private final Class%3C? extends Annotation%3E annotation;%0A        private Injector injector;%0A%0A        private HttpClientProvider(Class%3C? extends Annotation%3E annotation)%0A        %7B%0A            this.annotation = annotation;%0A        %7D%0A%0A
         @Inject%0A
@@ -4356,32 +4356,145 @@
 get()%0A        %7B%0A
+            ExecutorService executorService = injector.getInstance(Key.get(ExecutorService.class, annotation));%0A%0A
             Apac
@@ -4820,295 +4820,8 @@
  %7D%0A%0A
-            AsyncHttpClientConfig asyncConfig = injector.getInstance(Key.get(AsyncHttpClientConfig.class, annotation));%0A            ExecutorService executor = newFixedThreadPool(asyncConfig.getWorkerThreads(), new ThreadFactoryBuilder().setNameFormat(name + %22-http-client-%25s%22).build());%0A
 %0A   
@@ -4876,16 +4876,23 @@
 executor
+Service
 );%0A     

Add PreDestroy method to shutdown executor pool