/**
 * Copyright 2016 Palantir Technologies
 *
 * Licensed under the BSD-3 License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/BSD-3-Clause
 *
 * 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 com.palantir.atlasdb.ete;

import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import javax.net.ssl.SSLSocketFactory;

import org.junit.rules.RuleChain;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.palantir.atlasdb.http.AtlasDbHttpClients;
import com.palantir.atlasdb.todo.TodoResource;
import com.palantir.docker.compose.DockerComposeRule;
import com.palantir.docker.compose.connection.Container;
import com.palantir.docker.compose.connection.DockerPort;
import com.palantir.docker.compose.connection.waiting.HealthCheck;
import com.palantir.docker.compose.connection.waiting.SuccessOrFailure;
import com.palantir.docker.compose.execution.DockerComposeRunArgument;
import com.palantir.docker.compose.execution.DockerComposeRunOption;

public class EteSetup {
    private static final Gradle GRADLE_PREPARE_TASK = Gradle.ensureTaskHasRun(":atlasdb-ete-tests:prepareForEteTests");
    private static final Optional<SSLSocketFactory> NO_SSL = Optional.absent();

    private static final int TIMELOCK_SERVER_PORT = 3828;

    private static DockerComposeRule docker;
    private static List<String> availableClients;

    protected static <T> T createClientToSingleNode(Class<T> clazz) {
        return createClientFor(clazz, asPort(getSingleClient()));
    }

    protected static <T> T createClientToAllNodes(Class<T> clazz) {
        return createClientToMultipleNodes(clazz, availableClients);
    }

    protected static <T> T createClientToMultipleNodes(Class<T> clazz, List<String> nodeNames) {
        Collection<String> uris = ImmutableList.copyOf(nodeNames).stream()
                .map(node -> asPort(node))
                .map(port -> port.inFormat("http://$HOST:$EXTERNAL_PORT"))
                .collect(Collectors.toList());

        return AtlasDbHttpClients.createProxyWithFailover(NO_SSL, uris, clazz);
    }

    protected static String runCliCommand(String command) throws IOException, InterruptedException {
        return docker.run(
                DockerComposeRunOption.options("-T"),
                "ete-cli",
                DockerComposeRunArgument.arguments("bash", "-c", command));
    }

    protected static RuleChain setupComposition(String name, String composeFile, List<String> availableClientNames) {
        availableClients = ImmutableList.copyOf(availableClientNames);

        docker = DockerComposeRule.builder()
                .file(composeFile)
                .waitingForService(getSingleClient(), toBeReady())
                .saveLogsTo("container-logs/" + name)
                .build();

        return RuleChain
                .outerRule(GRADLE_PREPARE_TASK)
                .around(docker);
    }

    private static DockerPort asPort(String node) {
        return docker.containers().container(node).port(TIMELOCK_SERVER_PORT);
    }

    private static String getSingleClient() {
        return availableClients.get(0);
    }

    private static <T> T createClientFor(Class<T> clazz, Container container) {
        return createClientFor(clazz, container.port(TIMELOCK_SERVER_PORT));
    }

    private static <T> T createClientFor(Class<T> clazz, DockerPort port) {
        String uri = port.inFormat("http://$HOST:$EXTERNAL_PORT");
        return AtlasDbHttpClients.createProxy(NO_SSL, uri, clazz);
    }

    private static HealthCheck<Container> toBeReady() {
        return (container) -> {
            TodoResource todos = createClientFor(TodoResource.class, container);

            return SuccessOrFailure.onResultOf(() -> {
                todos.isHealthy();
                return true;
            });
        };
    }
}

@@ -1834,265 +1834,160 @@
     
-protected static %3CT%3E T createClientToSingleNode(Class%3CT%3E clazz) %7B%0A        return createClientFor(clazz, asPort(getSingleClient()));%0A    %7D%0A%0A    protected static %3CT%3E T createClientToAllNodes(Class%3CT%3E clazz) %7B%0A        return createClientToMultipleNodes(clazz, 
+static RuleChain setupComposition(String name, String composeFile, List%3CString%3E availableClientNames) %7B%0A        availableClients = ImmutableList.copyOf(
 avai
@@ -2001,434 +2001,348 @@
 ient
+Name
 s);%0A
+%0A
     
-%7D%0A%0A
     
-protected static %3CT%3E T createClientToMultipleNodes(Class%3CT%3E clazz, List%3CString%3E nodeNames) %7B%0A        Collection%3CString%3E uris = ImmutableList.copyOf(nodeNames).stream()%0A                .map(node -%3E asPort(node))%0A                .map(port -%3E port.inFormat(%22http://$HOST:$EXTERNAL_PORT%22))%0A                .collect(Collectors.toList());%0A%0A        return AtlasDbHttpClients.createProxyWithFailover(NO_SSL, uris, clazz
+docker = DockerComposeRule.builder()%0A                .file(composeFile)%0A                .waitingForService(getSingleClient(), toBeReady())%0A                .saveLogsTo(%22container-logs/%22 + name)%0A                .build();%0A%0A        return RuleChain%0A                .outerRule(GRADLE_PREPARE_TASK)%0A                .around(docker
 );%0A 
@@ -2347,34 +2347,24 @@
 %0A    %7D%0A%0A    
-protected 
 static Strin
@@ -2637,170 +2637,245 @@
     
-protected static RuleChain setupComposition(String name, String composeFile, List%3CString%3E availableClientNames) %7B%0A        availableClients = ImmutableList.copyOf(
+static %3CT%3E T createClientToSingleNode(Class%3CT%3E clazz) %7B%0A        return createClientFor(clazz, asPort(getSingleClient()));%0A    %7D%0A%0A    static %3CT%3E T createClientToAllNodes(Class%3CT%3E clazz) %7B%0A        return createClientToMultipleNodes(clazz, 
 avai
@@ -2889,348 +2889,432 @@
 ient
-Name
 s);%0A
-%0A
     
+%7D%0A%0A
     
-docker = DockerComposeRule.builder()%0A                .file(composeFile)%0A                .waitingForService(getSingleClient(), toBeReady())%0A                .saveLogsTo(%22container-logs/%22 + name)%0A                .build();%0A%0A        return RuleChain%0A                .outerRule(GRADLE_PREPARE_TASK)%0A                .around(docker
+private static %3CT%3E T createClientToMultipleNodes(Class%3CT%3E clazz, List%3CString%3E nodeNames) %7B%0A        Collection%3CString%3E uris = ImmutableList.copyOf(nodeNames).stream()%0A                .map(node -%3E asPort(node))%0A                .map(port -%3E port.inFormat(%22http://$HOST:$EXTERNAL_PORT%22))%0A                .collect(Collectors.toList());%0A%0A        return AtlasDbHttpClients.createProxyWithFailover(NO_SSL, uris, clazz
 );%0A 

Make methods package-private and reorder them