/*
 * (C) 2004 - Geotechnical Software Services
 * 
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation; either 
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 
 * MA  02111-1307, USA.
 */
package no.geosoft.cc.country;



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



/**
 * A country management singelton module. Useful for presentation of
 * sorted list of countries in GUI like in country combo boxes etc.
 * <p>
 * The country manager instantiate country objects for the countries
 * listed in the <tt>countries.txt</tt> resource of this package. The file
 * lists ISO codes and the official english names for countries of the world.
 * The countries.txt file has the following layout:
 *
 * <pre>
 *    AF; AFGHANISTAN
 *    AL; ALBANIA
 *    DZ; ALGERIA
 *    AS; AMERICAN SAMOA
 *    AD; ANDORRA
 *    AO; ANGOLA
 *    :
 * </pre>
 *
 * The country name is the defualt name used if a localized mapping is
 * not found, or the client askes for countries independent of locale.
 * <p>
 * TODO: Connect to an online resource to pick country ISO codes.
 * <p>
 * For internal application usage, only ISO codes for countries should
 * be used (i.e. for persistent storage store the ISO code rather than
 * a country name).
 * <p>
 * In user interfaces, localized names should be used according
 * to the locale of the application or preferred locale selected by the
 * user. Localized names are maintained in resource bundles picked from
 * property files with name <tt>Messages_xx_XX.properties</tt> where
 * <tt>xx</tt> is the language specifier and <tt>XX</tt> is the country
 * specifier. Provide localized country name mappings for all locales
 * supported by the client application. The properties files should be
 * located in the same package as the CountryManager. The properties
 * files has the following format (Messages_en_US.properties):
 *
 * <pre>
 *    AF = Afghanistan
 *    AL = Albania
 *    DZ = Algeria
 *    AS = American Samoa
 *    AD = Andorra
 *    AO = Angola
 *    :
 * </pre>
 *
 * <p>
 * Typical usage:
 * <pre>
 *    // Get a specific country
 *    Country country = CountryManager.getInstance().getCountry ("AF", french);
 *
 *    // Get world countries localized to french (assuming
 *    // Messages_fr_FR.properties is available).
 *    Locale french = new Locale ("fr", "FR");
 *    Collection countries = CountryManager.getInstance.getCountries (french);
 *
 *    // Put countries (sorted according to locale) in a combo box
 *    TreeSet sorted = new TreeSet (countries);
 *    JComboBox countriesCombo = new JComboBox (new Vector (sorted));
 * </pre>
 *
 * @see <a href="http://www.iso.org">ISO standard</a>
 * 
 * @author <a href="mailto:info@geosoft.no">GeoSoft</a>
 */   
public class CountryManager
{
  private static CountryManager  instance_ = new CountryManager();

  private HashMap  countries_;
  


  /**
   * Create country manager.
   * Private to prevent instantiation.
   */
  private CountryManager()
  {
    countries_ = null;
  }


  
  /**
   * Retur sole instance of this class.
   * 
   * @return  CountryManager singleton instance.
   */
  public static CountryManager getInstance()
  {
    return instance_;
  }

  

  /**
   * Return countries of the world (as specified in countries.txt) using
   * the default (official ISO name) country names.
   *
   * @return  Countries of the world.
   */
  public Collection getCountries()
  {
    if (countries_ == null)
      loadCountries();

    return countries_.values();
  }


  
  /**
   * Return countries of the world (as specified in countries.txt) with
   * names according  to the specified locale. The name mappings must be
   * available in a Messages_xx_XX.properties file where xx and XX match
   * the language and country of the specified locale respectively.
   * <p>
   * If the associated properties file is not found, the country names
   * are set according to the default ISO standard.
   *
   * @param locale  Locale to setting country names.
   * @return        Countries of the world.
   */
  public Collection getCountries (Locale locale)
  {
    if (countries_ == null)
      loadCountries();

    String bundleName = getClass().getPackage().getName() + ".Messages";
    ResourceBundle resourceBundle = ResourceBundle.getBundle (bundleName, locale);
    
    Collection countries = countries_.values();

    Collection sorted = new TreeSet();
    
    for (Iterator i = countries.iterator(); i.hasNext(); ) {
      Country country = (Country) i.next();

      Country clone = new Country (country);

      try {
        String localizedName = resourceBundle.getString (country.getIsoCode());
        clone.setName (localizedName);
      }
      catch (Exception exception) {
      }

      sorted.add (clone);
    }

    return sorted;
  }
    
                                  
  
  /**
   * Get country for specified ISO code.
   * 
   * @param isoCode  ISO code of country to find.
   * @return         Requested country (with default ISO name) (or null if
   *                 ISO code is unknown).
   */
  public Country getCountry (String isoCode)
  {
    if (countries_ == null)
      loadCountries();

    return (Country) countries_.get (isoCode);
  }



  /**
   * Get country for specified ISO code.
   * 
   * @param isoCode  ISO code of country to find.
   * @param locale   Locale to control name of requested country.
   * @return         Requested country (or null if ISO code is unknown).
   */
  public Country getCountry (String isoCode, Locale locale)
  {
    if (countries_ == null)
      loadCountries();

    Country country = (Country) countries_.get (isoCode);
    Country clone = null;
    
    if (country != null) {
      clone = new Country (country);

      String bundleName = getClass().getPackage().getName() + ".Messages";
      ResourceBundle resourceBundle = ResourceBundle.getBundle (bundleName, locale);

      try {
        String localizedName = resourceBundle.getString (country.getIsoCode());
        clone.setName (localizedName);
      }
      catch (Exception exception) {
      }
    }

    return clone;
  }
  

  
  /**
   * Load all countries from countries.txt file.
   */
  private void loadCountries()
  {
    countries_ = new HashMap();
    
    String fileName        = "countries.txt";
    String packageName     = getClass().getPackage().getName();
    String packageLocation = packageName.replace ('.', '/');
    String filePath        = "/" + packageLocation + "/" + fileName;

    InputStream stream = getClass().getResourceAsStream (filePath);
    BufferedReader reader = new BufferedReader (new InputStreamReader (stream));

    try {
      String line = reader.readLine();
      while (line != null) {
        StringTokenizer tokenizer = new StringTokenizer (line, ";");
        String isoCode = tokenizer.nextToken().trim();
        String name    = tokenizer.nextToken().trim();

        Country country = new Country (isoCode);
        country.setName (name);

        countries_.put (isoCode, country);
        
        line = reader.readLine();
      }
    }
    catch (IOException exception) {
    }
    finally {
      try {stream.close();} catch (Exception e) {}
    }
  }
  
  
  
  /**
   * Testing the class.
   * 
   * @param args  Not used.
   */
  public static void main (String[] args)
  {
    Locale locale = new Locale ("no", "NO");
    
    CountryManager countryManager = CountryManager.getInstance();

    Collection countries = countryManager.getCountries (locale);
    for (Iterator i = countries.iterator(); i.hasNext(); ) {
      Country country = (Country) i.next();
      System.out.println (country);
    }
  }
}