/*
 * COPYRIGHT (c) 1998-1999 Jan-Henrik Haukeland, http://www.tildeslash.com/
 */


package ts.config;

import java.io.*;
import java.util.*;
import java.awt.*;


/** 
 * This final class holds universal application properties,
 * as well as java default properties from java.lang.System.
 * The class uses a static initializer to load default properties
 * at loadtime. Using the singleton pattern to have only one 
 * instance of Config.
 *
 * @author  Jan-Henrik Haukeland  &lt;hauk@tildeslash.com&gt;
 * @version $Id: Config.java,v 1.12 1999/06/09 18:55:54 hauk Exp $
 */
public final class Config 
{
  public static int NO_INT_VALUE= -1;

  /** Property object to store configuration values in */
  private static Properties myConfig;

  /** Self */
  private static Config instance;


  static 
  {
    try {
      myConfig= new Properties(System.getProperties());

      InputStream defaultProperties= 
	  Class.forName("java.lang.Object").
	  getResourceAsStream("/ts/resources/default.properties");

      if ( defaultProperties == null ) {
	System.err.println("\n\nFailed to initialize architecture "+
			   "\nReason: Could not load default configuration");
	System.exit(1);
      }
      
      myConfig.load(defaultProperties);
    }
    catch (Exception e) {
      System.err.println("\n\nFailed to initialize architecture "+
			 "Could not load default properties\nReason: "+
			 e.getMessage());
      System.exit(1);
    }
  }

  private final static String HEADER= 
   "\n# Resource strings for TsBiff version "+Constant.VERSION+
   "\n#  Please use the configuration tool "+
   "to change values in this file\n#\n";





  private Config() { }
  
  
  /**
   * Returns the Config instance singleton
   * @return a Config singelton instance
   */
  public static Config getInstance()
  {
    if ( instance == null )
	instance=  new Config();
    return instance;
  }

  
  
  /** 
   * Load properties from file. Existing properties will be
   * replaced with new key/value pair if defined in file.
   *
   * @param     file  A properties File object
   * @exception ConfigException if invalid file 
   */
  public void load(File file) throws ConfigException
  {
    try {
      myConfig.load(new FileInputStream(file));
    }
    catch (FileNotFoundException fe) {
      throw new ConfigException(ConfigException.FATAL,
				"Properties file " + file +
				" not found.");
    }
    catch (IOException io) {
      throw new ConfigException(ConfigException.FATAL,
				"Could not load properties from file "+
				file+"\n Reason: "+
				io.getMessage());     
    }
    catch (SecurityException se) {
      throw new ConfigException(ConfigException.FATAL,
				"Could not load properties from file "+
				file+"\n Reason: "+
				se.getMessage());     
    }
  }


  /** 
   * Load properties from file using filename as locator
   *
   * @param     file  A string containing a filename
   * @exception ConfigException if invalid file 
   */
  public void load(String file) throws ConfigException
  {
    this.load(new File(file));
  }


  /**
   * Save properties to the submitted File
   *
   * @exception ConfigException if could not save properties file 
   */
  public void save(File file) throws ConfigException
  {
    try {
      FileOutputStream out= new FileOutputStream(file);
      myConfig.save(out, this.HEADER);
    } catch (IOException io) {
      throw new ConfigException(ConfigException.FATAL,
				"Cannot save Properties file :" + file);
    }
  }


  /**
   * Save properties to the submitted filename
   * @exception ConfigException if could not save properties file 
   */
  public void save(String file) throws ConfigException
  {
    this.save(new File(file));
  }


  /**
   * Set value for given property.
   *
   * @param key a key
   * @param value the value for the key
   */
  public static void set(String key, String value)
  {
    myConfig.put(key, value);
  }  



  // ----------------------- Get Object types ----------------------- //


  /**
   * Get a String array for the given property. The
   * key is supposed to have a value with whitespace separated
   * sub-strings.
   *
   * @param key A key 
   * @return A String array with values
   */
  public static String[] getValues(String key)
  {
    return split(myConfig.getProperty(key), null);
  }


  /**
   * Get a String array for the given property. The
   * key is supposed to have a value with comma separated
   * sub-strings.
   *
   * @param key A key 
   * @return A String array with values
   */
  public static String[] getCValues(String key)
  {
    return split(myConfig.getProperty(key), ",");
  }



  /**
   * Get an int array for the given property. The key is supposed to
   * have a list of int values. This method returns a null int array
   * If a value is not an int, or if there's no value.
   *
   * @param key A key 
   * @return A String array with values 
   */
  public static int[] getIntValues(String key)
  {
    String[] s= split(myConfig.getProperty(key), null);
    
    if ( s == null || s.length == 0 )
	return (int[])null;

    int[] i= new int[s.length];
    
    for ( int j= 0; j<s.length; j++ ) {
      try {
	i[j]= Integer.parseInt(s[j].trim());
      }
      catch (NumberFormatException  ne) {
	return (int[])null;
      }
    }
    return i;
  }



  /**
   * Get String value for given property. 
   *
   * @param key A key 
   * @return A String value for the key
   */
  public static String get(String key)
  {
    String s= myConfig.getProperty(key);
    if ( s == null )
	return (String)s;
    return s;
  }
  

