package de.tu_dresden.diplom.richter_mirko_mat2628335;

import org.apache.log4j.Logger;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.app.Velocity;

import java.io.File;
import java.io.StringWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;

/**
 * Main-class to configure the prototypical implementation. This class is being instantiated
 * by some application or application-part if conf-infos are needed.<br/>
 * IMPORTANT: configure the system-property <i>diplom.home</i> properly (prefix relative to the
 * diploma-file-structure as base-dir for all needed file-operations)
 */
public class Configuration {

   /**
    * loads only the acpProject Properties
    */
   public static final String APPLICATION_BASE = "@base@";

   /**
    * loads the acpProject-properties and the modulesBase properties
    */
   public static final String APPLICATION_MODULES_BASE = "@modules@";

   /**
    * Application of the ClientService1 (register and book tickets)
    */
   public static final String APPLICATION_CLIENT1 = "applicationClientService1";

   /**
    * Application that was only used to test functionalit of keystore, cert-generation etc.
    */
   public static final String APPLICATION_TESTER = "applicationTester";

   /**
    * Application that tests the functionality on SOAP-Level
    */
   public static final String APPLICATION_SOAP_UNIT_TEST = "applicationSOAPUnitTest";

   /**
    * Application that tests the functionality on non-SOAP-Level
    */
   public static final String APPLICATION_UNIT_TEST = "applicationUnitTest";

   /**
    * Application that operates on Systinets UDDI-Regsitry
    */
   public static final String APPLICATION_SYSREGAPP = "";

   /**
    * Mapps applications (== configuration contexts) to concrete module in the diploma-filestructure-manner.
    */
   private static Map APP_MODULE_MAPPING = null;

   /**
    * Name of the System-Property to read the base-dir from.<br/>
    * <b>All other paths are given relative to that path!</b>
    */
   public static final String SYS_PROP_DIPLOM_HOME = "diplom.home";

   /**
    * Name of the properties-file to configure the entire project
    */
   public static final String FILE_PROJECT_PROPERTIES = "acpProject.properties";

   /**
    * Name of the properties-file to configure all modules
    */
   public static final String FILE_MODULES_PROPERTIES = "modulesBase.properties";

   /**
    * Name of the properties-file to configure some specific module
    */
   public static final String FILE_MODULE_PROPERTIES = "application.properties";

   /**
    * Base part of all application-specific confile-properties
    */
   public static final String BASE_PROP_APP_CONF = "acpProject.confFile";

   /**
    * Property to read the certificate-dir from.
    */
   public static final String PROP_CERT_HOME = "acpProject.dir.cert";

   /**
    * Property to read the external-application-dir from.
    */
   public static final String PROP_EXT_APP_HOME = "acpProject.dir.ext_app";

   /**
    * Property to read the modules-dir from.
    */
   public static final String PROP_MODULES_HOME = "acpProject.dir.modules";

   /**
    * Property to read the library-dir from.
    */
   public static final String PROP_LIB_HOME = "acpProject.dir.lib";

   /**
    * Property to read the home (directory) of the usageCounter-module from.
    */
   public static final String PROP_MODULES_USAGE_COUNTER_HOME = "acpProject.moduleDir.usageCounter";

   /**
    * Property to read the home (directory) of the siplomBase-module from.
    */
   public static final String PROP_MODULES_DIPLOM_BASE_HOME = "acpProject.moduleDir.diplomBase";

   /**
    * Property to read the home (directory) of the usageCounterTest-module from.
    */
   public static final String PROP_MODULES_USAGE_COUNTER_TEST_HOME = "acpProject.moduleDir.usageCounterTest";

   /**
    * Property to read the home (directory) of the DOMCipher-module from.
    */
   public static final String PROP_MODULES_DOM_CIPHER_HOME = "acpProject.moduleDir.DOMCipher";

   /**
    * Property to read the home (directory) of the sysRegApp-module from.
    */
   public static final String PROP_MODULES_SYSREGAPP_HOME = "acpProject.moduleDir.sysRegApp";

   /**
    * Property to read the location of the log4j-configuration-file from.
    */
   public static final String PROP_LOG4J_CONF = "acpProject.log4j.configuration.file";

   /**
    * Property that defines how to obtain certificates (uddi or file)
    */
   public static final String PROP_CERT_METHOD = "acpProject.certMethod";

