package lu.tudor.santec.gecamed.core.gui.launcher;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Properties;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.RollingFileAppender;

/**
 * The GECAMedLauncher allows you to control the GECAMed with external programs, it allows to start GECAMed with parameters 
 * and during the run-time forward parameters. These parameters are divided up into commands and command parameters.
 * The GECAMedLauncher class try to make a Handshaking with the GECAMedLauncherServer inside GECAMed to send parameters over a 
 * socket connection.
 * Important: The GECAMedLauncher has to export as a jar file with the class GECAMedLauncherCommand.java to run.
 * 
 * @author Thorsten Roth thorsten.roth(at)tudor.lu
 *
 * @version
 * <br>$Log: GECAMedLauncher.java,v $
 * <br>Revision 1.7  2013-07-15 06:18:37  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.6  2013-01-22 14:36:22  troth
 * <br>New version 1.5 of GECAMedLauncher.
 * <br>It now have a user.conf file to set the server to which the user want to connect.
 * <br>
 * <br>Revision 1.5  2013-01-10 12:54:46  ferring
 * <br>system exit method changed
 * <br>
 * <br>Revision 1.4  2012-11-29 16:17:27  troth
 * <br>GECAMedLauncherServer 1.5 fix a little bug.
 * <br>
 * <br>Revision 1.3  2012-11-28 16:41:03  troth
 * <br>Version 1.3 of GECAMedLauncher and GECAMedLauncherServer 1.5.
 * <br>Some fixings in the LoginScreen.
 * <br>
 * <br>Revision 1.2  2012-10-31 16:40:47  troth
 * <br>Version 1.1 of GECAMed launcher.
 * <br>
 * <br>Revision 1.1  2012-10-19 16:24:42  troth
 * <br>First version of GECAMed launcher.
 * <br>
 */
public class GECAMedLauncher {

	/**
	 * Saves the launchers parameters
	 */
	public static final String[] args = null;
	
	/**
	 * local config file
	 */
	private static final String CONFIG_FILE = "config/GECAMedLaucherServer.conf";
	
	/**
	 * user config file
	 */
	private static final String USER_CONFIG_FILE = "user.conf";
	
	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(GECAMedLauncher.class.getName());
	
	/**
	 * used logfile 
	 */
	private static final File LOGFILE = new File("GECAMedLauncher.log");
	
	/**
	 * stores the local config settings
	 */
	private static Properties serverConfig = new Properties();
//	
//	/**
//	 * stores the user config settings
//	 */
//	private static Properties userConfig = new Properties();
	
	/**
	 * stores all start commands for GECAMedLauncher
	 */
	private static HashMap<String, GECAMedLauchnerCommand> commands = new HashMap<String, GECAMedLauchnerCommand>();
	
