/**
 * Copyright © CENRA (2016)
 *
 * remi.clement@espaces-naturels.fr
 * 
 * This software is a multi-platform and portable application based on 
 * the scientifical toolbox to monitor wetlands. With this toolbox it’s 
 * possible to calculate indicators «RhoMéO» to monitor quality, 
 * fonctionalities and urban and agricultural pressures of wetlands. 
 * Indicators are calculated using naturalist data entered and/or imported,
 * and geographic data downloaded from a geographic server.
 * 
 * This software is governed by the CeCILL license under French law and
 * abiding by the rules of distribution of free software.  You can  use, 
 * modify and/ or redistribute the software under the terms of the CeCILL
 * license as circulated by CEA, CNRS and INRIA at the following URL
 * "http://www.cecill.info". 
 * 
 * As a counterpart to the access to the source code and  rights to copy,
 * modify and redistribute granted by the license, users are provided only
 * with a limited warranty  and the software's author,  the holder of the
 * economic rights,  and the successive licensors  have only  limited
 * liability. 
 * 
 * In this respect, the user's attention is drawn to the risks associated
 * with loading,  using,  modifying and/or developing or reproducing the
 * software by the user in light of its specific status of free software,
 * that may mean  that it is complicated to manipulate,  and  that  also
 * therefore means  that it is reserved for developers  and  experienced
 * professionals having in-depth computer knowledge. Users are therefore
 * encouraged to load and test the software's suitability as regards their
 * requirements in conditions enabling the security of their systems and/or 
 * data to be ensured and,  more generally, to use and operate it in the 
 * same conditions as regards security. 
 * 
 * The fact that you are presently reading this means that you have had
 * knowledge of the CeCILL license and that you accept its terms.
*/
package fr.cenra.rhomeo.api;

import fr.cenra.rhomeo.core.RhomeoCore;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.StringJoiner;
import java.util.logging.Level;
import org.apache.sis.util.ArgumentChecks;

/**
 * An object able to provide some resource strings for given (or default) locales.
 * 
 * This interface provides a default implementation based on the class 
 * {@link ResourceBundle}. So, if you do not redefine its methods, make sure
 * the {@link ResourceBundle} associated to the class exists.
 * 
 * @author Samuel Andrés (Geomatys)
 */
public interface InternationalResource {
    
    /**
     * Default {@link InternationalResource} behaviour for a given class and a
     * given locale, retrieving the associated {@link ResourceBundle}.
     * 
     * The resource can be associated to a simple key or a complex one.
     * 
     * The default implementation assumes the key parts are separated by a dot.
     * 
     * For instance, let's assume the class my.package.MyClass implementq
     * {@link InternationalResource} without overriding methods and is 
     * associated to the following resource bundle : /my/package/MyClass.properties 
     * which content is below :
     * 
     * keyPart1.keyPart2=resource value
     * 
     * The resource string "resource value" can be retrived from a my.package.MyClass
     * instance, calling this method either specifying only one key part 
     * "keyPart1.keyPart2", or two key parts "keyPart1" and "keyPart2".
     * 
     * @param c
     * @param locale
     * @param keyParts
     * @return 
     * 
     * @throws IllegalArgumentException if the class parameter or the locale is null or no key is provided
     * @throws MissingResourceException if no resource bundle for the specified class can be found or no object for the key can be found
     */
    static String getResourceString(final Class<? extends InternationalResource> c, final Locale locale, final String... keyParts){
        ArgumentChecks.ensureNonNull("class", c);
        ArgumentChecks.ensureNonNull("locale", locale);
        ArgumentChecks.ensureNonNull("keyParts", keyParts);
        
        final ResourceBundle bundle;
        try{
            bundle = ResourceBundle.getBundle(c.getName(), locale);
        }
        catch(MissingResourceException e){
            // Warns about InternationalResource default behaviour is not fulfilled.
            RhomeoCore.LOGGER.log(Level.WARNING, "No resource found for the class {0}. Default {1} behaviour use resource bundle.", 
                    new Object[]{c.getCanonicalName(), InternationalResource.class.getSimpleName()});
            // Then, re-throw the exception…
            throw e;
        }
        
        final StringJoiner joiner = new StringJoiner(".");
        for(final String keyPart : keyParts){
            joiner.add(keyPart);
        }
        return bundle.getString(joiner.toString());
    }
    
    /**
     * 
     * @param c
     * @param keyParts
     * @return 
     * 
     * @see InternationalResource#getResourceString(java.lang.Class, java.util.Locale, java.lang.String...) , with default locale.
     * 
     * @throws IllegalArgumentException if the class parameter is null or no key is provided
     * @throws MissingResourceException if no resource bundle for the specified class can be found or no object for the key can be found
     */
    static String getResourceString(final Class<? extends InternationalResource> c, final String... keyParts){
        return getResourceString(c, Locale.getDefault(), keyParts);
    }
    