   /**
    * Property to read the location of the keystore-file from.
    */
   public static final String PROP_KEYSTORE_LOCATION = "acpProject.keystore.location";

   /**
    * Property to read the host that is used for service-deployment from.
    */
   public static final String PROP_TOMCAT_DEPLOYMENT_HOST = "acpProject.deployment.tomcat.host";

   /**
    * Property to read the port that is used for service-deployment from.
    */
   public static final String PROP_TOMCAT_DEPLOYMENT_PORT = "acpProject.deployment.tomcat.port";

   /**
    * Property to read the context that is used for service-deployment from.
    */
   public static final String PROP_TOMCAT_DEPLOYMENT_CONTEXT = "acpProject.deployment.tomcat.context";

   /**
    * Property to read the service that is used for service-deployment from.
    */
   public static final String PROP_TOMCAT_DEPLOYMENT_SERVICE = "acpProject.deployment.tomcat.service";

   /**
    * Property to read the adminService that is used for service-deployment from.
    */
   public static final String PROP_TOMCAT_DEPLOYMENT_ADMIN_SERVICE = "acpProject.deployment.tomcat.adminService";

   /**
    * Property to read the host that is used for communication with the services from.
    */
   public static final String PROP_TOMCAT_COMMUNICATION_HOST = "acpProject.communication.tomcat.host";

   /**
    * Property to read the port that is used for communication with the services from.
    */
   public static final String PROP_TOMCAT_COMMUNICATION_PORT = "acpProject.communication.tomcat.port";

   /**
    * Property to read the context that is used for communication with the services from.
    */
   public static final String PROP_TOMCAT_COMMUNICATION_CONTEXT = "acpProject.communicytion.tomcat.context";

   /**
    * Property to read the host that is used for communication with systinet's registry from.
    */
   public static final String PROP_SYSTINET_REGISTRY_COMMUNICATION_HOST = "acpProject.communication.systinetRegistry.host";

   /**
    * Property to read the port that is used for communication with systinet's registry from.
    */
   public static final String PROP_SYSTINET_REGISTRY_COMMUNICATION_PORT = "acpProject.communication.systinetRegistry.port";

   /**
    * Property to read the context that is used for communication with systinet's registry from.
    */
   public static final String PROP_SYSTINET_REGISTRY_COMMUNICATION_CONTEXT = "acpProject.communicytion.systinetRegistry.context";

   private static final Logger logger = Logger.getLogger(Configuration.class);

   private String executingApp = null;
   private String diplomHome = "e:/home/mirko/diplom";

   private PropertiesConfiguration config = null;
   private VelocityContext velocityContext = null;

   /**
    * configures the application - modules mapping
    */
   static {
      try {
         Velocity.init();
      } catch (Exception e) {
         e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      }
      APP_MODULE_MAPPING = new HashMap();
      APP_MODULE_MAPPING.put(APPLICATION_CLIENT1, PROP_MODULES_USAGE_COUNTER_HOME);
      APP_MODULE_MAPPING.put(APPLICATION_TESTER, PROP_MODULES_DIPLOM_BASE_HOME);
      APP_MODULE_MAPPING.put(APPLICATION_SOAP_UNIT_TEST, PROP_MODULES_USAGE_COUNTER_TEST_HOME);
      APP_MODULE_MAPPING.put(APPLICATION_UNIT_TEST, PROP_MODULES_USAGE_COUNTER_TEST_HOME);
      APP_MODULE_MAPPING.put(APPLICATION_SYSREGAPP, PROP_MODULES_SYSREGAPP_HOME);
   }

   /**
    * Creates a new Configuration object.
    *
    * @param executingApp Specifies which application-conf-file shall be red.
    */
   public Configuration(String executingApp) {
      final String fn = "[<init>] ";
      System.out.println("Creating a new configuration-object ...");
      this.executingApp = executingApp;
      String newDiplomHome = System.getProperty(SYS_PROP_DIPLOM_HOME);
      if (newDiplomHome != null && newDiplomHome.length() > 0) {
         if (logger.isDebugEnabled()) logger.debug(fn + "using set System-Property diplom.home=" + newDiplomHome);
         this.diplomHome = newDiplomHome;
      }else{
         logger.warn(fn + "NO System-Property diplom.home set! -> usinf default: '" + diplomHome + "'");
      }
      initialize();
      System.out.println("... DONE");
   }

