package de.uka.ipd.consensus.foundation;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import de.uka.ipd.consensus.foundation.evolution.EvolutionManager;
import de.uka.ipd.consensus.foundation.logging.LoggingManager;
import de.uka.ipd.consensus.foundation.ontology.OntologyManager;
import de.uka.ipd.consensus.foundation.rating.RatingManager;
import de.uka.ipd.consensus.foundation.rights.DynamicRightsManager;
import de.uka.ipd.consensus.foundation.scoring.IncentiveManager;
import de.uka.ipd.consensus.foundation.user.UserManager;

/**
 * This is the main class of the ConsensusFoundation framework that initializes and provides
 * easy access to all available modules.
 * <p>To initialize the framework, create an instance of this class and pass a configuration
 * file to the constructor (or use the default config file).</p>
 * @author Thomas Much
 * @version 2006-06-14
 */
public class ConsensusFoundation implements ConsensusFoundationManager {

	/**
	 * If no config file name is passed to the constructor and a file with this name is found on the classpath,
	 * it is used as the configuration properties file.
	 */
	public static final String CF_PROPERTIES     = "ConsensusFoundation.properties";

	
	/**
	 * The value of this property key names the user that is granted the superadmin role on login,
	 * regardless what roles the UserManager assigns to the user. A user with this role can assign
	 * the admin role to other users (and revoke it).
	 */
	public static final String CF_SUPERADMIN_KEY = "cf.superadmin";

	
	/**
	 * The configuration key that defines the LoggingManager.
	 * @see de.uka.ipd.consensus.foundation.logging.LoggingManager
	 * @see #CF_PROPERTIES
	 */
	public static final String CF_LOGGING_KEY    = "cf.loggingmanager";

	
	/**
	 * The configuration key that defines the UserManager.
	 * @see de.uka.ipd.consensus.foundation.user.UserManager
	 * @see #CF_PROPERTIES
	 */
	public static final String CF_USER_KEY       = "cf.usermanager";

	
	/**
	 * The configuration key that defines the OntologyManager.
	 * @see de.uka.ipd.consensus.foundation.ontology.OntologyManager
	 * @see #CF_PROPERTIES
	 */
	public static final String CF_ONTOLOGY_KEY   = "cf.ontologymanager";

	
	/**
	 * The configuration key that defines the EvolutionManager (optional).
	 * @see de.uka.ipd.consensus.foundation.evolution.EvolutionManager
	 * @see #CF_PROPERTIES
	 */
	public static final String CF_EVOLUTION_KEY  = "cf.evolutionmanager";

	
	/**
	 * The configuration key that defines the RatingManager (optional).
	 * @see de.uka.ipd.consensus.foundation.rating.RatingManager
	 * @see #CF_PROPERTIES
	 */
	public static final String CF_RATING_KEY     = "cf.ratingmanager";

	
	/**
	 * The configuration key that defines the IncentiveManager (optional).
	 * @see de.uka.ipd.consensus.foundation.scoring.IncentiveManager
	 * @see #CF_PROPERTIES
	 */
	public static final String CF_INCENTIVE_KEY  = "cf.incentivemanager";


	/**
	 * The configuration key that defines the DynamicRightsManager (optional).
	 * @see de.uka.ipd.consensus.foundation.rights.DynamicRightsManager
	 * @see #CF_PROPERTIES
	 */
	public static final String CF_DYNRIGHTS_KEY  = "cf.dynamicrightsmanager";


