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

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;

import javax.swing.JOptionPane;

import lu.tudor.santec.gecamed.core.gui.GECAMedOptionPane;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.Patient;
import lu.tudor.santec.gecamed.patient.ejb.entity.beans.PatientStub;
import lu.tudor.santec.gecamed.patient.ejb.session.beans.PatientAdminBean;
import lu.tudor.santec.gecamed.patient.ejb.session.interfaces.PatientAdminInterface;
import lu.tudor.santec.gecamed.patient.gui.PatientListModule;
import lu.tudor.santec.gecamed.patient.gui.PatientManagerModule;
import lu.tudor.santec.gecamed.patient.gui.patientlist.PatientSearchSettingsPlugin;

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;


/**
 * @author Thorsten Roth thorsten.roth(at)tudor.lu
 *
 * @version
 * <br>$Log: GECAMedLauncherServer.java,v $
 * <br>Revision 1.5  2013-12-05 16:38:51  ferring
 * <br>Throwables catching changed to get more output about errors
 * <br>
 * <br>Revision 1.4  2013-07-15 06:18:37  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.3  2013-01-10 12:54:46  ferring
 * <br>system exit method changed
 * <br>
 * <br>Revision 1.2  2013-01-10 09:43:11  ferring
 * <br>error catching and logging before closing improved
 * <br>
 * <br>Revision 1.1  2012-11-28 16:41:04  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 GECAMedLauncherServer {

	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(GECAMedLauncherServer.class.getName());
	
	/**
	 * used logfile 
	 */
	private static final File LOGFILE = new File("GECAMedLauncherServer.log");
	
	/**
	 * used lock-file while server is running
	 */
	private static final File LOCKFILE = new File("GECAMedLauchnerServer.isrunning");

	/**
	 * used config file
	 */
	private static final String CONFIG_FILE = "config/GECAMedLaucherServer.conf";
	
	/**
	 * user config file
	 */
	private static final String USER_CONFIG_FILE = "user.conf";
	
	/**
	 * stores the serverconfig
	 */
	private static Properties serverConfig = null;
	
	/**
	 * management server for client commands
	 */
	private ManagementServer mServer;

	private static final String APP_NAME = "GECAMedLaucher Server";
	
	private static String username = null;
	private static String password = null;
	private static String[] commands = null;
	
	private static Boolean gecamedStartup = true;
	
	public GECAMedLauncherServer()
	{

	}
	
	/**
	 * Main class to run the GECAMedLauncherServer
	 * @param args
	 */
	public static void main(String[] args)
	{
		if (args != null && args.length > 0)
		{
			// start server
			if ("start".equals(args[0].toLowerCase()))
			{
				GECAMedLauncherServer server = new GECAMedLauncherServer();
				server.startServer();
				while (LOCKFILE.exists()) {
					try {
						Thread.sleep(1000);
					} catch (Exception e) {}
				}
				server.stopServer();
				System.out.println("Exiting " + APP_NAME + " ....BYE BYE");
			}
			// stop server
			else if ("stop".equals(args[0].toLowerCase()))
			{
				LOCKFILE.delete();
			} else {
			    System.out.println("Help: run " + APP_NAME + " with parameter start/stop");			    
			}
		}else {
		    System.out.println("Help: run " + APP_NAME + " with parameter start/stop");			    
		}

	}
	
	/**
	 * starts the GECAMedLauncher server
	 */
	public void startServer()
	{

		// create the lock file
		try {
			LOCKFILE.createNewFile();
			LOCKFILE.deleteOnExit();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// set up the logging
		initLogger();
		
		logger.info("\r\n" +
				"#######################################################\r\n" +
				"#\r\n" + 
				"#  STARTING " + APP_NAME + "\r\n" +
				"#\r\n" + 		
				"#######################################################");
		
		
		// load the server config file
		loadConfig();
		
		// Read config file for server values
		int port = Integer.parseInt((String) serverConfig.get("SERVER_PORT"));
		int timeOut =  Integer.parseInt((String) serverConfig.get("SERVER_TIMEOUT"));
		
		//create a management Server
		mServer = new ManagementServer(APP_NAME, port, timeOut);
				
		// Register commands
		mServer.registerCommand(new ServerCommand("execute", "Execute a GECAMedLauncher Command, returns OK if successful")
		{
			@Override
			public String runCommand(String params)
			{
				System.out.println("Params: " + params);
				logger.info("Send params: " + params);
				if(!params.equalsIgnoreCase(""))
				{
					commands = params.split(" ");
					String[] loginCommand = commands[0].split(":");
					logger.info("Login command: " + loginCommand);
					username = loginCommand[1];
					password = loginCommand[2];
					
					System.out.println("... wait for gecamed is ready ...");
					logger.info("... wait for gecamed is ready ...");
					while(gecamedStartup)
					{
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							System.out.println("GECAMed Launchserver can't sleep");
							logger.info("GECAMed Launchserver can't sleep");
							e.printStackTrace();
						}
					}
					System.out.println("GECAMed is ready!!!");
					logger.info("GECAMed is ready!!! Run commands ...");
					// run commands in GECAMed
					Integer openPatient = 0;
					for(int i=1; i < commands.length; i ++)
					{
						String[] command = commands[i].split(":");
						
						System.out.println("try commamd:" + commands[i]);
						logger.info("try commamd:" + commands[i]);
						if(command[0].equalsIgnoreCase("PATIENTSEARCH"))
						{
							MainFrame.getInstance().selectModule("PatientList");
							PatientListModule.getInstance().patientListPanel.searchPatient(commands);
							MainFrame.getInstance().setToFront();
						}
						if(command[0].equalsIgnoreCase("PATIENTMATRICULESEARCH"))
						{
							MainFrame.getInstance().selectModule("PatientList");
							PatientListModule.getInstance().patientListPanel.searchPatient(commands);
							MainFrame.getInstance().setToFront();
						}
						if(command[0].equalsIgnoreCase("GOTOMODULE"))
						{
							if(openPatient != 2)
							{
								String moduleName = command[1]; 
								MainFrame.getInstance().selectModule(moduleName);
								MainFrame.getInstance().setToFront();
							}
						}
						if(command[0].equalsIgnoreCase("OPENPATIENTBYID"))
						{
							Integer patientId = null; 
							patientId = new Integer(command[1]);
							
							if(patientId != null)
							{
								openPatient = 1;
								PatientManagerModule.getInstance().loadPatient(patientId);
							}
							MainFrame.getInstance().setToFront();
						}
						if(command[0].equalsIgnoreCase("OPENPATIENTBYNAME"))
						{
							PatientAdminInterface patientInterface = (PatientAdminInterface) ManagerFactory.getRemote(PatientAdminBean.class);
							Integer limit = (Integer) PatientListModule.getInstance().patientListSettingsPlugin.getValue(PatientSearchSettingsPlugin.SEARCH_LIMIT);
							try {
//								ArrayList<PatientStub> patientList = new ArrayList<PatientStub>(patientInterface.getPatientStubListBySearchString(searchNameText, searchAddressText, filter, limit)));
								ArrayList<PatientStub> patientList = new ArrayList<PatientStub>(patientInterface.getPatientStubListBySearchString(command[1], "", "", limit));
								if(patientList.size() == 1)
								{
									openPatient = 1;
									PatientManagerModule.getInstance().loadPatient(patientList.get(0).getId());
								}else{
									openPatient = 2;
									MainFrame.getInstance().selectModule("PatientList");
									String[] searchCommand = {"PATIENTSEARCH:" + command[1]};
									PatientListModule.getInstance().patientListPanel.searchPatient(searchCommand);
								}
									
							} catch (Exception e) {
								logger.warn("Error OPENPATIENTBYNAME:\n" + e.getStackTrace());
								e.printStackTrace();
							}
							MainFrame.getInstance().setToFront();
						}
						if(command[0].equalsIgnoreCase("OPENPATIENTBYMATRICULE"))
						{
							PatientAdminInterface patientInterface = (PatientAdminInterface) ManagerFactory.getRemote(PatientAdminBean.class);
							Integer limit = (Integer) PatientListModule.getInstance().patientListSettingsPlugin.getValue(PatientSearchSettingsPlugin.SEARCH_LIMIT);
							try {
								ArrayList<PatientStub> patientList = new ArrayList<PatientStub>(patientInterface.getPatientStubListBySSec(command[1], limit));
								if(patientList.size() == 1)
								{
									openPatient = 1;
									PatientManagerModule.getInstance().loadPatient(patientList.get(0).getId());
								}else{
									openPatient = 2;
									MainFrame.getInstance().selectModule("PatientList");
									String[] searchCommand = {"PATIENTMATRICULESEARCH:" + command[1]};
									PatientListModule.getInstance().patientListPanel.searchPatient(searchCommand);
								}
									
							} catch (Exception e) {
								logger.warn("Error OPENPATIENTBYMATRICULE:\n" + e.getStackTrace());
								e.printStackTrace();
							}
							MainFrame.getInstance().setToFront();
						}
						if(command[0].equalsIgnoreCase("OPENPATIENTBYKIS"))
						{
//							System.out.println("Command OPENPATIENTBYKIS is not available !!!");
							PatientAdminInterface patientInterface = (PatientAdminInterface) ManagerFactory.getRemote(PatientAdminBean.class);
							Integer limit = (Integer) PatientListModule.getInstance().patientListSettingsPlugin.getValue(PatientSearchSettingsPlugin.SEARCH_LIMIT);
							try {
								ArrayList<Patient> patientList = new ArrayList<Patient>(patientInterface.getPatientByRisID(command[1], limit));
								if(patientList.size() == 1)
								{
									openPatient = 1;
									PatientManagerModule.getInstance().loadPatient(patientList.get(0).getId());
								}else{
									openPatient = 2;
//									MainFrame.getInstance().selectModule("PatientList");
//									String[] searchCommand = {"PATIENTMATRICULESEARCH:" + command[1]};
//									PatientListModule.getInstance().patientListPanel.searchPatient(searchCommand);
//									GECAMedOptionPane
									if(patientList.size() > 0)
									{
										String msg = "KIS-ID " + command[1] + " found several times:\n";
										for (Patient patient : patientList) {
											msg = msg + "Patient: " + patient.getId() + " | " + patient.getFirstName() + " " + patient.getSurName() + " | " + patient.getIdRIS() + "\n";
										}
										
										GECAMedOptionPane.showMessageDialog(MainFrame.getInstance(),
												msg,
												"WARNING: Open Patient by KIS-ID",
												JOptionPane.WARNING_MESSAGE);
//										JOptionPane.showMessageDialog(frame,
//											    "Eggs are not supposed to be green.",
//											    "Inane warning",
//											    JOptionPane.WARNING_MESSAGE);
//												//GECAMedOptionPane.ICON_QUESTION);
									}else{
										GECAMedOptionPane.showMessageDialog(MainFrame.getInstance(),
												"NO PATIENT WITH KIS-ID: " + command[1] + " FOUND",
												"WARNING: Open Patient by KIS-ID",
												JOptionPane.WARNING_MESSAGE);
												//GECAMedOptionPane.ICON_QUESTION);
									}
								}
									
							} catch (Exception e) {
								logger.warn("Error OPENPATIENTBYKIS:\n" + e.getStackTrace());
								e.printStackTrace();
							}
							MainFrame.getInstance().setToFront();
						}
						
					}
					
				}else{
					// if launcher is called with no parameter
					System.out.println("no params are set !!!");
					logger.info("GECAMedLauncher is called with no parameter !!!");
					username = "";
					password = "";
					commands = new String[1];
					commands[0] = "";
				}
			    return "OK";
			}
		});
		
		// start the management server
		mServer.start();
		
		// add a shutdown hook to stop the server
		MainFrame.addShutdownHook("stop launcher", new Thread()
		{
        	public void run()
        	{
        		try {
        		    logger.info(APP_NAME + " Shutdown hook: Stopping server");
        		    stopServer();
        		} catch (Throwable e) {
        			logger.warn("Can not stopping server!\n", e);
        		}
        	}
        });
		
	}
	
	/**
	 * stops the GECAMedLauncher server
	 */
	public void stopServer() 
	{		
		try {
		    mServer.stop();
		} catch (Exception e) {
		    e.printStackTrace();
		}
		
		LOCKFILE.delete();
		
		logger.info("\r\n" +
				"#######################################################\r\n" +
				"#\r\n" + 
				"# " + APP_NAME + " STOPPED" + "\r\n" +
				"#\r\n" + 		
				"#######################################################");
	}
	
	/**
	 * initialize the loggers
	 */
	private 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);
			Logger.getLogger("org.apache.fop").setLevel(Level.ERROR);
			Logger.getLogger("net.sf.jasperreports").setLevel(Level.ERROR);
		}
		catch (Exception e)
		{
			logger.log(Level.ERROR, "Error initializing Logger", e);
		}
	}
	
	/**
	 * load the Server configuration
	 */
	private void loadConfig()
	{	
		try {
			serverConfig = new Properties();
			// load the properties from the server config file
		    serverConfig.load(new FileInputStream(CONFIG_FILE));
			// check if the path to the user-specific configuration has been set in the server properties
		    String userpath = ((String) serverConfig.get("USER_PATH"));
			if((userpath != null) && (userpath.length() > 0)){
				// indeed, so overload (and extend) the server configuration
				String userConfigPath = userpath + USER_CONFIG_FILE;
				serverConfig.load(new FileInputStream(userConfigPath));
			}
		} catch (Throwable e) {
			logger.warn("Unable to load server config file!\n" + e.getStackTrace());
		} 
		
		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);
		}
	}
	
	public String getUsername()
	{
		return username;
	}
	
	public String getPassword()
	{
		return password;
	}
	
	public String[] getCommands()
	{
		return commands;
	}
	
	public void setGECAMedStartup(Boolean startup)
	{
		gecamedStartup = startup;
	}
}