/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.core.gui;

import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import lu.tudor.santec.gecamed.core.gui.widgets.hotkeys.HotkeySettingsExtension;
import lu.tudor.santec.gecamed.core.gui.widgets.hotkeys.HotkeySettingsPlugin;
import lu.tudor.santec.gecamed.core.utils.GECAMedUtils;
import lu.tudor.santec.gecamed.patient.gui.PatientListModule;
import lu.tudor.santec.gecamed.patient.gui.PatientManagerModule;
import lu.tudor.santec.gecamed.usermanagement.gui.AdminModule;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * Manager class to maintain all core and enabled optional GECAMedModules.<br/>
 * All modules are stored in a static hashmap.
 * 
 * 
 * @author martin.heinemann@tudor.lu
 * 04.12.2007
 * 15:53:50
 *
 *
 * @version
 * <br>$Log: ModuleManager.java,v $
 * <br>Revision 1.31  2013-12-27 18:09:26  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.30  2013-07-15 06:18:35  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.29  2013-03-04 13:30:14  ferring
 * <br>user will be notified, if a module wasn't loaded
 * <br>
 * <br>Revision 1.28  2013-02-19 12:07:34  ferring
 * <br>GECAMedLists changed. Will now automatically load list of all beans
 * <br>
 * <br>Revision 1.27  2012-10-19 16:24:42  troth
 * <br>First version of GECAMed launcher.
 * <br>
 * <br>Revision 1.26  2011-08-17 08:32:51  ferring
 * <br>small changes, mainly formal
 * <br>
 * <br>Revision 1.25  2010-10-19 14:37:13  troth
 * <br>rename class SimpleViewPanel in OverviewPanel
 * <br>
 * <br>Revision 1.24  2010-09-01 07:59:35  gbosch
 * <br>bug fixed, loading modules (ferringj)
 * <br>
 * <br>Revision 1.23  2010-08-27 08:14:31  ferring
 * <br>You can now enter an empty string of modules as arguments, to load no module
 * <br>
 * <br>Revision 1.22  2010-08-26 14:28:04  ferring
 * <br>additional options for main arguments added
 * <br>
 * <br>Revision 1.21  2010-06-09 13:01:07  ferring
 * <br>just a unnecessary import removed
 * <br>
 * <br>Revision 1.20  2010-06-09 12:58:12  ferring
 * <br>loaded modules needn't be shown anymore
 * <br>=> override the method isShown in the GECAMedModule (default is true)
 * <br>
 * <br>Revision 1.19  2010-06-09 09:51:43  ferring
 * <br>Permission and role added to view the form editor
 * <br>
 * <br>Revision 1.18  2010-03-17 11:39:58  hermen
 * <br>fixed logging
 * <br>
 * <br>Revision 1.17  2009-10-07 13:07:18  hermen
 * <br>added isVisit to waitingroom entry
 * <br>
 * <br>Revision 1.16  2009-01-22 07:35:51  hermen
 * <br>improved startup logging and progress dialog
 * <br>
 * <br>Revision 1.15  2008-11-18 11:07:05  hermen
 * <br>changed PhysicianChooser for Maisons médicales
 * <br>
 * <br>Revision 1.14  2008-10-07 09:34:37  heinemann
 * <br>translations of roles and rights;
 * <br>show only  roles and rights of the activated modules in the usermanagement section
 * <br>
 * <br>Revision 1.13  2008-10-02 12:49:15  hermen
 * <br>improved logging
 * <br>
 * <br>Revision 1.12  2008-09-25 09:43:07  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.11  2008-09-24 11:54:08  heinemann
 * <br>added first run actions to gecamed. will disable all views except the ones of the admin tab for user, office and physician.
 * <br>Tabs are enabled, if an physican was saved.
 * <br>
 * <br>Revision 1.10  2008-09-24 09:11:52  heinemann
 * <br>added first run actions to gecamed. will disable all views except the ones of the admin tab for user, office and physician.
 * <br>Tabs are enabled, if an physican was saved.
 * <br>
 * <br>Revision 1.9  2008-05-22 11:49:21  hermen
 * <br>added MUCH logger statements for debugging
 * <br>
 * <br>Revision 1.8  2008-05-09 13:22:58  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.7  2008-04-11 12:24:40  weitz
 * <br>*templatemodule finished, renamed commented /few little design bugs
 * <br>*patientmodule, create new letter dialog created
 * <br>
 * <br>Revision 1.6  2008-04-09 07:19:13  weitz
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.5  2008-04-08 13:54:48  weitz
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.4  2008-02-27 08:23:22  heinemann
 * <br>changed iterator to foreach
 * <br>
 * <br>Revision 1.3  2008-02-26 09:15:46  hermen
 * <br>added initModule() to GECAMedModule, which is called after all modules have been instanciated
 * <br>
 * <br>Revision 1.2  2008-01-22 13:51:30  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.1  2007-12-04 16:03:43  heinemann
 * <br>moved module maintainance from MainFrame to ModuleManager
 * <br>
 *   
 */
