package org.ethereumclassic.etherjar.rpc.transport;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.HttpClients;
import org.ethereumclassic.etherjar.rpc.JacksonRpcConverter;
import org.ethereumclassic.etherjar.rpc.RpcConverter;
import org.ethereumclassic.etherjar.rpc.json.RequestJson;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
import java.util.concurrent.*;
import java.util.logging.Logger;

/**
 * @author Igor Artamonov
 */
public class DefaultRpcTransport implements RpcTransport {

    private static final Logger log = Logger.getLogger(DefaultRpcTransport.class.getName());

    private int callSequence = 1;

    private URI host;
    private ExecutorService executorService;
    private RpcConverter rpcConverter;

    private HttpClient httpclient;

    public DefaultRpcTransport(URI host, RpcConverter rpcConverter, ExecutorService executorService, HttpClient httpClient) {
        this.host = host;
        this.rpcConverter = rpcConverter;
        this.executorService = executorService;
        httpclient = httpClient;
    }

    public DefaultRpcTransport(URI host, RpcConverter rpcConverter, ExecutorService executorService) {
        this.host = host;
        this.rpcConverter = rpcConverter;
        this.executorService = executorService;
        httpclient = HttpClients.createDefault();
    }

    public DefaultRpcTransport(URI host) {
        this.host = host;
        this.rpcConverter = createRpcConverter();
        this.executorService = createExecutor();
        httpclient = HttpClients.createDefault();
    }

    private RpcConverter createRpcConverter() {
        return new JacksonRpcConverter();
    }

    public ExecutorService createExecutor() {
        return Executors.newFixedThreadPool(2);
    }

    @Override
    public <T> CompletableFuture<T> execute(final String method, final List params, final Class<T> resultType) {
        CompletableFuture<T> f = new CompletableFuture<T>();
        executorService.submit(() -> {
            try {
                f.complete(executeSync(method, params, resultType));
            } catch (IOException e) {
                f.completeExceptionally(e);
            }
        });
        return f;
    }

    public <T> T executeSync(String method, List params, Class<T> resultType) throws IOException {
        String json = rpcConverter.toJson(buildCall(method, params));
        RequestBuilder requestBuilder = RequestBuilder.create("POST")
            .setUri(host)
            .addHeader("Content-Type", "application/json")
            .setEntity(new ByteArrayEntity(json.getBytes("UTF-8")));
        HttpResponse rcpResponse = httpclient.execute(requestBuilder.build());
        if (rcpResponse.getStatusLine().getStatusCode() != 200) {
            throw new IOException("Server returned error response: " + rcpResponse.getStatusLine().getStatusCode());
        }
        InputStream content = rcpResponse.getEntity().getContent();
        return rpcConverter.fromJson(content, resultType);
    }

    public RequestJson buildCall(String method, List params) {
        if (callSequence >= 0x1fffffff) {
            callSequence = 1;
        }
        return new RequestJson(method, params, callSequence++);
    }

}

@@ -175,24 +175,81 @@
 estBuilder;%0A
+import org.apache.http.conn.HttpClientConnectionManager;%0A
 import org.a
@@ -331,16 +331,85 @@
 lients;%0A
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;%0A
 import o
@@ -925,16 +925,67 @@
 Name());
+%0A    private static final int MAX_CONNECTIONS = 50;
 %0A%0A    pr
@@ -1398,32 +1398,37 @@
 ervice;%0A        
+this.
 httpclient = htt
@@ -1444,559 +1444,708 @@
     
-%7D%0A%0A    public DefaultRpcTransport(URI host, RpcConverter rpcConverter, ExecutorService executorService) %7B%0A        this.host = host;%0A        this.rpcConverter = rpcConverter;%0A        this.executorService = executorService;%0A        httpclient = HttpClients.createDefault();%0A    %7D%0A%0A    public DefaultRpcTransport(URI host) %7B%0A        this.host = host;%0A        this.rpcConverter = createRpcConverter();%0A        this.executorService = createExecutor(
+    if (this.httpclient == null) %7B%0A            this.httpclient = HttpClients.custom()%0A                .setConnectionManager(createConnectionManager())%0A                .setConnectionManagerShared(true)%0A                .build();%0A        %7D%0A    %7D%0A%0A    public DefaultRpcTransport(URI host, RpcConverter rpcConverter, ExecutorService executorService) %7B%0A        this(host, rpcConverter, executorService, null);%0A    %7D%0A%0A    public DefaultRpcTransport(URI host) %7B%0A        this(host, new JacksonRpcConverter(), Executors.newCachedThreadPool()
 );%0A    
+%7D%0A%0A
     
-httpclient = HttpClients.createDefault();
+protected HttpClientConnectionManager createConnectionManager() %7B
 %0A    
-%7D%0A%0A
     
-private RpcConverter createRpcConvert
+PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManag
 er()
- %7B
+;
 %0A   
@@ -2153,141 +2153,109 @@
     
-return new JacksonRpcConverter(
+cm.setMaxTotal(MAX_CONNECTIONS
 );%0A    
-%7D%0A%0A
     
-public ExecutorService createExecutor() %7B%0A        return Executors.newFixedThreadPool(2)
+cm.setDefaultMaxPerRoute(MAX_CONNECTIONS);%0A        return cm
 ;%0A  

allow up to 50 simultaneous connections to rpc