   /**
    * Reads the needed files out of the filesystem (accoridng to the
    * running application-conf)
    */
   private void initialize() {
      final String fn = "[initialize] ";
      if (diplomHome != null) {
         try {
            String configurationFile = diplomHome + File.separator + FILE_PROJECT_PROPERTIES;
            System.out.println("Configuring Project with '" + configurationFile + "'");
            config = new PropertiesConfiguration(configurationFile);
            velocityContext = new VelocityContext();
            Iterator it = config.getKeys();
            while (it.hasNext()) {
               String s = (String) it.next();
               velocityContext.put(s, config.getString(s));
               velocityContext.put("baseDir", diplomHome);
            }
            if (executingApp != APPLICATION_BASE) {
               String moduleConfFile = diplomHome + File.separator + config.getString(PROP_MODULES_HOME) + File.separator + FILE_MODULES_PROPERTIES;
               System.out.println("Configuring Modules with '" + moduleConfFile + "'");
               PropertiesConfiguration modulesConf = new PropertiesConfiguration(moduleConfFile);
               it = modulesConf.getKeys();
               while (it.hasNext()) {
                  String s = (String) it.next();
                  config.addProperty(s, modulesConf.getString(s));
                  velocityContext.put(s, config.getString(s));
               }
               if (executingApp != APPLICATION_MODULES_BASE) {
                  String appModConfFile = diplomHome + File.separator + config.getString((String) APP_MODULE_MAPPING.get(executingApp)) + File.separator + FILE_MODULE_PROPERTIES;
                  System.out.println("Configuring Application '" + executingApp + "' with MODULE-CONF '" + appModConfFile + "'");
                  PropertiesConfiguration applicationConf = new PropertiesConfiguration(appModConfFile);
                  it = applicationConf.getKeys();
                  while (it.hasNext()) {
                     String s = (String) it.next();
                     config.addProperty(s, applicationConf.getString(s));
                     velocityContext.put(s, config.getString(s));
                  }
                  String appConfProp = BASE_PROP_APP_CONF + "." + executingApp;
                  String appConfFile = diplomHome + File.separator + config.getString(appConfProp);
                  System.out.println("Configuring Application with '" + appConfFile + "'");
                  PropertiesConfiguration appConf = new PropertiesConfiguration(appModConfFile);
                  it = appConf.getKeys();
                  while (it.hasNext()) {
                     String s = (String) it.next();
                     config.addProperty(s, appConf.getString(s));
                     velocityContext.put(s, config.getString(s));
                  }
               }
            }
         } catch (ConfigurationException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
         }
      } else {
         logger.warn(fn + "System-Property 'diplom.home' has not been set properly ... application may not run!!");
      }
   }

   /**
    * Resolves the given property, what means, if any ${PROP_NAME} exists it is being
    * replaced by its value in this configuration
    *
    * @param property
    * @return
    */
   public String resolveProperty(String property) {
      final String fn = "[resolveProperty] ";
      String result = null;
      try {
         if (logger.isDebugEnabled()) logger.debug(fn + "Trying to resolve property '" + property + "'");
         StringWriter sw = new StringWriter();
         Velocity.evaluate(velocityContext, sw, "ConfigurationResolver", property);
         result = sw.toString();
      } catch (ParseErrorException e) {
         e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      } catch (MethodInvocationException e) {
         e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      } catch (ResourceNotFoundException e) {
         e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      } catch (IOException e) {
         e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      }
      return result;
   }

   /**
    * Obtain the underlying Commons-Configuration object
    *
    * @return
    */
   public PropertiesConfiguration getConfig() {
      return config;
   }

   /**
    * Obtain the absolute path to the cert-directory
    *
    * @return
    */
   public String getCertHome() {
      return diplomHome + File.separator + config.getString(PROP_CERT_HOME);
   }

   /**
    * Obtain the absolute path to the ext_app-directory
    *
    * @return
    */
   public String getExtAppHome() {
      return diplomHome + File.separator + config.getString(PROP_EXT_APP_HOME);
   }

   /**
    * Obtain the absolute path to the modules-directory
    *
    * @return
    */
   public String getModulesHome() {
      return diplomHome + File.separator + config.getString(PROP_MODULES_HOME);
   }

