/*
 * Copyright 2014 Jeanfrancois Arcand
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.atmosphere.wasync.impl;

import org.atmosphere.wasync.Options;
import org.atmosphere.wasync.Socket;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;

import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig;

/**
 * Util class for building {@link AsyncHttpClient}
 *
 * @author Jeanfrancois Arcand
 */
public class ClientUtil {
    private static final String WASYNC_USER_AGENT = "wAsync/2.0";

    public final static AsyncHttpClient createDefaultAsyncHttpClient(Options o) {
    	return createDefaultAsyncHttpClient(o.requestTimeoutInSeconds(), null);      
    }

    public final static AsyncHttpClient createDefaultAsyncHttpClient(int requestTimeoutInSeconds) {
    	return createDefaultAsyncHttpClient(requestTimeoutInSeconds, null);
    }
    
    public final static AsyncHttpClient createDefaultAsyncHttpClient(int requestTimeoutInSeconds, NioClientSocketChannelFactory socketChannelFactory) {
        AsyncHttpClientConfig.Builder b = new AsyncHttpClientConfig.Builder();
        b.setFollowRedirect(true).setConnectionTimeout(-1).setReadTimeout(requestTimeoutInSeconds == -1 ? requestTimeoutInSeconds : requestTimeoutInSeconds * 1000).setUserAgent(WASYNC_USER_AGENT);

        NettyAsyncHttpProviderConfig nettyConfig = new NettyAsyncHttpProviderConfig();
        
        if (socketChannelFactory != null) {
        	nettyConfig.setSocketChannelFactory(socketChannelFactory);
        }
        nettyConfig.addProperty("child.tcpNoDelay", "true");
        nettyConfig.addProperty("child.keepAlive", "true");

        AsyncHttpClientConfig config = b.setAsyncHttpClientProviderConfig(nettyConfig).build();
        return new AsyncHttpClient(config);
    }
    
    public static Socket create(Options options) {
        return create(options, DefaultSocket.class);
    }

    public static Socket create(Options options, Class<? extends Socket> socket) {
        AsyncHttpClient asyncHttpClient = options.runtime();
        if (asyncHttpClient == null || asyncHttpClient.isClosed()) {
            asyncHttpClient = ClientUtil.createDefaultAsyncHttpClient(options);
            options.runtime(asyncHttpClient);
        }
        return getSocket(options, socket);
    }

    public final static Socket getSocket(Options options, Class<? extends Socket> socket) {
        try {
            return socket.getConstructor(Options.class).newInstance(options);
        } catch (Exception e) {
            return new DefaultSocket(options);
        }
    }

    public static Socket create(Class<? extends Socket> socket) {
        AsyncHttpClient asyncHttpClient = createDefaultAsyncHttpClient(new DefaultOptionsBuilder().reconnect(true).build());

        return getSocket(new DefaultOptionsBuilder().runtime(asyncHttpClient, false).build(), socket);
    }

    public static Socket create() {
        AsyncHttpClient asyncHttpClient = createDefaultAsyncHttpClient(new DefaultOptionsBuilder().reconnect(true).build());

        return getSocket(new DefaultOptionsBuilder().runtime(asyncHttpClient, false).build(), DefaultSocket.class);
    }
}

@@ -707,81 +707,8 @@
 ket;
-%0Aimport org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
 %0A%0Aim
@@ -801,16 +801,69 @@
 Config;%0A
+import com.ning.http.client.AsyncHttpProviderConfig;%0A
 import c
@@ -1194,32 +1194,240 @@
 tions o) %7B%0A    %09
+NettyAsyncHttpProviderConfig nettyConfig = new NettyAsyncHttpProviderConfig();%0A        nettyConfig.addProperty(%22child.tcpNoDelay%22, %22true%22);%0A        nettyConfig.addProperty(%22child.keepAlive%22, %22true%22);%0A        
 return createDef
@@ -1480,17 +1480,20 @@
 ), n
-ull);    
+ettyConfig);
   %0A 
@@ -1493,24 +1493,28 @@
 g);  %0A    %7D%0A
+    
 %0A    public 
@@ -1575,43 +1575,74 @@
 ent(
-int requestTimeoutInSeconds
+Options o, AsyncHttpProviderConfig asyncHttpProviderConfig
 ) %7B%0A    
 %09ret
@@ -1637,17 +1637,20 @@
 ) %7B%0A    
-%09
+    
 return c
@@ -1677,16 +1677,18 @@
 pClient(
+o.
 requestT
@@ -1706,16 +1706,39 @@
 onds
-, null);
+(), asyncHttpProviderConfig);  
 %0A   
@@ -1847,58 +1847,55 @@
 ds, 
-NioClientSocketChannelFactory socketChannelFactory
+AsyncHttpProviderConfig asyncHttpProviderConfig
 ) %7B%0A
@@ -2170,25 +2170,24 @@
 AGENT);%0A
-%0A
         
 NettyAsy
@@ -2178,21 +2178,16 @@
         
-Netty
 AsyncHtt
@@ -2191,37 +2191,30 @@
 Http
-Provider
+Client
 Config 
-nettyC
+c
 onfig = 
 new 
@@ -2213,376 +2213,60 @@
 g = 
-new NettyAsyncHttpProviderConfig();%0A        %0A        if (socketChannelFactory != null) %7B%0A        %09nettyConfig.setSocketChannelFactory(socketChannelFactory);%0A        %7D%0A        nettyConfig.addProperty(%22child.tcpNoDelay%22, %22true%22);%0A        nettyConfig.addProperty(%22child.keepAlive%22, %22true%22);%0A%0A        AsyncHttpClientConfig config = b.setAsyncHttpClientProviderConfig(netty
+b.setAsyncHttpClientProviderConfig(asyncHttpProvider
 Conf
@@ -2324,32 +2324,35 @@
 fig);%0A    %7D%0A    
+   
 %0A    public stat

Allow for safe coarser-scope shared AsyncHttpClient creation for which no client and its options builder exists.