	/**
	 * @param args
	 */
	public static void main(String[] args)
	{
		logger.info("Start GECAMedLaucher 1.5 :)");
		// init logger
		initLogger();
		// load the config
		loadConfig();
		// ini commands
		initCommands();
		
		// check parameters if they are right
		StringBuilder params = new StringBuilder();
		logger.info("Check if the parameters are correct.");
		if(args.length > 0)
		{
			for(String param : args)
			{
				String[] paramLine = param.split(":");
				if(!commands.containsKey(paramLine[0].toUpperCase()))
				{
					logger.warn("Error: " + paramLine[0] + " is not a GECAMed Launcher Command!");
					System.out.println("Please use one or a combination of the following:\n");
					for (GECAMedLauchnerCommand command : commands.values())
					{
						System.out.println(command.getDescription()+"\n");
					}
					System.exit(1);
				}
				params.append(param + " ");
			}
		}
		/////////////////////////////////////////////////////
		// We are locking if GECAMed is running already
		/////////////////////////////////////////////////////
		// get ip and port form config file
		
		String userpath = ((String) serverConfig.get("USER_PATH"));
		if(userpath == null) logger.info("USER_PATH not set.");
		
		// load user config here and overwrite serverConfig values.
		if (userpath != null) {
			String userConfigPath = userpath + USER_CONFIG_FILE;
			logger.info("try to load user.conf: " + userConfigPath);
			if (new File(userConfigPath).exists()) {
				try {
					serverConfig.load(new FileInputStream(userConfigPath));
					logger.info("Loaded User Config From: " + userConfigPath);
				} catch (Exception e) {
					logger.error("Error: Can't load user config file: " + userConfigPath, e);
					System.exit(1);
				}
			} else {
				logger.warn("File not exists: " + userConfigPath);
			}
		}
		
		printConf();
		
		
		String serverNameLocal = ((String) serverConfig.get("SERVER_NAME_LOCAL"));
		if(serverNameLocal == null)
		{
			logger.error("SERVER_NAME_LOCAL not set, in file " + CONFIG_FILE + "!");
			System.exit(1);
		}
		int serverPort = Integer.parseInt((String) serverConfig.get("SERVER_PORT"));
		if(((String) serverConfig.get("SERVER_PORT")) == null)
		{
			logger.error("SERVER_PORT not set, in file " + CONFIG_FILE + "!");
			System.exit(1);
		}
		String serverNameGECAMed = ((String) serverConfig.get("SERVER_NAME_GECAMED"));
		if(serverNameGECAMed == null)
		{
			logger.warn("SERVER_NAME_GECAMED not set, in file " + CONFIG_FILE + "!");
		}
		String javaPath = ((String) serverConfig.get("JAVA_PATH"));
		if(javaPath == null)
		{
			logger.error("JAVA_PATH not set, in file " + CONFIG_FILE + "!");
			System.exit(1);
		}
				
		if(serverNameGECAMed == null) {
			logger.error("SERVER_NAME_GECAMED not set, in file " + CONFIG_FILE + " and not set in file " + USER_CONFIG_FILE + "!");
			System.exit(1);
		}

		// Try to open connection to gecamed
		logger.info("Try to open connection to gecamed ...");
		Socket socket = null;
		try {
			socket = new Socket(serverNameLocal, serverPort);
		} catch (UnknownHostException e1) {
			logger.info("waiting for startup GECAMed ...");
//			e1.printStackTrace();
		} catch (IOException e1) {
			logger.info("waiting for startup GECAMed ...");
//			e1.printStackTrace();
		}
		
		/////////////////////////////////////////////////////
		// If GECAMed is not running start GECAMed
		/////////////////////////////////////////////////////
		if(socket == null)
		{
			logger.info("try to start GECAMed ...");
			Process gecamedProcess = null;
			try {
				logger.info("Java path: " + javaPath);
				logger.info("try to start server @: " + javaPath + "javaws http://"+ serverNameGECAMed +":8080/gecamed/GECAMed.jnlp");
				gecamedProcess = Runtime.getRuntime().exec(javaPath + "javaws http://"+ serverNameGECAMed +":8080/gecamed/GECAMed.jnlp");
			} catch (IOException e) {
				logger.warn("Can't start GECAMed !!!");
				e.printStackTrace();
				System.exit(-1);
			}
			
			try {
				gecamedProcess.waitFor();
//				System.out.println("ExitValue: " + gecamedProcess.exitValue());
				logger.info("GECAMed starts ...");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			// try to open connection to gecamed again
			logger.info("Try to open connection to gecamed again ...");
			while(socket == null)
			{
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
					logger.info("GECAMed Launcher can't sleep");
				}
				try {
					socket = new Socket(serverNameLocal, serverPort);
				} catch (UnknownHostException e1) {
					logger.info("waiting for startup GECAMed ...");
//					e1.printStackTrace();
				} catch (IOException e1) {
					logger.info("waiting for startup GECAMed ...");
//					e1.printStackTrace();
				}
			}
			
		}
		/////////////////////////////////////////////////////
		// Send GECAMed the parameters
		/////////////////////////////////////////////////////
		logger.info("Send GECAMed the parameters");
		// Anfrage an Server
		if(socket != null)
		{
			PrintWriter serverRequest = null;
			try {
				serverRequest = new PrintWriter(socket.getOutputStream(), true);
			} catch (IOException e) {
				logger.warn("Error: Server request not successfull!\n" + e.getStackTrace());
				e.printStackTrace();
			}
			
			// start launcher call with or without parameters
			if(args.length > 0)
			{	
				serverRequest.println("execute=" + params.toString());
			}else{
				// if launcher is called with no parameter
				serverRequest.println("execute=");
			}
			// Server answer
			
			InputStreamReader PortLeser = null;
			try {
				PortLeser = new InputStreamReader(socket.getInputStream());
			} catch (IOException e) {
				logger.warn("Error: GECAMedLauncherServer not answer!\n" + e.getStackTrace());
				e.printStackTrace();
			}
		
			// Eingabe = Entrée
			BufferedReader Eingabe = new BufferedReader(PortLeser);
			try {
				if(Eingabe.readLine().equalsIgnoreCase("ok"))
				{
					logger.info("Parameters are send.");
				}else{
					logger.info("Error: Parameter sending. Server result: " + Eingabe.readLine());
				}
			} catch (IOException e) {
				logger.warn("Error: Can not read answer from server!\n" + e.getStackTrace());
				e.printStackTrace();
			}
		}
		logger.info("GECAMed successfully launched, GECAMedLauncher ende now ;)");
	}

	
	private static void printConf() {
		try {
			StringBuffer sb = new StringBuffer("Using Config:\n");
			for (Object key : serverConfig.keySet()) {
				sb.append("\t").append(key).append("=").append(serverConfig.get(key)).append("\n");
			}
			logger.info(sb.toString());			
		} catch (Exception e) {
			logger.warn("Error printing Config", e);
		}
	}