  /**
   * Get String value for given property. If no value is
   * found return the given default value.
   *
   * @param key A key 
   * @param def the default value to return if no value is found
   * @return A String value for the key
   */
  public static String get(String key, String def)
  {
    return getDefault(key, def);
  }
  

  /**
   * Get Integer value for given property.
   *
   * @param key A key 
   * @return An Integer value for the key
   */
  public static Integer getInteger(String key)
  {
    String i= myConfig.getProperty(key);
    if ( i == null )
	return (Integer)null;
    try {
      return new Integer(i.trim()); 
    } catch (NumberFormatException n) {
      return (Integer)null;
    }
  }

 
  /**
   * Get Integer value for given property. If no value is
   * found return the given default value.
   *
   * @param key A key 
   * @param def the default value to return if no value is found
   * @return An Integer value for the key
   */
  public static Integer getInteger(String key, Integer def)
  {
    String i= getDefault(key, def.toString());
    try {
      return (new Integer(i.trim())); 
    } catch (NumberFormatException n) {
      return (Integer)null;
    }
  }


  /**
   * Get Boolean value for given property
   *
   * @param key A key 
   * @return A value for the key
   */
  public static Boolean getBoolean(String key)
  {
    String b= myConfig.getProperty(key);
    if ( b == null )
	return (Boolean)null;
    return (new Boolean(b.trim())); 
  }  


  /**
   * Get Boolean value for given property. If no value is
   * found return the given default value.
   *
   * @param key A key 
   * @param def the default value to return if no value is found
   * @return A value for the key
   */
  public static Boolean getBoolean(String key, Boolean def)
  {
    String b= getDefault(key, def.toString());
    return (new Boolean(b.trim())); 
  }  



  
  /**
   * Get Font value for given property. If no value is
   * found return Font(sanserif-plan-12) as default value.
   *
   * @param key A key 
   * @return A value for the key
   */
  public static Font getFont(String key)
  {
    String f= myConfig.getProperty(key);
    if ( f == null )
	return Font.decode("SansSerif-plain-12");
    return Font.decode(f.trim()); 
  }  


  /**
   * Get Color value for given property. If no value is
   * found return Color.black as default value.
   *
   * @param key A key 
   * @return A value for the key
   */
  public static Color getColor(String key)
  {
    String c= myConfig.getProperty(key);
    if ( c == null )
	return Color.black;
    return Color.decode(c.trim()); 
  }  


  // -------------------- Get primitive types ----------------------- //


  /**
   * Get int value for given property. If no value is
   * found return NO_INT_VALUE;
   *
   * @param key A key 
   * @return An int value for the key
   */
  public static int getint(String key)
  {
    try {
      return getInteger(key).intValue();
    }catch(NullPointerException n) { return NO_INT_VALUE; }
  }


  /**
   * Get int value for given property. If no value is
   * found return the given default value.
   *
   * @param key A key 
   * @param def the default value to return if no value is found
   * @return An int value for the key
   */
  public static int getint(String key, int def)
  {
    try {
      return getInteger(key,new Integer(def)).intValue();
    }catch(NullPointerException n) { return NO_INT_VALUE; }
  }

  
  /**
   * Get boolean value for given property
   *
   * @param key A key 
   * @return A primitive boolean value for the key
   */
  public static boolean getboolean(String key)
  {
    return getBoolean(key).booleanValue();
  }  


  /**
   * Get boolean value for given property. If no value is
   * found return the given default value.
   *
   * @param key A key 
   * @param def the default value to return if no value is found
   * @return A primitive boolean value for the key
   */
  public static boolean getboolean(String key, boolean def)
  {
    return getBoolean(key, new Boolean(def)).booleanValue();
  }  


  // ---------------------------------------------------------------- //


  /**
   * Composes string containing all properties and their values
   * 
   * @return A string with the content nicely formated
   */
  public String toString()
  {
    String      s="\n";
    Enumeration enum;
    Object      key;
    
    enum = myConfig.keys();
    
    while(enum.hasMoreElements())
    {
      key = enum.nextElement();
      s = s.concat("\t"+key+": "+"\t\t"+myConfig.get(key)+"\n");
    }
    
    return s;
  }


  /**
   * List properties to stdout
   */
  public void list()
  {
    myConfig.list(System.out);
  }


  
  // ---------------------------------------------------------------- //


  /**
   * Returns default if key has no value, else return value of key
   * @return value of key if exist, else default
   */
  private static String getDefault(String key, String def)
  {
    String v= myConfig.getProperty(key);
    if ( v == null || v.length() == 0 )
	return def.trim();
    return v.trim();
  }



  /** 
   * Split the given string on whitespace or the given delimiter and
   * return the substrings. If delimiter is null, whitespace is used
   * as default delimiter.
   *
   * @param s A String to split on whitespace or the given delimiter
   * @param delim an optional delimiter to split the strings on
   * @return an array of substrings 
   */
  private static String[] split(String s, String delim) 
  {
    Vector v= new Vector();
    StringTokenizer t;
    if ( delim != null )
	t= new StringTokenizer(s,delim,false);
    else 
	t= new StringTokenizer(s);

    String array[];
    
    while (t.hasMoreTokens())
	v.addElement(((String)t.nextToken()).trim());
    array= new String[v.size()];
    v.copyInto(array);
    return array;
  }

}

		
