package com.github.dockerjava.netty.exec;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.isEmptyString;
import static org.hamcrest.Matchers.not;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.api.model.StreamType;
import com.github.dockerjava.core.command.AttachContainerResultCallback;
import com.github.dockerjava.netty.AbstractNettyDockerClientTest;

@Test(groups = "integration")
public class AttachContainerCmdExecTest extends AbstractNettyDockerClientTest {

    @BeforeTest
    public void beforeTest() throws Exception {
        super.beforeTest();
    }

    @AfterTest
    public void afterTest() {
        super.afterTest();
    }

    @BeforeMethod
    public void beforeMethod(Method method) {
        super.beforeMethod(method);
    }

    @AfterMethod
    public void afterMethod(ITestResult result) {
        super.afterMethod(result);
    }

    @Test
    public void attachContainerWithoutTTY() throws Exception {

        String snippet = "hello world";

        CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("echo", snippet)
                .withTty(false).exec();

        LOG.info("Created container: {}", container.toString());
        assertThat(container.getId(), not(isEmptyString()));

        dockerClient.startContainerCmd(container.getId()).exec();

        AttachContainerTestCallback callback = new AttachContainerTestCallback() {
            @Override
            public void onNext(Frame frame) {
                assertEquals(frame.getStreamType(), StreamType.STDOUT);
                super.onNext(frame);
            };
        };

        dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true)
                .withLogs(true).exec(callback).awaitCompletion(10, TimeUnit.SECONDS);
        callback.close();

        assertThat(callback.toString(), containsString(snippet));
    }

    @Test
    public void attachContainerWithStdin() throws Exception {

        String snippet = "hello world";

        CreateContainerResponse container = dockerClient.createContainerCmd("busybox").withCmd("/bin/sh", "-c", "read line && echo $line")
                .withTty(false).withStdinOpen(true).exec();

        LOG.info("Created container: {}", container.toString());
        assertThat(container.getId(), not(isEmptyString()));

        dockerClient.startContainerCmd(container.getId()).exec();

        InspectContainerResponse inspectContainerResponse = dockerClient.inspectContainerCmd(container.getId()).exec();

        assertTrue(inspectContainerResponse.getState().getRunning());

        AttachContainerTestCallback callback = new AttachContainerTestCallback() {

            @Override
            public void onNext(Frame frame) {
                assertEquals(frame.getStreamType(), StreamType.STDOUT);
                super.onNext(frame);
            };
        };

        InputStream stdin = new ByteArrayInputStream((snippet + "\n").getBytes());

        dockerClient.attachContainerCmd(container.getId()).withStdErr(true).withStdOut(true).withFollowStream(true)
                .withStdIn(stdin)
                .exec(callback)
                .awaitCompletion(5, TimeUnit.SECONDS);
        callback.close();

        assertThat(callback.toString(), containsString(snippet));
    }

    @Test
    public void attachContainerWithTTY() throws Exception {

        File baseDir = new File(Thread.currentThread().getContextClassLoader()
                .getResource("attachContainerTestDockerfile").getFile());

        String imageId = buildImage(baseDir);

        CreateContainerResponse container = dockerClient.createContainerCmd(imageId).withTty(true).exec();

        LOG.info("Created container: {}", container.toString());
        assertThat(container.getId(), not(isEmptyString()));

        dockerClient.startContainerCmd(container.getId()).exec();

        AttachContainerTestCallback callback = new AttachContainerTestCallback() {
            @Override
            public void onNext(Frame frame) {
                assertEquals(frame.getStreamType(), StreamType.RAW);
                super.onNext(frame);
            };
        };

        dockerClient.attachContainerCmd(container.getId())
                .withStdErr(true)
                .withStdOut(true)
                .withFollowStream(true)
                .exec(callback)
                .awaitCompletion(10, TimeUnit.SECONDS);
        callback.close();

        // HexDump.dump(collectFramesCallback.toString().getBytes(), 0, System.out, 0);

        assertThat(callback.toString(), containsString("stdout\r\nstderr"));
    }

    public static class AttachContainerTestCallback extends AttachContainerResultCallback {
        private StringBuffer log = new StringBuffer();

        @Override
        public void onNext(Frame item) {
            log.append(new String(item.getPayload()));
            super.onNext(item);
        }

        @Override
        public String toString() {
            return log.toString();
        }
    }
}

@@ -36,16 +36,69 @@
 .exec;%0A%0A
+import static java.util.concurrent.TimeUnit.SECONDS;%0A
 import s
@@ -142,16 +142,16 @@
 rtThat;%0A
-
 import s
@@ -2486,33 +2486,24 @@
 pletion(10, 
-TimeUnit.
 SECONDS);%0A  
@@ -2792,32 +2792,49 @@
 erCmd(%22busybox%22)
+%0A                
 .withCmd(%22/bin/s
@@ -2844,16 +2844,27 @@
  %22-c%22, %22
+sleep 1 && 
 read lin
@@ -2912,16 +2912,33 @@
 y(false)
+%0A                
 .withStd
@@ -2945,24 +2945,41 @@
 inOpen(true)
+%0A                
 .exec();%0A%0A  
@@ -3162,32 +3162,103 @@
 tId()).exec();%0A%0A
+        Thread.sleep(SECONDS.toMillis(3)); //wait bash initialisation%0A%0A
         InspectC
@@ -3516,17 +3516,16 @@
 ack() %7B%0A
-%0A
         
@@ -3698,33 +3698,32 @@
 );%0A            %7D
-;
 %0A        %7D;%0A%0A   
@@ -3865,42 +3865,93 @@
 d())
-.withStdErr(true).withStdOut(true)
+%0A                .withStdErr(true)%0A                .withStdOut(true)%0A                
 .wit
@@ -4073,20 +4073,12 @@
 ion(
+1
 5, 
-TimeUnit.
 SECO
@@ -5232,32 +5232,32 @@
 .exec(callback)%0A
+
                 
@@ -5277,25 +5277,16 @@
 ion(10, 
-TimeUnit.
 SECONDS)

fix concurrency issue