/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.keyboardplaying.xtt.ui.i18n;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;

import org.keyboardplaying.xtt.configuration.PreferencesHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * A helper for internationalization.
 *
 * @author Cyrille Chopelet (https://keyboardplaying.org)
 */
public class I18nHelper {

    /** The key the locale is stored under in the preferences. */
    protected static final String LOCALE_PREFKEY = "project.dir";

    private static final Logger LOG = LoggerFactory.getLogger(I18nHelper.class);

    private static final String BUNDLE_BASE_NAME = "org.keyboardplaying.xtt.ui.i18n.Messages";
    private static final String LOCALE_REGEX = "([a-z]{2})(?:_([A-Z]{2})?(?:_([a-zA-Z]+))?(?:_#.+)?)?";

    @Autowired
    private PreferencesHelper preferences;

    private Set<I14ed> i14ed = new HashSet<>();
    private ResourceBundle bundle;

    /** Creates a new instance and sets it the locale to its default. */
    public I18nHelper() {
        updateResourceBundle(Locale.getDefault());
    }

    /** Initializes the internationalization helper with the saved locale. */
    @PostConstruct
    public void init() {
        String locale = preferences.get(LOCALE_PREFKEY);
        Locale l = locale == null ? Locale.getDefault() : parseLocale(locale);
        updateResourceBundle(l);
    }

    /**
     * Sets the preferences helper.
     *
     * @param preferences
     *            the preferences helper
     */
    public void setPreferences(PreferencesHelper preferences) {
        this.preferences = preferences;
    }

    protected void updateResourceBundle(Locale locale) {
        this.bundle = ResourceBundle.getBundle(BUNDLE_BASE_NAME, locale);
    }

    /**
     * Registers an {@link I14ed} component to be notified when the locale changes.
     *
     * @param internationalized
     *            the component to notify of locale changes
     */
    public void register(I14ed internationalized) {
        this.i14ed.add(internationalized);
    }

    /**
     * Parses the locale from the supplied representation.
     *
     * @param locale
     *            the string representation of a locale (e.g.: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr_MAC")
     * @return the associated {@link Locale} or default locale if format is incorrect
     */
    public Locale parseLocale(String locale) {
        Matcher m = Pattern.compile(LOCALE_REGEX).matcher(locale);
        Locale l;

        if (!m.matches()) {
            LOG.warn(locale + " does not match the expected format for a locale");
            l = Locale.getDefault();
        } else {
            String language = m.group(1);
            String country = m.group(2);
            String variant = m.group(3);
            if (variant != null) {
                l = new Locale(language, country, variant);
            } else if (country != null) {
                l = new Locale(language, country);
            } else {
                l = new Locale(language);
            }
        }

        return l;
    }

    /**
     * Returns the current locale.
     *
     * @return the current locale
     */
    public Locale getLocale() {
        return bundle.getLocale();
    }

    /**
     * Sets the locale for the messages to display.
     *
     * @param locale
     *            the locale to use
     */
    public void setLocale(Locale locale) {
        updateResourceBundle(locale);
        preferences.set(LOCALE_PREFKEY, locale.toString());
        for (I14ed internationalized : i14ed) {
            internationalized.updateMessages(this);
        }
    }

    /**
     * Gets the string message corresponding to the key.
     *
     * @param key
     *            the key for the desired string
     * @return the string for the given key
     */
    public String getMessage(String key) {
        return bundle.getString(key);
    }

    /**
     * Returns a list of all locales a bundle has been written for.
     *
     * @return a list of all locales availables within the application
     */
    public List<Locale> getAvailableLocales() {

        /* Convert base name to a path. */
        String bundlePath = BUNDLE_BASE_NAME.replaceAll("\\.", "/") + "_%s.properties";

        List<Locale> available = new ArrayList<>();

        /* Loop over all locales to find those we do support. */
        // Get a list of all locales the JVM supports
        Locale[] locales = Locale.getAvailableLocales();
        // Loop over those to find available locales
        for (Locale locale : locales) {
            URL bundleUrl = ClassLoader.getSystemResource(String.format(bundlePath, locale.toString()));
            if (bundleUrl != null) {
                available.add(locale);
            }
        }

        /* Return the list. */
        return available;
    }
}

@@ -1562,19 +1562,14 @@
  = %22
-project.dir
+locale
 %22;%0A%0A

Fix preference key for locale