	/**
	 * The configuration key that defines the ScoringListeners as a comma separated list (optional).
	 * @see de.uka.ipd.consensus.foundation.scoring.ScoringListener
	 */
	public static final String CF_SCORING_KEY    = "cf.scoringlistener";

	
	/**
	 * The configuration key that defines the QueryEngine if an implementation other than the default one shall be used (optional).
	 * @see de.uka.ipd.consensus.foundation.query.QueryEngine
	 */
	public static final String CF_QUERY_KEY      = "cf.queryengine";

	
	/**
	 * The configuration key that defines the ExportHandlers (comma separated list) in addition to any default handlers (optional).
	 * @see de.uka.ipd.consensus.foundation.ontology.ExportHandler
	 */
	public static final String CF_EXPORT_KEY     = "cf.exporthandler";

	
	/**
	 * The configuration key that defines the ImportHandlers (comma separated list) in addition to any default handlers (optional).
	 * @see de.uka.ipd.consensus.foundation.ontology.ImportHandler
	 */
	public static final String CF_IMPORT_KEY     = "cf.importhandler";

	
	/**
	 * The configuration key that defines the loglevel (optional).
	 * @see de.uka.ipd.consensus.foundation.logging.LoggingManager
	 */
	public static final String CF_LOGLEVEL_KEY   = "cf.loglevel";

	
	private static final String CF_LOGGING_DEFAULTVALUE   = "de.uka.ipd.consensus.impl.LoggingManagerImpl";
	private static final String CF_USER_DEFAULTVALUE      = "de.uka.ipd.consensus.impl.UserManagerImpl";
	private static final String CF_ONTOLOGY_DEFAULTVALUE  = "de.uka.ipd.consensus.impl.OntologyManagerImpl";
//	private static final String CF_EVOLUTION_DEFAULTVALUE = "de.uka.ipd.consensus.impl.EvolutionManagerImpl";
//	private static final String CF_RATING_DEFAULTVALUE    = "de.uka.ipd.consensus.impl.SQLRatingManagerImpl";
//	private static final String CF_INCENTIVE_DEFAULTVALUE = "de.uka.ipd.consensus.impl.IncentiveManagerImpl";
//	private static final String CF_DYNRIGHTS_DEFAULTVALUE = "de.uka.ipd.consensus.impl.DynamicRightsManagerImpl";

	private LoggingManager       loggingManager;
	private UserManager          userManager;
	private OntologyManager      ontologyManager;
	private EvolutionManager     evolutionManager;
	private RatingManager        ratingManager;
	private IncentiveManager     incentiveManager;
	private DynamicRightsManager dynamicRightsManager;
	
	private boolean running = false;


	/**
	 * Initializes the framework and its components with the configuration file defined by <code>CF_PROPERTIES</code>.
	 * @throws Exception if an error occurred before the framework was initialized completely
	 * @see #CF_PROPERTIES
	 */
	public ConsensusFoundation() throws Exception {
		Properties props = this.loadProperties( null );
		this.setup( props );
	}


	/**
	 * Initializes the framework and its components with the given Properties object.
	 * @param config the Properties object containing the configuration keys
	 * @throws Exception if an error occurred before the framework was initialized completely
	 */
	public ConsensusFoundation(Properties config) throws Exception {
		this.setup( config );
	}


	/**
	 * Initializes the framework and its components with the given configuration file.
	 * @param configFile the properties file containing the configuration keys
	 * @throws Exception if an error occurred before the framework was initialized completely
	 */
	public ConsensusFoundation(String configFile) throws Exception {
		Properties props = this.loadProperties( configFile );
		this.setup( props );
	}


	private Properties loadProperties(String configFile) throws Exception {
		Properties props = new Properties();

		if (configFile == null || configFile.length() == 0) {
			configFile = CF_PROPERTIES;
		}

		InputStream in = null;
		
		try {
			in = this.getClass().getClassLoader().getResourceAsStream(configFile);
			
			if (in == null) {
				throw new IOException("ConfigFile \"" + configFile + "\" not found.");
			}

			props.load( in );
		}
		finally {
			if (in != null) {
				in.close();
			}
		}
		
		return props;
	}


