/////////////////////////////////////////////////////////////////////////
//
// © University of Southampton IT Innovation Centre, 2004
//
// Copyright in this library belongs to the IT Innovation Centre of
// 2 Venture Road, Chilworth Science Park, Southampton, SO16 7NP, UK.
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//      Created by:             Stuart E. Middleton
//      Created date:           2004/04/30
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////

package uk.ac.soton.itinnovation.gemss.utils.configuration;

import java.util.Properties;
import java.io.FileInputStream;
import java.io.File;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.Set;
import java.util.Iterator;

public class Configuration {

    private static Logger mLogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.utils.configuration.Configuration");
    private Properties mStaticProperties = null;
    private Properties mRuntimeProperties = null;
    private String mPropertiesFilePath= null;
    private String mConfigDir = null;
    private String mFileSeparator = null;

    /**
     * Constructor that initialises this configuration with configuration file path,
     * A default configuration directory of "." is used
     * @param propertiesFilePath path to configuration file
     * @throws ConfigurationException
     */
    public Configuration(String propertiesFilePath) throws ConfigurationException {
        init(propertiesFilePath);
        mConfigDir = "";
    }

    /**
     * Constructor that initialises this configuration with a configuration file path and
     * also sets the configuration directory
     * @param propertiesFilePath path to configuration file
     * @param configurationDir path to configuration directory where other configuration files are stored.
     * @throws ConfigurationException
     */
    public Configuration(String propertiesFilePath,String configurationDir) throws ConfigurationException {
        if(configurationDir==null) {
          mLogger.log(Level.SEVERE,"Configuration directory is invalid");
          throw new ConfigurationException("Configuration directory is invalid");
        }

        init(propertiesFilePath);
        mFileSeparator = System.getProperty("file.separator");
        if(!configurationDir.equals("") && (configurationDir.endsWith(mFileSeparator) || configurationDir.charAt(configurationDir.length()-1)=='/')) {
            //remove the trailing slash
            int length = configurationDir.length();
            mConfigDir = configurationDir.substring(0,length-1);
        }
        else
            mConfigDir = configurationDir;
        //check that the config directory exists

          File file = new File(mConfigDir);
          if(!file.exists()) {
            mLogger.log(Level.SEVERE,"Supplied configuration directory '" + mConfigDir +"' does not exist");
            throw new ConfigurationException("Configuration directory '" + mConfigDir +"' does not exist");
          }


    }

    /**
     * Retrieve the configuration value for the passed configuration option name
     * @param name name of option to retrieve
     * @return configuration option value
     * @throws ConfigurationException
     */
    public String getConfigurationValue(String name) throws ConfigurationException {
        if(name==null) {
            mLogger.warning("Tried to find a configuration option with an null name");
            throw new ConfigurationException("Tried to find a configuration option with an invalid name");
        }
        try{
            String value =  mStaticProperties.getProperty(name);
            if(value==null || value.equals("")) {
                //try user properties
                value = mRuntimeProperties.getProperty(name);
            }
            if(value==null)
                throw new ConfigurationException("Configuration option '" + name + "' could not be retrieved from configuration file '" + mPropertiesFilePath + "' or runtime properties");
            return value.trim();
        }
        catch(Exception ex) {
            mLogger.log( Level.WARNING,"Failed to get config option "+name+" in file "+mPropertiesFilePath,ex);
            throw new ConfigurationException("Configuration option '" + name + "' could not be retrieved from configuration file '" + mPropertiesFilePath + "' or runtime properties");
        }
    }

    /**
     * Retrieve the directory path where configuration files can be found
     * @return directory path without any trailing slash
     */
    public String getConfigurationFileDir() {
        return mConfigDir;
    }

    /**
     * Add a configuration option to the active configuration, no overwriting is allowed
     * @param name configuration option name
     * @param value configuration option value
     * @throws ConfigurationException
     */
    public synchronized void addConfigurationSetting(String name, String value) throws ConfigurationException {
        //don't want to overwrite settings
        Set keys = mRuntimeProperties.keySet();
        Iterator iterator = keys.iterator();
        while(iterator.hasNext()) {
            String key = (String) iterator.next();
            if(key.equals(name)) {
                mLogger.severe("Trying to overwrite configuration option named '" + name + "', this is not allowed");
                throw new ConfigurationException("Trying to overwrite configuration option named '" + name + "', this is not allowed");
            }
        }
        //fine write the property
        mRuntimeProperties.setProperty(name,value);
    }

    private void init(String propertiesFilePath) throws ConfigurationException {
        try{
            mPropertiesFilePath = propertiesFilePath;
            mStaticProperties = new Properties();
            File file = new File(propertiesFilePath);
            FileInputStream inStream = new FileInputStream(file);
            mStaticProperties.load(inStream);
            inStream.close();
            mRuntimeProperties = new Properties();
        }
        catch(java.io.IOException ex) {
            mLogger.log(Level.SEVERE,"Could not read configuration file '" + propertiesFilePath + "'. Please check it exists and is correct, more information is available in the logs",ex);
            throw new ConfigurationException("Could not read configuration file '" + propertiesFilePath + "', please check it exists and is correct");
        }
    }
}