package org.jcon.ba.lib;

import org.jcon.param.util.build.BuildParamEvent;
import org.jcon.param.util.build.BuildParamListener;
import org.jcon.util.DataLib;
import org.jcon.util.GenLib;
import org.jcon.util.msg.Message;
import org.jcon.util.service.ContainerServices;
import org.jcon.util.service.ContainerServicesUser;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Hashtable;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.Vector;

/**
* This class manages the locale related data for a system.
* It's main responsibility is to provide locale dependent
* translations and formatting on request. It supports only
* one Locale at a time. 
* <p>
* The MarkerClassName and Locale must be set before the
* class can be used for localization.
*
* @author Jack Harich
*/
public class LocaleMgr implements BuildParamListener,
	ContainerServicesUser {

//---------- Private Fields ------------------------------
// properties
private String markerClassName;
private Locale locale; // *** = new Locale("en", "US"); // Default
private ContainerServices containerServices;

// Internal
private Object  markerClass; 
private boolean initialized;

// Key = area, Object = ResourceBundle
private Hashtable areaBundles = new Hashtable();

private static final String INTZ = "INTZ";
private static final String NOT_FOUND = "#NOTFOUND#";

//---------- BuildParamListener Implementation -----------
public String[] getBuildParamKeys() {
	return new String[] {INTZ};
}
public void processBuildParamEvent(BuildParamEvent evt) {
	// We only process INTZ replacements
	if (! evt.getBuildKey().equals(INTZ)) return;
	
	// value is of the format "area.key"
	// Get area and key from value
    String value = evt.getBuildValue();
    int dotIndex = value.indexOf(".");
    String area = value.substring(0, dotIndex);
    String key = value.substring(dotIndex + 1);

    //print(".replaceParamText() - value = '" + value + "'");
    //    print(" - area = " + area + ", key = " + key);

	// Use area and key to set replacement
	String newValue = getString(area, key);
	//print(" - replacing " + value + " with " + newValue);
    evt.setReplacement(newValue);
}
//---------- ContainerServicesUser Implementation --------
public void setContainerServices(ContainerServices services) {
    containerServices = services;
} 
public String[] getContainerServicesInterests() {
    return new String[] {"AcquireSystemProperty"};
}
//---------- Properties ----------------------------------
//----- markerClassName
/**
* Sets the marker for the locale property files root
* directory. Each locale must have "area files" which are
* used in methods such as getString(area, key). The files
* must contain the keys the system needs.
*/
public void setMarkerClassName(String markerClassName) {
	this.markerClassName = markerClassName;
	markerClass = GenLib.createInstance(markerClassName);
	resetState();
}
public String getMarkerClassName() {
	return markerClassName;
}
//----- locale
/**
* Sets the Locale. The variant may be null. The default
* locale is en_US.
* <p>
* When the arguments are concatanated with underscores,
* it forms the localeID that is the directory the locale
* files are in. For example "en_US" is the master locale.
*
* @param  language  the standard langauge identifier, such as "en" or "ja".
* @param  country   the standard country indntifier, such as "US" or "JA".
* @param  variant   the optional variant.
*/
public void setLocale(String language, String country,
									String variant) {
	if (variant == null) {
		locale = new Locale(language, country);
	} else {
		locale = new Locale(language, country, variant);
	}
	//print(".setLocale() - Default before set = " + Locale.getDefault());
	Locale.setDefault(locale);
	//print(".setLocale() - Default after set = " + Locale.getDefault());
	resetState();
}
//---------- Public Methods ------------------------------
/**
* Returns the localized String for the area and key.
* The area is relative to the marker and can contain
* directory names separated by "/". The last area word is
* the file name. All file types must be "locale". 
* <p>
* For example
* getString("Window1", "UserID") would return "User ID" 
* from the file "Window1.locale" in the en_US directory
* under the directory with the marker class. 
* 
* @param  area  identifies the group of keys, such as the
*               name of a window.
*
* @param  key   identifies text needing localization, such
*      			as a label, window title or button label.
*
* @return       the localized String or "#NOTFOUND#" if error.
*/
public String getString(String area, String key) {
  try {
    ResourceBundle bundle = getBundle(area);
    if (bundle == null) {
      return NOT_FOUND; // Error already shown
    } else {
      return bundle.getString(key);
    }
  } catch (java.util.MissingResourceException e) {
    print("error looking for resource: " + area + "." + key);
    throw e;
  }
}
/**
* Returns the localized String for the area, key and values.
* This is used for text that has data values in it.
*
* @param  area   same as getString().
* @param  key    same as getString().
* @param  values the data values that need to be put into
*                localized text. Their order and count must
*				 agree with the stored translation text.
*
* @return        the localized String or "#NOTFOUND#" if error.
*/
public String getFormattedString(String area, String key,
										String[] values) {
	ResourceBundle bundle = getBundle(area);
	if (bundle == null) {
		return NOT_FOUND; // Error already shown
	} else {
		String text = bundle.getString(key);
		return MessageFormat.format(text, values);
	}
}
//---------- Private Methods -----------------------------
/**
* Returns the bundle for the area using the current
* markerClass and locale. The file must be a properties
* type file.
*
* @param  area may have relative file prefix, ie
*         "work/window2". An area is a group of Locale 
*         text Strings that apply to an area such as a
*         window or layer.
*
* @return  bundle for area in markerClass or complains and
*          returns null if not found.
*/
// Based on code in java.util.ResourceBundle
// If necessary we will make this public

private ResourceBundle getBundle(String area) {
	ResourceBundle bundle = (ResourceBundle)areaBundles.get(area);
	if (bundle == null) {
		checkInit();
		String resourceName = locale + "/" + area + ".locale";
		InputStream stream = markerClass.getClass()
			.getResourceAsStream(resourceName);
		if (stream == null) {
			GenLib.error("LocaleMgr.getBundle()", "No bundle " +
			"found for markerClass " + markerClass.getClass().getName() + " " +
			"for resource " + resourceName);
			return null;
		}
		stream = new BufferedInputStream(stream);
		try {
			bundle = new PropertyResourceBundle(stream);
			areaBundles.put(area, bundle);
			
		} catch(IOException ex) {
			GenLib.exception("LocaleMgr.getBundle()", "Failure " +
			"for markerClass " + markerClass.getClass().getName() + " " +
			"using area " + area + " when reading bundle.", ex);
			return null;
		}
	}
	return bundle;
}
// Get the locale from SystemProperties,
// Example: LocaleID is: en_US
private void checkInit() {
    if (initialized) return;
    Message message;
    
    // Get system property LocaleID
    message = new Message("AcquireSystemProperty");
    message.setString("PropertyName", "localeid=");
    containerServices.sendMessage(message);
    String localeID = message.getString("PropertyValue");
print(" - Acquired LocaleID= " + localeID);    
    if (localeID == null) localeID = "en_US"; // Default
    
    // Create locale
    String[] args = DataLib.convertDelimStringToArray(localeID, "_");
    String language = args[0];
    String country = args[1];
    String variant = (args.length < 3 ? null : args[2]);
    setLocale(language, country, variant);
    
    initialized = true;
}
private void resetState() {
	areaBundles.clear();
}
//--- Std
private static void print(String text) {
    System.out.println("LocaleMgr" + text);
}

} // End class
