/*
 * Copyright 2013-2014 Splunk, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"): you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Util {
    // Port that we are going to use for testing.
    public static final int port = 15000;

    // Timeout for all TCP connection and reading waits.
    public static final int timeoutInMs = 500;

    // A container that we can put a string in and use wait/notify on.
    public static class StringContainer {
        public String value = null;
    }

    /**
     * Asynchronously read a line from a TCP port or time out.
     *
     * This method immediately returns a StringContainer object with its value set to <code>null</code>.
     * It then listens on TCP port <code>port</code>. If a line arrives on that port within <code>timeoutInMs</code>
     * milliseconds, its sets that line (minus the terminating newline) as the value of the returned
     * StringContainer and calls notifyAll on the StringContainer. If no line as arrived after the timeout
     * expires, it calls notifyAll but leaves the value <code>null</code>.
     *
     * A user of the method should call it something like this:
     *
     *     StringContainer container = readLineFromPort(10000, 500);
     *
     *     // Write something to port 10000
     *
     *     synchronized (container) { container.wait(); }
     *     if (container.value != null) {
     *         // Do something with the read line
     *     } else {
     *         // Timed out
     *     }
     *
     * @param port Port to listen on.
     * @param timeoutInMs How many milliseconds to wait for a line.
     * @return a StringContainer to wait on.
     */
    public static StringContainer readLineFromPort(final int port, final int timeoutInMs) {
        final StringContainer container = new StringContainer();

        new Thread(new Runnable() {
            @Override
            public void run() {
                ServerSocket serverSocket = null;
                Socket socket = null;

                try {
                    serverSocket = new ServerSocket(port);
                    serverSocket.setSoTimeout(timeoutInMs);
                    socket = serverSocket.accept();
                    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    container.value = in.readLine();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (socket != null) try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    if (serverSocket != null) try {
                        serverSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    synchronized (container) {
                        container.notifyAll();
                    }
                }
            }
        }).start();

        return container;
    }
}