	/**
	 * load the configuration from the config file
	 */
	private static void loadConfig() {
		serverConfig.clear();
		try {
		    serverConfig.load(new FileInputStream(CONFIG_FILE));
		} catch (Exception e) {
			logger.error("Error: Can't load config file: " + CONFIG_FILE, e);
			System.exit(1);
		} 
	}
	
	
	/**
	 * init the startup commands to launch GECAMed
	 */
	private static void initCommands()
	{
		/** key = command | value = GECAMedLauncherCommand (name and description) */
		
		/** LOGIN:<username>:<userpasswort> */
		commands.put("LOGIN", new GECAMedLauchnerCommand("LOGIN", "LOGIN:<username>:<userpasswort>"));
		
		/** PATIENTSEARCH:<searchterm> */
		commands.put("PATIENTSEARCH", new GECAMedLauchnerCommand("PATIENTSEARCH", "PATIENTSEARCH:<searchterm>"));
		
		/** PATIENTMATRICULESEARCH:<socialsecuritynumber> **/
		commands.put("PATIENTMATRICULESEARCH", new GECAMedLauchnerCommand("PATIENTMATRICULESEARCH", "PATIENTMATRICULESEARCH:<socialsecuritynumber>"));
		
		/** GOTOMODULE:<modulname> **/
		commands.put("GOTOMODULE", new GECAMedLauchnerCommand("GOTOMODULE", "GOTOMODULE:<modulname>"));
		
		/** OPENPATIENTBYID:<gecamedpatientid> **/
		commands.put("OPENPATIENTBYID", new GECAMedLauchnerCommand("OPENPATIENTBYID", "OPENPATIENTBYID:<gecamedpatientid>"));
		
		/** OPENPATIENTBYNAME:<patientname> **/
		commands.put("OPENPATIENTBYNAME", new GECAMedLauchnerCommand("OPENPATIENTBYNAME", "OPENPATIENTBYNAME:<patientname"));
		
		/** OPENPATIENTBYMATRICULE:<socialsecuritynumber> **/
		commands.put("OPENPATIENTBYMATRICULE", new GECAMedLauchnerCommand("OPENPATIENTBYMATRICULE", "OPENPATIENTBYMATRICULE:<socialsecuritynumber>"));
		
		/** OPENPATIENTBYKIS:<kisid> **/
		commands.put("OPENPATIENTBYKIS", new GECAMedLauchnerCommand("OPENPATIENTBYKIS", "OPENPATIENTBYKIS:<kisid>"));
	}
	
	/**
	 * initialize the logger
	 */
	private static void initLogger() 
	{
		try
		{
			// init the log4j logger
			Logger.getRootLogger().removeAllAppenders();
			
			// define the logging layouts
			Layout layout = new PatternLayout("%d{yyyy-MM-dd hh:mm:ss,SSS} %c %M (line %L)%n%p: %m%n");
			
			// set the logger
			Logger.getRootLogger().addAppender(new ConsoleAppender(layout));
			
			RollingFileAppender fileAppender = new RollingFileAppender(
					layout,
					LOGFILE.getAbsolutePath(),
					true);
			fileAppender.setMaxFileSize("5MB");
			fileAppender.setMaxBackupIndex(3);
			fileAppender.setEncoding("UTF-8");
			BasicConfigurator.configure(fileAppender);
			
			// define level for logging levels
			Logger.getRootLogger().setLevel(Level.INFO);
		}
		catch (Exception e)
		{
			logger.log(Level.ERROR, "Error initializing Logger", e);
			System.err.println("Error initializing Logger");
			e.printStackTrace();
		}
	}
}
