/** Copyright © 2003 by Jean-Hugues de Raigniac <jhraigniac@workingfrog.org>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

package org.workingfrog.i18n.util;

import java.awt.Toolkit;

import java.io.File;
import java.io.IOException;

import java.net.MalformedURLException;
import java.net.JarURLConnection;
import java.net.URL;

import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ListResourceBundle;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.StringTokenizer;
import java.util.jar.JarFile;

import javax.swing.KeyStroke;

/**
 *  This class manages the resource bundle files needed to localize the
 *  application. All registered resource files are searched in order
 *  to find the localization of a given string.
 *
 * @author Jean-Hugues de Raigniac
 */
public class Localizer {

    /** will replace all loading mechanism. */
    private static Loader loader = new Loader();

    /**
     * The method changes the current Locale to the given one.
     * The resources bound to the given Locale are also preloaded.
     * If the given Locale is not already registered, it will be
     * registered automatically.
     *
     * @see java.util.Locale
     */
    public static void switchCurrentLocale(Locale locale) {
        Locale.setDefault(locale);
        loader.reload();
    }
    
    /**
     * The method returns the current locale.
     *
     * @return The current locale
     */
    public static Locale getCurrentLocale() {
        return Locale.getDefault();
    }
    
    /**
     * The method returns all resources for the given locale.
     *
     * @param locale Resources are searched for this locale.
     * @return Map of all resources and their names bound to the given locale.
     */
    public static Map getResourcesFor(Locale locale) {
        return loader.getBundles(locale);
    }
    
    /**
     * The method adds a new resource under the given name.
     * The resource is preloaded and bound to a given Locale or to every
     * registered Locale, if no Locale is given.
     *
     * @param binding Name under which the resource should be registered.
     * @param resourceName Name of the resource to be registered.
     * @throws MissingResourceException when resourceName is invalid
     */
    public static synchronized void addResource
        (String binding, String resourceName) throws MissingResourceException {
        loader.loadResource(binding, resourceName);
    }

    /**
     * This function returns a localized string corresponding
     * to the specified key. Searching goes through all registered
     * ResourceBundles
     *
     * @param binding bundle binding
     * @param key String to be localized
     * @return First localization for the given string found in the registered
     * ResourceBundles, the key itself if no localization has been found.
     */
    public static String localize(String binding, String key) {
        ResourceBundle bundle = loader.getBundle(binding);
        Object object = null;
        if (LogLevel.isChecking(LogLevel.L10N)) {
            object = Localizer.getValue(bundle, key);
        } else {
            object = bundle.getString(key);
        }

        return (object == null) ? key : (String) object;
    }

    private static Object getValue (ResourceBundle bundle, String key) {
        try {
            // at this time there is only 2 child of ResourceBundle :
            // ListResourcebundle and PropertyResourceBundle.
            if (bundle instanceof ListResourceBundle) {
                return ((ListResourceBundle) bundle).handleGetObject(key);
            } else {
                return ((PropertyResourceBundle) bundle).handleGetObject(key);
            }
        } catch (MissingResourceException e) {
            return null;
        }
    }

    public static boolean containsKey(String binding, String key) {
        ResourceBundle bundle = loader.getBundle(binding);
        if (bundle == null) {
            return false;
        }

        return (Localizer.getValue(bundle, key) != null);
    }

    /**
     * This function returns a localized menu shortcut key
     * to the specified key.
     *
     * @param binding Name of resource to be searched.
     * @param key Shortcut string to be localized.
     * @return Localized KeyStroke object.
     */
    public static KeyStroke getShortcut(String binding, String key) {

        KeyStroke stroke = null;
        ResourceBundle resource = loader.getBundle(binding);
        try {
            Object obj = resource.getObject(key);
            if (obj instanceof KeyStroke) {
                stroke = (KeyStroke) obj;
            } else if (obj instanceof String) {
                boolean hasShortcutModifier = false;
                StringBuffer shortcutBuf = new StringBuffer();
                
                StringTokenizer tokenizer = new StringTokenizer((String) obj);
                while (tokenizer.hasMoreTokens()) {
                    String token = tokenizer.nextToken();
                    
                    if (token.equals("shortcut")) {
                        hasShortcutModifier = true;
                    } else {
                        shortcutBuf.append(token);
                        shortcutBuf.append(" ");
                    }
                }
                stroke = KeyStroke.getKeyStroke(shortcutBuf.toString());
                int modifiers = stroke.getModifiers() 
                    | (hasShortcutModifier
                       ? Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
                       : 0);
                int keyCode = stroke.getKeyCode();
                stroke = KeyStroke.getKeyStroke(keyCode, modifiers);
            }
        } catch (MissingResourceException e) { /** empty */
        } catch (ClassCastException e) { /** empty */
        } catch (NullPointerException e) { /** empty */ }
        return stroke;
    }

