package vs;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * threaded server for var.sockets.tcp.filer File service. waits for clients to
 * connect. Upon connection sends concurrently to other client connections a
 * manually defined file back to client. closes the connection directly
 * afterwards.
 *
 * @author Sandro Leuchter
 *
 */
public class FileServerThreadPool {
	/**
	 * path to file which will be sent to clients; relative to current working
	 * directory (e.g. project root)
	 */
	private static final String FILE = "target/classes/message.txt";
	/**
	 * port on which this service is currently listening on localhost
	 */
	private final int port;
	/**
	 * thread pool of this server
	 */
	private final Executor threadPool;

	/**
	 * the only constructor for this class
	 *
	 * @param port port on which this service will be listening on localhost
	 */
	public FileServerThreadPool(int port) {
		this.port = port;
		this.threadPool = Executors.newSingleThreadExecutor();
		// threadPool = Executors.newCachedThreadPool();
		// threadPool =
		// Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2);
	}

	/**
	 * creates server socket on localhost:port, infinitely handles connections to
	 * clients concurrently
	 */
	public void start() {
		try (ServerSocket serverSocket = new ServerSocket(this.port)) {
			System.out.println("FileServer (threaded) auf " + serverSocket.getLocalSocketAddress() + " gestartet ...");
			File file = new File(FILE);
			if (file.exists()) {
				System.out.println("\"" + file.getAbsolutePath() + "\" soll gesendet werden.");
				while (true) {
					Socket socket = serverSocket.accept();
					this.threadPool.execute(new FileThread(socket));
				}
			}
		} catch (IOException e) {
			System.err.println(e);
		}
	}

	/**
	 * Each connection is handled with an instance of this class.
	 */
	private class FileThread implements Runnable {
		/**
		 * TCP connection to client
		 */
		private final Socket socket;

		/**
		 * the only constructor for this class
		 *
		 * @param socket the individual socket that the server created on accepting a
		 *               client that this EchoThread instance will be communicating with
		 */
		public FileThread(Socket socket) {
			this.socket = socket;
		}

		/**
		 * defines the behavior of this Thread instance, will be executed concurrently
		 * if start() is called on instance
		 *
		 */
		@Override
		public void run() {
			SocketAddress socketAddress = null;
			try (BufferedReader in = new BufferedReader(new FileReader(FILE));
					PrintWriter out = new PrintWriter(this.socket.getOutputStream(), true)) {
				socketAddress = this.socket.getRemoteSocketAddress();
				System.out.println("Verbindung zu  " + socketAddress + " aufgebaut");
				// Inhalt von in zeilenweise an out senden:
				System.out.println("Übertragung zu " + socketAddress + " begonnen");
				String input;
				while ((input = in.readLine()) != null) {
					out.println(input);
					// Thread.sleep(1000);
				}
				System.out.println("Übertragung zu " + socketAddress + " beendet");
			} catch (Exception e) {
				System.err.println(e);
			} finally {
				System.out.println("Verbindung zu  " + socketAddress + " abgebaut");
			}
		}
	}

	/**
	 * main method: entrypoint to run service
	 *
	 * @param args args[0] must be the port number of the server (int); rest of args
	 *             is ignored
	 */
	public static void main(String[] args) {
		int port = Integer.parseInt(args[0]);
		new FileServerThreadPool(port).start();
	}
}
