package grizzly;

import java.io.IOException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.sun.grizzly.filterchain.*;
import com.sun.grizzly.nio.transport.TCPNIOTransport;
import com.sun.grizzly.ssl.SSLContextConfigurator;
import com.sun.grizzly.ssl.SSLEngineConfigurator;
import com.sun.grizzly.ssl.SSLFilter;
import com.sun.grizzly.ssl.SSLStreamReader;
import com.sun.grizzly.ssl.SSLStreamWriter;
import com.sun.grizzly.*;

/**
 *
 * @author oleksiys
 */
public class SSLTest {

    public static final int PORT = 7779;

    private volatile FilterChain customFilterChain;

    public boolean assertEquals(Object o, Object o1) {
        return o == o1;
    }

    public boolean assertTrue(boolean b) {
        return b;
    }

    public boolean assertTrue(Object o, Object o1) {
        return o == o1;
    }

    public static void main(String[] args) throws Exception {

        SSLTest sslTest = new SSLTest();
        sslTest.run();
    }

    public void run() throws Exception {
        final FilterChain filterChain = new DefaultFilterChain();

        filterChain.add(new TransportFilter());
        filterChain.add(new DecodingFilter());
        customFilterChain = filterChain;

        testSimpleSyncSSL();
    }
    
    private class DecodingFilter extends FilterAdapter {

        @Override
        public NextAction handleRead(FilterChainContext ctx,
                NextAction nextAction) throws IOException {
            Connection<?> connection = ctx.getConnection();
            System.out.println("Came here");
            //decoding action.  It bypasses the SSL filter and receives the handshake data.
            //Error: can't parse the data as it contains the handshake info.
            return nextAction;
        }
    }
    private class MyFilterAdapter extends FilterAdapter {

        //Accepts a client connection.
        @Override
        public NextAction handleAccept(
                FilterChainContext ctx, NextAction nextAction) throws IOException {
            com.sun.grizzly.Connection connection = ctx.getConnection();


            connection.configureBlocking(true);
            connection.setProcessor(customFilterChain);
            return nextAction;
        }
    }

    public void testSimpleSyncSSL() throws Exception {
        Connection connection = null;
        SSLContextConfigurator sslContextConfigurator = createSSLContextConfigurator();
        SSLEngineConfigurator clientSSLEngineConfigurator = null;
        if (sslContextConfigurator.validateConfiguration(true)) {
            clientSSLEngineConfigurator =
                    new SSLEngineConfigurator(
                    sslContextConfigurator.createSSLContext());
        } else {
            System.out.println("Failed to validate SSLContextConfiguration.");
        }
        TCPNIOTransport transport =
                TransportFactory.getInstance().createTCPTransport();
        transport.getFilterChain().add(new TransportFilter());
        transport.getFilterChain().add(new SSLFilter(
                new SSLEngineConfigurator(sslContextConfigurator.createSSLContext(), false, true, true),
                new SSLEngineConfigurator(clientSSLEngineConfigurator)));
        //transport.getFilterChain().add(new EchoFilter());
        transport.getFilterChain().add(new MyFilterAdapter());

        SSLStreamReader reader = null;
        SSLStreamWriter writer = null;

        try {
            transport.bind(PORT);
            transport.start();

            transport.configureBlocking(true);

            Future<Connection> future = transport.connect("localhost", PORT);
            connection = future.get(10, TimeUnit.SECONDS);
            assertTrue(connection != null);

            connection.setProcessor(null);

            connection.configureBlocking(true);
            reader = new SSLStreamReader(transport.getStreamReader(connection));
            writer = new SSLStreamWriter(transport.getStreamWriter(connection));

            Future handshakeFuture = writer.handshake(reader,
                    clientSSLEngineConfigurator);

            assertTrue(handshakeFuture.isDone());
            handshakeFuture.get();

            byte[] sentMessage = "Hello world!".getBytes();

            // aquire read lock to not allow incoming data to be processed by Processor
            writer.writeByteArray(sentMessage);
            Future<Integer> writeFuture = writer.flush();

            assertTrue("Write timeout", writeFuture.isDone());
            writeFuture.get();

            byte[] receivedMessage = new byte[sentMessage.length];

            Future readFuture = reader.notifyAvailable(receivedMessage.length);
            assertTrue(readFuture.isDone());
            readFuture.get();

            reader.readByteArray(receivedMessage);

            String sentString = new String(sentMessage);
            String receivedString = new String(receivedMessage);
            assertEquals(sentString, receivedString);
        } finally {
            if (reader != null) {
                reader.close();
            }

            if (writer != null) {
                writer.close();
            }
            if (connection != null) {
                connection.close();
            }

            transport.stop();
            TransportFactory.getInstance().close();
        }
    }


    private SSLContextConfigurator createSSLContextConfigurator() {
        SSLContextConfigurator sslContextConfigurator =
                new SSLContextConfigurator();
        ClassLoader cl = getClass().getClassLoader();
        // override system properties
        String cacertsUrl = "/Users/oleksiys/Projects/Grizzly2dot0Test/src/truststore";
        if (cacertsUrl != null) {
            sslContextConfigurator.setTrustStoreFile(cacertsUrl);
            sslContextConfigurator.setTrustStorePass("password");
        }

        // override system properties
        String keystoreUrl = "/Users/oleksiys/Projects/Grizzly2dot0Test/src/mykeystore";
        if (keystoreUrl != null) {
            sslContextConfigurator.setKeyStoreFile(keystoreUrl);
            sslContextConfigurator.setKeyStorePass("password");
        }

        return sslContextConfigurator;
    }
}