/*
# Licensed Materials - Property of IBM
# Copyright IBM Corp. 2015,2016 
*/
package quarks.tests.connectors.wsclient.javax.websocket;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;

import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;

/**
 * Simple WebSocket server program to echo received messages.
 * <p>
 * See https://github.com/jetty-project/embedded-jetty-websocket-examples
 */
@ServerEndpoint(value="/echo")
public class WebSocketServerEcho {
    String svrName = this.getClass().getSimpleName();
    Server server;
    ServerConnector connector;
    URI curEndpointURI;
    boolean curNeedClientAuth;
    
    public static void main(String[] args) throws Exception {
        URI uri = new URI("ws://localhost:0");
        boolean needClientAuth = false;
        if (args.length > 0)
            uri = new URI(args[0]);
        if (args.length > 1)
            needClientAuth = "needClientAuth".equals(args[1]);
        WebSocketServerEcho srvr = new WebSocketServerEcho();
        srvr.start(uri, needClientAuth);
    }
    
    public void start(URI endpointURI) {
        start(endpointURI, false);
    }
    
    public void start(URI endpointURI, boolean needClientAuth) {
        curEndpointURI = endpointURI;
        curNeedClientAuth = needClientAuth;

        System.out.println(svrName+" "+endpointURI + " needClientAuth="+needClientAuth);

        server = createServer(endpointURI, needClientAuth);
        connector = (ServerConnector)server.getConnectors()[0];

        // Setup the basic application "context" for this application at "/"
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);
        
        try {
            // Initialize javax.websocket layer
            ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);

            // Add WebSocket endpoint to javax.websocket layer
            wscontainer.addEndpoint(this.getClass());

            // System.setProperty("javax.net.debug", "ssl"); // or "all"; "help" for full list

            server.start();
            System.out.println(svrName+" started "+connector);
            // server.dump(System.err);            
        }
        catch (Exception e) {
            // TODO Auto-generated catch block
            throw new RuntimeException("start", e);
        }
    }
    
    private Server createServer(URI endpointURI, boolean needClientAuth) {
        if ("ws".equals(endpointURI.getScheme())) {
            return new Server(endpointURI.getPort());
        }
        else if ("wss".equals(endpointURI.getScheme())) {
            // see http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java
            //     http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java
            
            Server server = new Server();
            
            SslContextFactory sslContextFactory = new SslContextFactory();
            sslContextFactory.setKeyStorePath(getStorePath("serverKeyStore.jks"));
            sslContextFactory.setKeyStorePassword("passw0rd");
            sslContextFactory.setKeyManagerPassword("passw0rd");
            sslContextFactory.setCertAlias("default");
            sslContextFactory.setNeedClientAuth(needClientAuth);
            sslContextFactory.setTrustStorePath(getStorePath("serverTrustStore.jks"));
            sslContextFactory.setTrustStorePassword("passw0rd");
            
            HttpConfiguration httpsConfig = new HttpConfiguration();
            httpsConfig.addCustomizer(new SecureRequestCustomizer());
            
            ServerConnector https= new ServerConnector(server,
                    new SslConnectionFactory(sslContextFactory,
                            HttpVersion.HTTP_1_1.asString()),
                    new HttpConnectionFactory(httpsConfig));
            https.setPort(endpointURI.getPort());
            
            server.addConnector(https);
            return server;
        }
        else
            throw new IllegalArgumentException("unrecognized curEndpointURI: "+endpointURI);
    }
    
    private String getStorePath(String storeLeaf) {
        return KeystorePath.getStorePath(storeLeaf);
    }
    
    public int getPort() {
        // returns -1 if called before started
        return connector.getLocalPort();
    }
    
    /** restart a running server on the same port, etc: stop, delay, start */
    private final ScheduledExecutorService schedExecutor = Executors.newScheduledThreadPool(0);
    public void restart(int secDelay) {
        // stop, schedule delay&start and return
        URI endpointURI = setPort(curEndpointURI, getPort());
        try {
            System.out.println(svrName+" restart: stop "+connector);
            connector.stop();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(svrName+" restart: scheduling start after "+secDelay+"sec");
        schedExecutor.schedule(() -> {
            System.out.println(svrName+" restart: starting...");
            start(endpointURI, curNeedClientAuth);
        }, secDelay, TimeUnit.SECONDS);
    }
    
    private URI setPort(URI endpointURI, int port) {
        try {
            URI uri = endpointURI;
            if (uri.getPort() != port) {
                uri = new URI(uri.getScheme(), uri.getUserInfo(),
                        uri.getHost(), port,
                        uri.getPath(), uri.getQuery(), uri.getFragment());
            }
            return uri;
        } catch (URISyntaxException e) {
            throw new RuntimeException("Unable to create URI", e);
        }
    }
    
    public void stop() {
        if (connector != null) {
            try {
                System.out.println(svrName+" stop "+connector);
                connector.stop();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            finally {
                connector = null;
            }
        }
    }
    
    @OnOpen
    public void opOpen(Session session) {
        System.out.println(svrName+" onOpen ");
    }
    
    @OnClose
    public void onClose(Session session, CloseReason reason) {
        System.out.println(svrName+" onClose reason="+reason);
    }
    
    @OnMessage
    public void onStringMessage(Session session, String message) {
        System.out.println(svrName+" onStringMessage msg="+message);
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  // "echo" response
    }
    
    @OnMessage
    public void onByteMessage(Session session, ByteBuffer message) {
        System.out.println(svrName+" onByteMessage "+message.array().length+" bytes");
        try {
            session.getBasicRemote().sendBinary(message);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  // "echo" response
    }
    
    @OnError
    public void onError(Throwable cause) {
        System.err.println(svrName+" onError " + cause);
        cause.printStackTrace(System.err);
    }

}
@@ -5242,30 +5242,19 @@
 ognized 
-c
 ur
-EndpointURI
+i
 : %22+endp

tidy up