package org.httpkit.server;

import clojure.lang.IPersistentMap;
import clojure.lang.PersistentArrayMap;
import org.httpkit.LineTooLargeException;
import org.httpkit.ProtocolException;
import org.httpkit.RequestTooLargeException;
import org.httpkit.server.MockClojureHandler;
import org.junit.Assert;
import org.junit.Test;

import java.nio.ByteBuffer;
import java.util.Map;
import java.util.TreeMap;
import java.util.Vector;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;

public class RingHandlerTest {

    private HttpDecoder httpDecoder = new HttpDecoder(8388608, 4096, ProxyProtocolOption.DISABLED);

    @Test
    public void shouldUseExternalThreadPoolForExecution() throws InterruptedException, ProtocolException, LineTooLargeException, RequestTooLargeException {
        Vector<String> assertionItems = new Vector<String>();

        ExecutorService testWorkerPool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(2));
        RingHandler ringHandler = new RingHandler(new MockClojureHandler(aDummyResponse()), testWorkerPool);
        ringHandler.handle(aDummyRequest(), new MockRespCallback(assertionItems));

        Thread.sleep(50);

        assertDummyRequestReceived(assertionItems);
    }

    @Test
    public void shouldUseInternalThreadPoolForExecution() throws InterruptedException, ProtocolException, LineTooLargeException, RequestTooLargeException {
        Vector<String> assertionItems = new Vector<String>();

        RingHandler ringHandler = new RingHandler(1, new MockClojureHandler(aDummyResponse()), "some-prefix", 2, "http-kit");
        ringHandler.handle(aDummyRequest(), new MockRespCallback(assertionItems));

        Thread.sleep(50);

        assertDummyRequestReceived(assertionItems);
    }


    public class MockRespCallback extends RespCallback {
        private Vector<String> storage;

        public MockRespCallback(Vector<String> storage) {
            super(null, null);
            this.storage = storage;
        }

        @Override
        public void run(ByteBuffer... buffers) {
            StringBuilder builder = new StringBuilder();
            for (ByteBuffer buffer : buffers) {
                builder.append(new String(buffer.array()));
            }
            storage.add(builder.toString());
        }
    }

    private HttpRequest asHttpRequest(String... requestLines) throws ProtocolException, LineTooLargeException, RequestTooLargeException {
        httpDecoder.reset();
        String joinedRequest = String.join("\n", requestLines);
        return httpDecoder.decode(ByteBuffer.wrap((joinedRequest + "\n\n").getBytes()));
    }

    private IPersistentMap aDummyResponse() {
        Map<Object, Object> m = new TreeMap<Object, Object>();
        m.put(ClojureRing.BODY, "DUMMY_RESPONSE");
        m.put(ClojureRing.STATUS, 301);
        return PersistentArrayMap.create(m);
    }

    private HttpRequest aDummyRequest() throws ProtocolException, LineTooLargeException, RequestTooLargeException {
        return asHttpRequest(
                "GET /foo/bar?query=baz HTTP/1.0",
                "Host: github.com/http-kit/http-kit",
                "Content-Type: text/html",
                "x-forwarded-for: 0.0.0.0:80");
    }

    private void assertDummyRequestReceived(Vector<String> assertionItems) {
        Assert.assertThat("should return only one single response", assertionItems.size(), is(1));
        String element = assertionItems.firstElement();
        Assert.assertThat("should write dummy-response as a side-effect", element, containsString("DUMMY_RESPONSE"));
        Assert.assertThat("should write dummy-response with correct status code", element, containsString("HTTP/1.1 301 Moved Permanently"));
    }
}

@@ -2714,24 +2714,36 @@
 ();%0A        
+%0A        // 
 String joine
@@ -2785,16 +2785,540 @@
 tLines);
+ %0A        // String.join is was only released in 1.8%0A        %0A        String joinedRequest = %22%22;%0A        int requestLength = requestLines.length;%0A%0A        if (requestLines != null && requestLength %3E 0) %7B%0A            StringBuilder sb = new StringBuilder();%0A            for (int i = 0; i %3C requestLength; i++) %7B%0A%0A                sb.append(requestLines%5Bi%5D);%0A%0A                if (i != requestLength - 1) %7B%0A                    sb.append(%22%5Cn%22);%0A                %7D%0A%0A            %7D%0A            joinedRequest = sb.toString();%0A        %7D
 %0A       

Use Java 7 compatible method to join string in tests (@alekcz)