/*
 * $Id: Translation.java 4536 2008-11-25 16:39:05Z uckelman $
 *
 * Copyright (c) 2007 by Rodney Kinney, Brent Easton
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License (LGPL) as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, copies are available
 * at http://www.opensource.org.
 */
package VASSAL.i18n;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Locale;
import java.util.Properties;

import VASSAL.build.AbstractConfigurable;
import VASSAL.build.AutoConfigurable;
import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.configure.Configurer;
import VASSAL.configure.ConfigurerFactory;
import VASSAL.tools.ArchiveWriter;
import VASSAL.tools.ReadErrorDialog;
import VASSAL.tools.io.IOUtils;

public class Translation extends AbstractConfigurable
                         implements Comparable<Translation> {
  public static final String LOCALE = "locale"; //$NON-NLS-1$
  protected Locale locale;
  protected boolean dirty = false;
  protected Properties localProperties;

  public Translation() {
    locale = new Locale(Locale.getDefault().getLanguage());
  }

  public String[] getAttributeDescriptions() {
    return new String[]{"Locale:  "};
  }

  public Class<?>[] getAttributeTypes() {
    return new Class<?>[]{
      LocalePrompt.class
    };
  }

  public static class LocalePrompt implements ConfigurerFactory {
    public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
      return new LocaleConfigurer(key, name, "");
    }
  }

  public String[] getAttributeNames() {
    return new String[]{LOCALE};
  }

  public String getAttributeValueString(String key) {
    if (LOCALE.equals(key)) {
      return LocaleConfigurer.localeToString(locale);
    }
    else
      return null;
  }

  public void setAttribute(String key, Object value) {
    if (LOCALE.equals(key)) {
      locale = LocaleConfigurer.stringToLocale((String) value);
      setConfigureName(locale.getDisplayName());
    }
  }

  protected String getDescription() {
    return locale.getDisplayName(Locale.getDefault());
  }
  
  public String getLanguageCode() {
    return locale.getLanguage();
  }

  public String getConfigureName() {
    return getDescription();
  }

  @SuppressWarnings("unchecked")
  public Class[] getAllowableConfigureComponents() {
    return new Class[0];
  }

  public HelpFile getHelpFile() {
    return null;
  }

  public void removeFrom(Buildable parent) {
    Localization.getInstance().removeTranslation(this);
  }

  public void addTo(Buildable parent) {
    Localization.getInstance().addTranslation(this);
    if (!GameModule.getGameModule().isLocalizationEnabled()) {
      try {
        loadProperties();
      }
// FIXME: review error message
// FIXME: should we catch a FileNotFoundException here instead?
      catch (IOException e) {
        // Fail quietly: This error will occur when adding a new translation.
      }
    }
  }

  /**
   * Set a property into our property map. i.e. an attribute has been translated
   * 
   * @param key
   *          property key
   * @param value
   *          property value
   */
  public void setProperty(String key, String value) {
    if (value == null || value.length() == 0) {
      getProperties().remove(key);  
    }
    else {
      getProperties().setProperty(key, value);
    }
    dirty = true;
  }

  /**
   * Return true if this translation has unsaved modifications
   * 
   * @return true if undaved changes
   */
  public boolean isDirty() {
    return dirty;
  }

  /**
   * Return the translation for the supplied key
   * 
   * @param s
   * @return
   */
  public String translate(String key) {
    return getProperties().getProperty(key);
  }

  /**
   * Load properties from the bundle file in the module/extension
   * @throws IOException 
   * 
   */
  protected void loadProperties() throws IOException {
    if (localProperties == null) {
      localProperties = new Properties();
    }

    if (GameModule.getGameModule() != null) {
      BufferedInputStream in = null;
      try {
        try {
          in = new BufferedInputStream(
            GameModule.getGameModule()
                      .getDataArchive()
                      .getInputStream(getBundleFileName())
          );
        }
        catch (FileNotFoundException e) {
          // ignore, properties have not been saved yet
          dirty = false;
          return;
        }
      
        localProperties.load(in);
        in.close();
      }
      finally {
        IOUtils.closeQuietly(in);
      }
    }
       
    dirty = false;
  }

  protected VassalResourceBundle getBundle() throws IOException {
    BufferedInputStream in = null;
    try {
      in = new BufferedInputStream(
        GameModule.getGameModule()
                  .getDataArchive()
                  .getInputStream(getBundleFileName())
      );

      final VassalResourceBundle b = new VassalResourceBundle(in);
      in.close();
      return b;
    }
    finally {
      IOUtils.closeQuietly(in);
    }
  }

  /**
   * Reload the properties from the module/extension
   * @throws IOException 
   * 
   */
  public void reloadProperties() throws IOException {
    localProperties = new Properties();
    loadProperties();
  }

  /**
   * Save the properties back to the module/extension
   * @throws IOException 
   * 
   */
  protected void saveProperties() throws IOException {
    final ByteArrayOutputStream out = new ByteArrayOutputStream();
    getProperties().store(out, "Module translation");

    final ArchiveWriter writer = GameModule.getGameModule().getArchiveWriter();
    if (writer != null) {
      writer.addFile(getBundleFileName(), out.toByteArray());
      dirty = false;
    }
  }

  /**
   * Return the properties map for this translation. Create and load from
   * the module if necessary.
   * 
   * @return properties
   */
  public Properties getProperties() {
    if (localProperties == null) {
      try {
        loadProperties();
      }
      catch (IOException e) {
        ReadErrorDialog.error(e, getBundleFileName());
        localProperties = new Properties();
      }
    }
    return localProperties;
  }

  /**
   * Build the bundle name
   * 
   * @return bundle name
   */
  public String getBundleName() {
    return Resources.MODULE_BUNDLE + "_" + locale.getLanguage() //$NON-NLS-1$
        + (locale.getCountry().length() > 0 ? ("_" + locale.getCountry()) : ""); //$NON-NLS-1$ //$NON-NLS-2$
  }

  /**
   * Build the bundle file name
   * 
   * @return bundle file name
   */
  public String getBundleFileName() {
    return getBundleName() + ".properties"; //$NON-NLS-1$
  }

  public int compareTo(Translation o) {
    return getDescription().compareTo(o.getDescription());
  }

  public Locale getLocale() {
    return locale;
  }
}