	private void setup(Properties props) throws Exception {
		this.init( props );

		String cfLogging   = props.getProperty(CF_LOGGING_KEY, CF_LOGGING_DEFAULTVALUE);
		String cfUser      = props.getProperty(CF_USER_KEY, CF_USER_DEFAULTVALUE);
		String cfOntology  = props.getProperty(CF_ONTOLOGY_KEY, CF_ONTOLOGY_DEFAULTVALUE);
		String cfEvolution = props.getProperty(CF_EVOLUTION_KEY/*, CF_EVOLUTION_DEFAULTVALUE*/);
		String cfRating    = props.getProperty(CF_RATING_KEY/*, CF_RATING_DEFAULTVALUE*/);
		String cfIncentive = props.getProperty(CF_INCENTIVE_KEY/*, CF_INCENTIVE_DEFAULTVALUE*/);
		String cfDynRights = props.getProperty(CF_DYNRIGHTS_KEY/*, CF_DYNRIGHTS_DEFAULTVALUE*/);
		
		running = true;

		loggingManager = (LoggingManager)Class.forName(cfLogging).newInstance();
		loggingManager.init(props);

		userManager = (UserManager)Class.forName(cfUser).newInstance();
		userManager.init(props,loggingManager);

		ontologyManager = (OntologyManager)Class.forName(cfOntology).newInstance();
		ontologyManager.init(props,loggingManager,userManager);

		if ((cfEvolution != null) && (cfEvolution.length() > 0)) {
			evolutionManager = (EvolutionManager)Class.forName(cfEvolution).newInstance();
			evolutionManager.init(props,loggingManager,userManager,ontologyManager);
		}

		if ((cfRating != null) && (cfRating.length() > 0)) {
			ratingManager = (RatingManager)Class.forName(cfRating).newInstance();
			ratingManager.init(props,loggingManager,userManager,ontologyManager);
		}

		if ((cfIncentive != null) && (cfIncentive.length() > 0)) {
			incentiveManager = (IncentiveManager)Class.forName(cfIncentive).newInstance();
			incentiveManager.init(props,loggingManager,userManager,ontologyManager);
		}

		if ((cfDynRights != null) && (cfDynRights.length() > 0)) {
			dynamicRightsManager = (DynamicRightsManager)Class.forName(cfDynRights).newInstance();
			dynamicRightsManager.init(props,loggingManager,userManager,ontologyManager,incentiveManager);
		}
		
		this.initCompleted( props );
		
		loggingManager.initCompleted(props, this);
		userManager.initCompleted(props, this);
		ontologyManager.initCompleted(props, this);
		if (evolutionManager != null) evolutionManager.initCompleted(props, this);
		if (ratingManager != null) ratingManager.initCompleted(props, this);
		if (incentiveManager != null) incentiveManager.initCompleted(props, this);
		if (dynamicRightsManager != null) dynamicRightsManager.initCompleted(props, this);
		
		loggingManager.info("ConsensusFoundation initialized & ready (" + this + ").");
	}


	/**
	 * Called by the constructor before the modules are instantiated and their <code>init()</code> method is called.
	 * @param config all entries from <code>ConsensusFoundation.properties</code>
	 * @throws Exception if any error occurs (startup of the application is aborted in this case)
	 * @see #ConsensusFoundation()
	 */
	protected void init(Properties config) throws Exception { }


	/**
	 * Called by the constructor after the <code>init()</code> method of all available modules has been called,
	 * but before their <code>initCompleted()</code> method is called.
	 * @param config all entries from <code>ConsensusFoundation.properties</code>
	 * @throws Exception if any error occurs (startup of the application is aborted in this case)
	 */
	protected void initCompleted(Properties config) throws Exception { }


	public LoggingManager getLoggingManager() {
		return loggingManager;
	}


	public UserManager getUserManager() {
		return userManager;
	}


	public OntologyManager getOntologyManager() {
		return ontologyManager;
	}


	public EvolutionManager getEvolutionManager() {
		return evolutionManager;
	}


	public IncentiveManager getIncentiveManager() {
		return incentiveManager;
	}


	public RatingManager getRatingManager() {
		return ratingManager;
	}


	public DynamicRightsManager getDynamicRightsManager() {
		return dynamicRightsManager;
	}
	

	/**
	 * An application must call this method to shut down the framework and its components.
	 * This method calls the <code>shutdown()</code> method of each module.
	 * @see ConsensusFoundationModule#shutdown()
	 */
	public void shutdown() {
		if (running) {
			running = false;

			if (loggingManager != null) loggingManager.info("ConsensusFoundation shutdown ... (" + this + ")");
			
			if (dynamicRightsManager != null) dynamicRightsManager.shutdown();
			if (incentiveManager != null) incentiveManager.shutdown();
			if (ratingManager != null) ratingManager.shutdown();
			if (evolutionManager != null) evolutionManager.shutdown();
			if (ontologyManager != null) ontologyManager.shutdown();
			if (userManager != null) userManager.shutdown();
			if (loggingManager != null) loggingManager.shutdown();
		}
	}


	/**
	 * Calls <code>shutdown()</code> if the application forgot to do so.
	 * @see #shutdown()
	 */
	protected void finalize() throws Throwable {
		shutdown();
		super.finalize();
	}


	public String getVersion() {
		return "2006-06-14 tm (" + this.getClass().getName() + ")";
	}
}
