/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.util;

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

/**
 * A class which centralizes a number of "international" resources, such as locales,
 * country lists, and language lists.
 *
 * @author Eric J. Bowersox &lt;erbo@silcom.com&gt;
 * @version X
 */
public class International
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

  private static International self = new International();  // me

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private List country_list = null;    // list of Country objects
  private Map country_map = null;      // mapping from codes to Country objects
  private List language_list = null;   // list of Language objects
  private Map language_map = null;     // mapping from codes to Language objects

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  /**
   * Constructs a new instance of the <CODE>International</CODE> object.  Only one instance
   * of this object is ever created.
   */
  private International()
  { // do nothing
  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  /**
   * Loads the internal list of countries from a resource file.
   *
   * @exception RuntimeException If an I/O error occurred while loading the country list.
   */
  private synchronized void loadCountryList()
  {
    if ((country_list!=null) || (country_map!=null))
      return;  // already loaded

    // Create temporary list and map to hold read data.
    ArrayList tmp_list = new ArrayList();
    HashMap tmp_map = new HashMap();

    try
    { // Load the country properties file.
      BufferedReader data =
          new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("countries.properties")));
      String l;  // temporary line
      while ((l = data.readLine())!=null)
      { // read lines from the properties file
	l = l.trim();
	if ((l.length()==0) || (l.startsWith("#")))
	  continue;  // blank line or comment line
	int pos = l.indexOf('=');
	if (pos<0)
	  continue;  // no properties - just forget this line
	Country c = new Country(l.substring(0,pos),l.substring(pos+1));
	tmp_list.add(c);
	tmp_map.put(c.getCode(),c);

      } // end while

    } // end try
    catch (IOException e)
    { // IO error loading country properties...
      throw new RuntimeException("Error loading country.properties: " + e.getMessage());

    } // end catch

    // Set up the lists, which are considered "unmodifiable."
    tmp_list.trimToSize();
    country_list = Collections.unmodifiableList(tmp_list);
    country_map = Collections.unmodifiableMap(tmp_map);

  } // end loadCountryList

  /**
   * Loads the internal list of languages from a resource file.
   *
   * @exception RuntimeException If an I/O error occurred while loading the language list.
   */
  private synchronized void loadLanguageList()
  {
    if ((language_list!=null) || (language_map!=null))
      return;  // already loaded

    // Create temporary list and map to hold read data.
    ArrayList tmp_list = new ArrayList();
    HashMap tmp_map = new HashMap();

    try
    { // Load the language properties file.
      BufferedReader data =
          new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("languages.properties")));
      String l;  // temporary line
      while ((l = data.readLine())!=null)
      { // read lines from the properties file
	l = l.trim();
	if ((l.length()==0) || (l.startsWith("#")))
	  continue;  // blank line or comment line
	int pos = l.indexOf('=');
	if (pos<0)
	  continue;  // no properties - just forget this line
	Language lng = new Language(l.substring(0,pos),l.substring(pos+1));
	tmp_list.add(lng);
	tmp_map.put(lng.getCode(),lng);

      } // end while

    } // end try
    catch (IOException e)
    { // IO error loading language properties...
      throw new RuntimeException("Error loading language.properties: " + e.getMessage());

    } // end catch

    // Set up the lists, which are considered "unmodifiable."
    tmp_list.trimToSize();
    language_list = Collections.unmodifiableList(tmp_list);
    language_map = Collections.unmodifiableMap(tmp_map);

  } // end loadLanguageList

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  /**
   * Returns the list of defined countries.
   *
   * @return The list of <CODE>Country</CODE> objects that are currently defined.
   */
  public List getCountryList()
  {
    loadCountryList();
    return country_list;

  } // end getCountryList

  /**
   * Returns the <CODE>Country</CODE> object with the specified code.
   *
   * @param code The country code to match.
   * @return The matching <CODE>Country</CODE> object, or <CODE>null</CODE> if no country matched.
   */
  public Country getCountryForCode(String code)
  {
    if (code==null)
      return null;
    loadCountryList();
    return (Country)(country_map.get(code.trim().toUpperCase()));

  } // end getCountryForCode

  /**
   * Returns the list of defined languages.
   *
   * @return The list of <CODE>Language</CODE> objects that are currently defined.
   */
  public List getLanguageList()
  {
    loadLanguageList();
    return language_list;

  } // end getLanguageList

  /**
   * Returns the <CODE>Language</CODE> object with the specified code.
   *
   * @param code The language code to match.
   * @return The matching <CODE>Language</CODE> object, or <CODE>null</CODE> if no language matched.
   */
  public Language getLanguageForCode(String code)
  {
    if (code==null)
      return null;
    loadLanguageList();
    return (Language)(language_map.get(code.trim()));

  } // end getLanguageForCode

  /**
   * Creates a <CODE>Locale</CODE> from a standard descriptor string.
   *
   * @param streq The string equivalent of the Locale to be created.
   * @return The corresponding <CODE>Locale</CODE>, or the default <CODE>Locale</CODE> if the parameter is
   *         <CODE>null</CODE> or the empty string.
   */
  public Locale createLocale(String streq)
  {
    if ((streq==null) || (streq.length()==0))
      return Locale.getDefault();  // no locale
    int p1 = streq.indexOf('_');
    if (p1<0)
      return new Locale(streq,"");  // language but no country specified
    String x_lang = streq.substring(0,p1);
    int p2 = streq.indexOf('_',p1+1);
    if (p2<0)
    { // there's only one underscore - figure out what part the last part is
      String lastpart = streq.substring(p1+1);
      if (lastpart.length()==2)
	return new Locale(x_lang,lastpart);  // language + country
      else
	return new Locale(x_lang,"",lastpart); // language + country(null) + variant

    } // end if

    // do all three variants
    return new Locale(x_lang,streq.substring(p1+1,p2),streq.substring(p2+1));

  } // end createLocale

  /*--------------------------------------------------------------------------------
   * External static operations
   *--------------------------------------------------------------------------------
   */

  /**
   * Returns the singleton instance of the <CODE>International</CODE> object.
   *
   * @return The singleton instance of the <CODE>International</CODE> object.
   */
  public static International get()
  {
    return self;

  } // end get()

} // end class International