   /**
    * Obtain the absolute path to the lib-directory
    *
    * @return
    */
   public String getLibHome() {
      return diplomHome + File.separator + config.getString(PROP_LIB_HOME);
   }

   /**
    * Obtain the absolute path the home of module "usageCounter"
    *
    * @return
    */
   public String getModuleUsageCounterHome() {
      return diplomHome + File.separator + config.getString(PROP_MODULES_USAGE_COUNTER_HOME);
   }

   /**
    * Obtain the absolute path the home of module "usageCounterTest"
    *
    * @return
    */
   public String getModuleUsageCounterTestHome() {
      return diplomHome + File.separator + config.getString(PROP_MODULES_USAGE_COUNTER_TEST_HOME);
   }

   /**
    * Obtain the absolute path the home of module "siplomBase"
    *
    * @return
    */
   public String getModuleDiplomBaseHome() {
      return diplomHome + File.separator + config.getString(PROP_MODULES_DIPLOM_BASE_HOME);
   }

   /**
    * Obtain the absolute path the home of module "DOMCipher"
    *
    * @return
    */
   public String getModuleDOMCipherHome() {
      return diplomHome + File.separator + config.getString(PROP_MODULES_DOM_CIPHER_HOME);
   }

   /**
    * Obtain the absolute path the home of module "sysRegApp"
    *
    * @return
    */
   public String getModuleSysRegAppHome() {
      return diplomHome + File.separator + config.getString(PROP_MODULES_SYSREGAPP_HOME);
   }

   /**
    * Obtain the absolute path of the Log4J Configuration-File
    *
    * @return
    */
   public String getLog4jFile() {
      return diplomHome + File.separator + config.getString(PROP_LOG4J_CONF);
   }

   /**
    * Obtain the absolute path of the used keystore
    *
    * @return
    */
   public String getKeystoreLocation() {
      return diplomHome + File.separator + config.getString(PROP_KEYSTORE_LOCATION);
   }

   /**
    * Obtain the location (URL) of the deployment-service
    *
    * @return
    */
   public String getDeploymentServiceLocation() {
      return "http://" + config.getString(PROP_TOMCAT_DEPLOYMENT_HOST) + ":" + config.getString(PROP_TOMCAT_DEPLOYMENT_PORT) + "/" + config.getString(PROP_TOMCAT_DEPLOYMENT_CONTEXT) + "/" + config.getString(PROP_TOMCAT_DEPLOYMENT_SERVICE);
   }

   /**
    * Obtain the location (URL) of the admin-servlet
    *
    * @return
    */
   public String getDeploymentAdminServiceLocation() {
      return "http://" + config.getString(PROP_TOMCAT_DEPLOYMENT_HOST) + ":" + config.getString(PROP_TOMCAT_DEPLOYMENT_PORT) + "/" + config.getString(PROP_TOMCAT_DEPLOYMENT_CONTEXT) + "/" + config.getString(PROP_TOMCAT_DEPLOYMENT_ADMIN_SERVICE);
   }

   /**
    * Obtain the base-URL for communication with axis (the basics for
    * URL-creation of deployed witness-services and server-services)
    *
    * @return
    */
   public String getCommunicationContextLocationTomcat() {
      return "http://" + config.getString(PROP_TOMCAT_COMMUNICATION_HOST) + ":" + config.getString(PROP_TOMCAT_COMMUNICATION_PORT) + "/" + config.getString(PROP_TOMCAT_COMMUNICATION_CONTEXT);
   }

   /**
    * Obtain the base-URL for communication with systinet's UDDI-registry.
    *
    * @return
    */
   public String getCommunicationContextLocationSystinet() {
      return "http://" + config.getString(PROP_SYSTINET_REGISTRY_COMMUNICATION_HOST) + ":" + config.getString(PROP_SYSTINET_REGISTRY_COMMUNICATION_PORT) + "/" + config.getString(PROP_SYSTINET_REGISTRY_COMMUNICATION_CONTEXT);
   }

   /**
    * Checks wheather the certificates shall be red from UDDI-registry
    *
    * @return true if certificates shall be red from uddi, fals if they shall be red directly from configured keystore
    */
   public boolean isCertMethodUDDI() {
      boolean result = false;
      String method = config.getString(PROP_CERT_METHOD);
      if (method != null) {
         if (method.equals("uddi")) {
            result = true;
         }
      }
      return result;
   }

}
