package clientP2P;

import java.util.Scanner;
import java.util.List;
import clientP2P.ClientManagementUDP;
import clientP2P.ClientManagementTCP;
import serverP2P.ServerManagementUDP;
import serverP2P.ServerManagementTCP;
import tools.Logger;
import tools.LogLevel;
import tools.Directories;
import tools.HostItem;
import tools.ServerPortRange;
import tools.TrackerPortRange;

/** Client + Server implementation.
 * @author	Louis Royer
 * @author	Flavien Haas
 * @author	JS Auge
 * @version	1.0
 */

public class ClientP2P {
	private String logDir = "logs/";
	private String partsDir = ".parts/";
	private Logger loggerServer;
	private Logger loggerClient;
	private Directories directories;
	private HostItem tracker;
	private HostItem server;
	private Scanner scanner;

	/** Initialize loggers if directories and logger are null,
	 * else fail silently.
	 */
	public void initDirectoriesAndLoggers() {
		if (directories == null && loggerServer == null && loggerClient == null) {
			directories = new Directories("P2P_JAVA_PROJECT_" + server.getPort());
			directories.createSubdir(logDir);
			loggerServer = new Logger(directories.getDataHomeDirectory() + logDir + "server.log");
			loggerClient = new Logger(directories.getDataHomeDirectory() + logDir + "client.log");
			directories.createSubdir(partsDir);
		}
	}

	/** Constructor.
	 * @param hostnameServer	hostname to bind
	 * @param portServer	port to bind
	 * @param hostnameTracker	hostname of tracker
	 * @param portTracker	port of tracker
	 */
	public ClientP2P(String hostnameServer, int portServer, String hostnameTracker, int portTracker) {
		scanner = new Scanner(System.in);
		server = new HostItem(hostnameServer, portServer);
		tracker = new HostItem(hostnameTracker, portTracker);
		initDirectoriesAndLoggers();
		System.out.println("Server will listen on port " + portServer + " and serve files from " + directories.getDataHomeDirectory());
		directories.askOpenDataHomeDirectory(null, scanner);
	}

	/** Print cli usage
	 * @param serverPortRange range of server ports
	 * @param trackerPortRange range of tracker ports
	 */
	private static void printUsage(ServerPortRange serverPortRange, TrackerPortRange trackerPortRange) {
		System.out.println("usage :");
		System.out.println("\tjava clientP2P.ClientP2P");
		System.out.println("or");
		System.out.println("java clientP2P.ClientP2P -- " +
			"<clientTransportProtocol> " +
			"<integratedServerHOSTNAME> <integratedServerPORT> " +
			"<trackerHOSTNAME> <trackerPORT> ");
		System.out.println("(" + trackerPortRange + " and " + serverPortRange +")");
	}

	/** Main program entry point.
	 * 1rst parameter is optionnal, and is used to
	 * define port used by the server module to listen. If not provided, default to another port.
	 * @param args	server listenning port
	 */
	public static void main(String [] args) {
		final String defaultHostname = "localhost";
		String hostnameServer = "";
		int portServer = 0;
		String hostnameTracker = "";
		int portTracker = 0;
		String protocolClient = "";
		Scanner scanner = new Scanner(System.in);
		final ServerPortRange serverPortRange = new ServerPortRange();
		final TrackerPortRange trackerPortRange = new TrackerPortRange();

		if ((args.length != 6) && (args.length != 0)) {
			ClientP2P.printUsage(serverPortRange, trackerPortRange);
			System.exit(1);
		}
		else if (args.length == 6) {
				protocolClient = args[1];
				hostnameServer = args[2];
				portServer = Integer.valueOf(Integer.parseInt(args[3]));
				hostnameTracker = args[4];
				portTracker = Integer.valueOf(Integer.parseInt(args[5]));
		} else {
			System.out.println("Client, wich transport protocol do you want to use (default = TCP): ");
			protocolClient = scanner.nextLine();
			System.out.println("server side, enter hostname to bind (default = localhost): ");
			hostnameServer = scanner.nextLine();
			if (hostnameServer.equals("")) {
				hostnameServer = defaultHostname;
				System.out.println("using default hostname : " + hostnameServer);
			}
			System.out.println("enter port (default = " + serverPortRange.getDefaultPort() +"): ");
			String portServerStr = scanner.nextLine();
			if (portServerStr.equals("")) {
				portServer = serverPortRange.getDefaultPort();
				System.out.println("using default port : " + portServer);
			} else {
				portServer = Integer.valueOf(Integer.parseInt(portServerStr));
			}
			System.out.println("enter hostname of tracker (default = localhost): ");
			hostnameTracker = scanner.nextLine();
			if (hostnameTracker.equals("")) {
				hostnameTracker = defaultHostname;
				System.out.println("tracker default hostname : " + hostnameTracker);
			}
			System.out.println("enter tracker's port (default = "+trackerPortRange.getDefaultPort() + "): ");
			String portTrackerStr = scanner.nextLine();
			if (portTrackerStr.equals("")) {
				portTracker = trackerPortRange.getDefaultPort();
				System.out.println("using default port : " + portTracker);
			} else {
				portTracker = Integer.valueOf(Integer.parseInt(portTrackerStr));
			}
		}

		boolean serverInRange = serverPortRange.isPortInRange(portServer);
		boolean trackerInRange = trackerPortRange.isPortInRange(portTracker);

		if (serverInRange && trackerInRange) {
			System.out.println("using hostname : " + hostnameServer);
			System.out.println("using port : " + portServer);
			System.out.println("tracker hostname : " + hostnameTracker);
			System.out.println("tracker port : " + portTracker);
			ClientP2P c = new ClientP2P(hostnameServer, portServer, hostnameTracker, portTracker);
			ServerManagementUDP smudp = new ServerManagementUDP(c.directories.getDataHomeDirectory(), c.server, c.tracker, c.loggerServer);
			ServerManagementTCP smtcp = new ServerManagementTCP(c.directories.getDataHomeDirectory(), c.server, c.tracker, c.loggerServer);
			Thread tudp = new Thread(smudp);
			tudp.setName("server UDP P2P-JAVA-PROJECT");
			tudp.start();
			Thread ttcp = new Thread(smtcp);
			ttcp.setName("server TCP P2P-JAVA-PROJECT");
			ttcp.start();

			// Wait a bit before printing client interface
			// This is not required, but allow to have a cleaner interface
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt();
			}

			Thread tclient;
			switch (protocolClient) {
				case "UDP":
				case "udp":
				case "upd": // to avoid users typos
				case "2" :
					System.out.println("Starting with UDP");
					ClientManagementUDP cmudp = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner, c.server);
					tclient = new Thread(cmudp);
					break;
				case "TCP":
				case "tcp":
				case "1":
				default:
					System.out.println("Starting with TCP");
					ClientManagementTCP cmtcp = new ClientManagementTCP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner, c.server);
					tclient = new Thread(cmtcp);
					break;
			}
			tclient.setName("client P2P-JAVA-PROJECT");
			tclient.start();
			try {
				tclient.join();
			} catch (InterruptedException e) {}
			smudp.setStop();
			smtcp.setStop();
		} else {
			if (!serverInRange) {
				System.err.println("SERVER: Port not in range. " + serverPortRange);
			}
			if (!trackerInRange) {
				System.err.println("TRACKER: Port not in range. " + trackerPortRange);
			}
			System.exit(2);
		}
	}
}