    public static Locale[] getLocales (Object object) {
        String[] names = null;
        HashSet set = new HashSet();

        //TODO unstable method
        names = Localizer.getListingFromDir();
//        if (object.getClass().getResource(".") == null) {
//            System.out.println("jar");
//            names = Localizer.getListingFromJar(object);
//        } else {
//            System.out.println("dir");
//            names = Localizer.getListingFromDir(object);
//        }

        // some cleaning
        for (int i = 0; i < names.length; i++) {
//            names[i] = names[i].substring(names[i].indexOf("ResourceBundle")
//                       + 14); // <=> "ResourceBundle.length()
//            names[i] = names[i].substring(0, names[i].lastIndexOf("."));
//            if (!names[i].equals("")) {
//                set.add(names[i].substring(1));
//            }
            // extension removed
            names[i] = names[i].substring(0, names[i].lastIndexOf("."));
            if (names[i].indexOf("_") > -1) {
                names[i] = names[i].substring(names[i].indexOf("_") + 1);
                set.add(names[i]);
            }
        }

        // Locale load
        Locale[] locales = new Locale[set.size()];
        int index = 0;
        Iterator i = set.iterator();
        while (i.hasNext()) {
            StringTokenizer tokens =
                new StringTokenizer((String) i.next(), "_");

            switch (tokens.countTokens()) {
                case 1: locales[index++] = new Locale(tokens.nextToken(), "");
                        break;
                case 2: locales[index++] = new Locale(tokens.nextToken(),
                            tokens.nextToken());
                        break;
                case 3: locales[index++] = new Locale(tokens.nextToken(),
                            tokens.nextToken(), tokens.nextToken());
                        break;
            }
        }

        return locales;
    }

    private static String[] getListingFromJar (Object object) {
        URL url = object.getClass().getResource(".");
        JarURLConnection uc = null;
        Enumeration enumeration = null;
        String classFile = "/" + (object.getClass().getName()).replace('.', '/')
            + ".class";
        String extForm =
            object.getClass().getResource(classFile).toExternalForm();
        String root =
            extForm.substring(0, extForm.length() - classFile.length());

        // If it's a jar, clean it up and make it look like a file url
        if (root.startsWith("jar:")) {
            root = root.substring(4);
            if (root.endsWith("!")) {
                root = root.substring(0, root.length() - 1);
            }
        }

        try {
            url = new URL("jar", "", root + "!/");
            uc = (JarURLConnection) url.openConnection();
            JarFile dir = uc.getJarFile();
            enumeration = dir.entries();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        java.util.Vector v = new java.util.Vector();
        while (enumeration.hasMoreElements()) {
            String file =
                ((java.util.jar.JarEntry) enumeration.nextElement()).getName();
            if (file.indexOf("i18n") > -1
                && (file.indexOf("ResourceBundle.") > -1)
                || file.indexOf("ResourceBundle_") > -1) {
                v.addElement(file.substring(file.lastIndexOf("/") + 1));
            }
        }
        v.trimToSize();
        Object[] array = v.toArray();
        String[] names = new String[array.length];
        for (int i = 0; i < array.length; i++) {
            names[i] = (String) array[i];
        }

        return names;
    }

    private static String[] getListingFromDir () {
		URL url = null;
		try {
            url = new File(Translator.getBundlesPath()).toURL();
		} catch (MalformedURLException e) {
            System.err.println("Bundles path \""
                + Translator.getBundlesPath() + "\" is not valid.");
			e.printStackTrace();
		}
        File dir = new File(url.getFile());
        return dir.list(ResourceBundleFilter.getInstance());
    }      

}

