package org.apache.synapse.transport.nhttp;

import junit.framework.TestCase;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.nio.DefaultHttpServerIODispatch;
import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.*;
import org.apache.http.nio.params.NIOReactorPNames;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.IOReactorExceptionHandler;
import org.apache.http.nio.reactor.ListenerEndpoint;
import org.apache.http.params.*;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.util.concurrent.ThreadFactory;

public class HttpCoreTest extends TestCase {

    private DefaultListeningIOReactor ioReactor;

    public void testHttpCore() throws Exception {
        System.out.println("Round 1");
        initServer();
        // send a message
        HttpGet get = new HttpGet("http://localhost:8280");
        DefaultHttpClient client = new DefaultHttpClient();
        HttpResponse response = client.execute(get);
        assertEquals(200, response.getStatusLine().getStatusCode());
        System.out.println("Waiting - Shutdown in 5 secs");
        Thread.sleep(5000);
        shutdown();

        Thread.sleep(2000);

        System.out.println("\n\nRound 2");
        initServer();
        System.out.println("Waiting - Shutdown in 5 secs");
        Thread.sleep(5000);
        shutdown();
    }

    private void initServer() throws Exception {
        final int port = 8280;

        // HTTP parameters for the server
        HttpParams params = new BasicHttpParams();
        params.
                setIntParameter(HttpConnectionParams.SO_TIMEOUT,60000).
                setIntParameter(HttpConnectionParams.CONNECTION_TIMEOUT, 0).
                setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8 * 1024).
                setBooleanParameter(HttpConnectionParams.STALE_CONNECTION_CHECK, false).
                setBooleanParameter(HttpConnectionParams.TCP_NODELAY, true).
                setBooleanParameter(NIOReactorPNames.INTEREST_OPS_QUEUEING, false).
                setParameter(HttpProtocolParams.ORIGIN_SERVER, "Synapse-PassThrough-HTTP");

        IOReactorConfig reactorConfig = new IOReactorConfig();
        reactorConfig.setSoTimeout(60000);
        reactorConfig.setConnectTimeout(0);
        reactorConfig.setSndBufSize(8096);
        reactorConfig.setRcvBufSize(8096);
        reactorConfig.setTcpNoDelay(true);
        reactorConfig.setInterestOpQueued(false);
        reactorConfig.setIoThreadCount(2);

        // Create server-side I/O event dispatch
        final IOEventDispatch ioEventDispatch = new DefaultHttpServerIODispatch(
                new TestSourceHandler(), params);
        // Create server-side I/O reactor
        ioReactor = new DefaultListeningIOReactor(reactorConfig,
                new ThreadFactory() {
                    public Thread newThread(Runnable r) {
                        return new Thread(r, "http-server-io");
                    }
                });
        ioReactor.setExceptionHandler(new IOReactorExceptionHandler() {
            public boolean handle(IOException ioException) {
                ioException.printStackTrace();
                return true;
            }

            public boolean handle(RuntimeException runtimeException) {
                runtimeException.printStackTrace();
                return true;
            }
        });

        // Listen of the given port
        ListenerEndpoint endpoint = ioReactor.listen(new InetSocketAddress(port));
        Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    // Ready to go!
                    ioReactor.execute(ioEventDispatch);
                } catch (InterruptedIOException ex) {
                    System.err.println("Interrupted");
                } catch (IOException e) {
                    System.err.println("I/O error: " + e.getMessage());
                }
                System.out.println("Shutdown");
            }
        };
        t.start();

        endpoint.waitFor();
        System.out.println("Listener started on port: " + port);
    }

    private void shutdown() throws Exception {
        ioReactor.shutdown();
    }

    static class TestSourceHandler implements NHttpServerEventHandler {
        public void connected(NHttpServerConnection nHttpServerConnection) throws IOException, HttpException {
            System.out.println("Connected");
        }

        public void requestReceived(NHttpServerConnection nHttpServerConnection) throws IOException, HttpException {
            System.out.println("Request received");
            DefaultHttpResponseFactory fac = new DefaultHttpResponseFactory();
            HttpResponse response = fac.newHttpResponse(new ProtocolVersion("HTTP", 1,1), 200, nHttpServerConnection.getContext());
            response.setStatusCode(200);
            nHttpServerConnection.submitResponse(response);
            System.out.println("Response submitted");
            nHttpServerConnection.close();
        }

        public void inputReady(NHttpServerConnection nHttpServerConnection, ContentDecoder contentDecoder) throws IOException, HttpException {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        public void responseReady(NHttpServerConnection nHttpServerConnection) throws IOException, HttpException {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        public void outputReady(NHttpServerConnection nHttpServerConnection, ContentEncoder contentEncoder) throws IOException, HttpException {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        public void endOfInput(NHttpServerConnection nHttpServerConnection) throws IOException {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        public void timeout(NHttpServerConnection nHttpServerConnection) throws IOException {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        public void closed(NHttpServerConnection nHttpServerConnection) {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        public void exception(NHttpServerConnection nHttpServerConnection, Exception e) {
            //To change body of implemented methods use File | Settings | File Templates.
        }
    }
}