    /**
     * Use the default behaviour to retrieve the property mapping the given key for
     * the given class before formating the result using the given arguments.
     * 
     * Note the key cannot be split into dot-separated key parts because of the 
     * arguments vararg parameter.
     * 
     * @param c
     * @param locale
     * @param key
     * @param arguments
     * @return 
     * 
     * @see InternationalResource#getResourceString(java.lang.Class, java.util.Locale, java.lang.String...) , with default locale.
     * 
     * @throws IllegalArgumentException if the class parameter or the locale are null or no key is provided
     * @throws MissingResourceException if no resource bundle for the specified class can be found or no object for the key can be found
     */
    static String getFormatedResourceString(final Class<? extends InternationalResource> c, final Locale locale, final String key, final Object... arguments){
        return MessageFormat.format(getResourceString(c, locale, key), arguments);
    }
    
    /**
     * 
     * Same behaviour as {@link InternationalResource#getFormatedResourceString(java.lang.Class, java.util.Locale, java.lang.String, java.lang.Object...) }, 
     * with default locale.
     * 
     * @param c
     * @param key
     * @param arguments
     * @return 
     * 
     * @see InternationalResource#getResourceString(java.lang.Class, java.lang.String...).
     * 
     * @throws IllegalArgumentException if no key is provided
     * @throws MissingResourceException if no object for the key can be found
     */
    static String getFormatedResourceString(final Class<? extends InternationalResource> c, final String key, final Object... arguments){
        return MessageFormat.format(getResourceString(c, key), arguments);
    }
    
    /**
     * Retrieves a string resource for the given locale.
     * 
     * The resource can be associated to a simple key or a complex one.
     * 
     * The default implementation assumes the key parts are separated by a dot.
     * 
     * For instance, let's assume the class my.package.MyClass implementq
     * {@link InternationalResource} without overriding methods and is 
     * associated to the following resource bundle : /my/package/MyClass.properties 
     * which content is below :
     * 
     * keyPart1.keyPart2=resource value
     * 
     * The resource string "resource value" can be retrived from a my.package.MyClass
     * instance, calling this method either specifying only one key part 
     * "keyPart1.keyPart2", or two key parts "keyPart1" and "keyPart2".
     * 
     * @param keyParts
     * @param locale
     * @return 
     * 
     * @throws IllegalArgumentException if the locale is null or no key is provided
     * @throws MissingResourceException if no resource bundle can be found for the class of the current object or no object for the key can be found
     */
    default String getResourceString(final Locale locale, final String... keyParts) {
        return getResourceString(getClass(), locale, keyParts);
    }
    
    /**
     * Shortcut method to avoid to specify locale parameter.
     * 
     * Default implementation maps the {@link Locale#getDefault() } {@link Locale}.
     * 
     * @param keyParts
     * @return 
     * 
     * @see InternationalResource#getResourceString(java.util.Locale, java.lang.String...) ...).
     * 
     * @throws IllegalArgumentException if no key is provided
     * @throws MissingResourceException if no resource bundle can be found for the class of the current object or no object for the key can be found
     */
    default String getResourceString(final String... keyParts) {
        return getResourceString(Locale.getDefault(), keyParts);
    }
    
    /**
     * Retrieves a string resource for the given locale and formats the result 
     * using the given arguments.
     * 
     * @param locale
     * @param key
     * @param arguments
     * @return 
     * 
     * @see InternationalResource#getFormatedResourceString(java.lang.Class, java.util.Locale, java.lang.String, java.lang.Object...)
     * 
     * @throws IllegalArgumentException if the locale is null or no key is provided
     * @throws MissingResourceException if no resource bundle for the class can be found for the class of the current object or no object for the key can be found
     */
    default String getFormatedResourceString(final Locale locale, final String key, final Object... arguments) {
        return getFormatedResourceString(getClass(), locale, key, arguments);
    }
    
    /**
     * Shortcut method to avoid to specify locale parameter.
     * 
     * Default implementation maps the {@link Locale#getDefault() } {@link Locale}.
     * 
     * @param key
     * @param arguments
     * @return 
     * 
     * @see InternationalResource#getFormatedResourceString(java.util.Locale, java.lang.String, java.lang.Object...) ...).
     * 
     * @throws IllegalArgumentException if no key is provided
     * @throws MissingResourceException if no resource bundle for the class can be found for the class of the current object or no object for the key can be found
     */
    default String getFormatedResourceString(final String key, final Object... arguments) {
        return getFormatedResourceString(Locale.getDefault(), key, arguments);
    }
}