public class ModuleManager {
	
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(ModuleManager.class.getName());
	
	/**
	 * 
	 */
	private static LinkedHashMap<String, GECAMedModule> modules = new LinkedHashMap<String, GECAMedModule>();
	protected static ModuleManagerPlugin moduleManagerPlugin = new ModuleManagerPlugin();
	
	
	
	/**
	 * Loads all registerd modules
	 * 
	 * @return a list of modules, that couldn't be loaded
	 */
	public static List<String> loadModules() {
		/* ================================================== */
		List<String> errorModules	= new LinkedList<String>();
		
		/* ------------------------------------------------------- */
		// create all enabled optional modules
		/* ------------------------------------------------------- */
		String[] enabledModules = null;
		
		try {
			String[] commands = LoginScreen.getInstance().commandlineArgs;
	//		boolean found = false;
			if (commands != null) {
			    // arguments tell which modules to load
				for (int i = 0; i < commands.length; i++) {
				    String command = commands[i];
				    String[] commandline = command.split(":");
				    if (commandline[0].equalsIgnoreCase("MODULELIST")) {
				    	String[] array = command.split(":");
				    	if (array.length > 1) {
				    		enabledModules = array[1].split(";");
				    	} else {
				    		enabledModules = new String[0];
				    	} 
				    	break;
				    }
				}
			} 
		} catch (Exception e) {
			e.printStackTrace();
			enabledModules = null;
		}
		
		if (enabledModules == null) {
			enabledModules = moduleManagerPlugin.getEnabledModules().split(" ");
		}
		
		/* ------------------------------------------------------- */
		// load rights for all modules
		/* ------------------------------------------------------- */
		for (int i = 0; i < enabledModules.length; i++) {
			/* ------------------------------------------------------- */
			try {
				/* ------------------------------------------------------- */
				if (enabledModules[i] == null || enabledModules[i].equals(""))
					continue;
				/* ------------------------------------------------------- */
				// use reflections to load the permissions of each module
				/* ------------------------------------------------------- */
				Class<?> l_class = Class.forName(enabledModules[i]);
				@SuppressWarnings("unused")
				String l_ModuleName = (String) l_class.getField("MODULE_NAME").get(null);
				/* ------------------------------------------------------- */
				// save to global permission handling
				/* ------------------------------------------------------- */
//				GECAMedModule.addModulePermissions(l_ModuleName + "Module");
				/* ------------------------------------------------------- */
			} catch (ClassNotFoundException eCNF) {
			    logger.log(Level.WARN, "Module " + enabledModules[i] + " does not exist");
			} catch (Exception e) {
			    logger.log(Level.WARN, "loading permissions of module: \"" + enabledModules[i] + "\" failed", e);
			    errorModules.add(enabledModules[i]);
			}
			/* ------------------------------------------------------- */
		}
		/* ------------------------------------------------------- */
		
		/* ******************************************************** */
		// load all core modules
		/* ------------------------------------------------------- */
		// load the PatientModule
		/* ------------------------------------------------------- */
	   final GECAMedModule patientModule = new PatientManagerModule();
//	   GECAMedModule.addModulePermissions(patientModule.getName() + "Module");
	   /* ------------------------------------------------------- */
	   // load PatientListModule
	   /* ------------------------------------------------------- */
	   final GECAMedModule patientListModule = new PatientListModule((PatientManagerModule)patientModule);
//	   GECAMedModule.addModulePermissions(patientListModule.getName() + "Module");
	   /* ------------------------------------------------------- */
	   // put both of them to the hashmap
		modules.put(patientListModule.getName(), patientListModule);
		modules.put(patientModule.getName(), patientModule);
		
		
		/* ------------------------------------------------------- */
		// create the enabled modules
		/* ------------------------------------------------------- */
		for (int i = 0; i < enabledModules.length; i++) {
			/* ------------------------------------------------------- */
			// get the class
			try {
            	/* ------------------------------------------------------- */
            	if (enabledModules[i] == null || enabledModules[i].equals(""))
					continue;
            	/* ------------------------------------------------------- */
            	// use reflectins to load the modules
            	/* ------------------------------------------------------- */
            	Class<?> l_class = Class.forName(enabledModules[i]);
            	Constructor<?> l_constructor = l_class.getConstructor((Class[]) null);
            	/* ------------------------------------------------------- */
            	// create the new module
            	/* ------------------------------------------------------- */
            	GECAMedModule l_module = (GECAMedModule) l_constructor.newInstance((Object[]) null);
            	/* ------------------------------------------------------- */
            	// save to the hashmap
            	/* ------------------------------------------------------- */
            	if (l_module.isShown()) {
            		modules.put(l_module.getName(), l_module);
            	} else {
            		logger.info(l_module + " NOT SHOWN");
            	}
                /* ------------------------------------------------------- */
	    	} catch (ClassNotFoundException eCNF) {
	    	    logger.log(Level.WARN, "The module: " + enabledModules[i] + "  does not exist");
    		} catch (Exception e) {
    		    logger.log(Level.WARN, "The module: \"" + enabledModules[i] + "\"  could not be loaded", e);
    		    errorModules.add(enabledModules[i]);
    		}
		}
		/* ------------------------------------------------------- */
		// create the Admin Module
		// if the user has admin rights
		/* ------------------------------------------------------- */
		if (MainFrame.isAdmin() || GECAMedUtils.isDemo()) {
			/* ------------------------------------------------------- */
			final GECAMedModule adminModule = new AdminModule();
             modules.put(adminModule.getName(), adminModule);
             /* ------------------------------------------------------- */
		}
		logger.log(Level.INFO, MainFrame.createLogSectionString("ALL MODULES CREATED"));
		
		/* ------------------------------------------------------- */
		// call initModule on every Module to do some final initialisations 
		// after all modules have been created
		/* ------------------------------------------------------- */
		for (GECAMedModule module : modules.values()) {
			/* ------------------------------------------------------- */
		        logger.log(Level.INFO, MainFrame.createLogSubSectionString("Initializing " + module.getName().toUpperCase() + "  Module: " + module.getClass().getCanonicalName()+".initModule();"));
		        
		        MainFrame.loginScreen.setStatus(Translatrix
				.getTranslationString("main.initializingModule")
				+ " " + Translatrix.getTranslationString(module.getName()), 3);
		        
			module.initModule();
			/* ------------------------------------------------------- */
		}
		
		if (errorModules.isEmpty())
			logger.log(Level.INFO, MainFrame.createLogSectionString("ALL MODULES LOADED AND INITIALIZED"));
		else
			logger.log(Level.WARN, MainFrame.createLogSectionString("NOT all modules successfully loaded!"));
		
		return errorModules;
		/* ================================================== */
	}
		
	
	/**
	 * Extracts all hotkeys per module.
	 * 
	 * @return HotKeySettingsPlugin containing all hotkeys of the modules
	 */
	@SuppressWarnings("unchecked")
	public static HotkeySettingsPlugin loadHotkeys() {
		/* ================================================== */
		// load the hotkeys for the modules
		/* ------------------------------------------------------- */
		HotkeySettingsPlugin hotkeySettings = new HotkeySettingsPlugin();
		
		hotkeySettings.addExtension(new HotkeySettingsExtension("Global", new Vector(RegistrationDesk.getAllHotKeyActions())));
		
		for (GECAMedModule modul : modules.values()) {
			/* ------------------------------------------------------- */
			if (modul.getModuleActions() != null && modul.getModuleActions().size() > 0)
				hotkeySettings.addExtension(new HotkeySettingsExtension(modul));
			/* ------------------------------------------------------- */
		}
		
		return hotkeySettings;
		/* ================================================== */
	}
	
	
	/**
	 * @return
	 */
	public static Collection<GECAMedModule> getModules() {
		/* ================================================== */
		return modules.values();
		/* ================================================== */
	}
	
	/**
	 * returns the module object by it's name
	 * @param name
	 * @return
	 */
	public static GECAMedModule getModule(String name) {
		/* ================================================== */
		return modules.get(name);
		/* ================================================== */
	}
	
	
	
	/**
	 *
	 * Returns the names of all modules that can be invoked
	 * by the patientlist to open the selected patient.
	 *
	 *
	 * @return
	 */
	public static Set<String> getModuleNamesToDisplayPatients() {
		/* ================================================== */
		Set<String> result = new HashSet<String>();
		for (GECAMedModule mod : ModuleManager.getModules()) {
			if(mod.canDisplayPatient())
				result.add(mod.getName());
		}
		return result;
		/* ================================================== */
	}
	
	
	/**
	 * Enable/Disables all but this modul.
	 * @param name
	 */
	public static void enableAllButThis(String name, boolean b) {
		/* ================================================== */
		for (GECAMedModule module : modules.values()) {
			/* ------------------------------------------------------- */
			module.getModuleButton().setEnabled(!b);
			if (module.getModuleMenu() != null)
				module.getModuleMenu().setEnabled(false);
			/* ------------------------------------------------------- */
		}
		/* ------------------------------------------------------- */
		try {
			/* ------------------------------------------------------- */
			GECAMedModule oddModule = modules.get(name);
			oddModule.getModuleButton().setEnabled(b);
			if (oddModule.getModuleMenu() != null)
				oddModule.getModuleMenu().setEnabled(false);
			/* ------------------------------------------------------- */
		} catch (Exception e) {
//			e.printStackTrace();
		}
		/* ================================================== */
	}
	/**
	 * Enable/Disables all but this modul.
	 * @param name
	 */
	public static void enableAll(boolean b) {
		/* ================================================== */
		for (GECAMedModule module : modules.values()) {
			/* ------------------------------------------------------- */
			module.getModuleButton().setEnabled(b);
			if (module.getModuleMenu() != null)
				module.getModuleMenu().setEnabled(false);
			/* ------------------------------------------------------- */
		}
		/* ================================================== */
	}
}
