/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.core.i18n;

import java.io.File;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;

import nuts.core.io.IOUtils;

/**
 * utility class for Locale. 
 */
public class LocaleUtils extends org.apache.commons.lang3.LocaleUtils {
	protected final static Map<String, String> charsetMap = new Hashtable<String, String>();

	static {
		loadBuiltInCharsetMap();
	}
	
    /**
     * Loads a preset language-to-encoding map. It assumes the usual character
     * encodings for most languages.
     * The previous content of the encoding map will be lost.
     * This default map currently contains the following mappings:
     * <table>
     *   <tr><td>ar</td><td>ISO-8859-6</td></tr>
     *   <tr><td>be</td><td>ISO-8859-5</td></tr>
     *   <tr><td>bg</td><td>ISO-8859-5</td></tr>
     *   <tr><td>ca</td><td>ISO-8859-1</td></tr>
     *   <tr><td>cs</td><td>ISO-8859-2</td></tr>
     *   <tr><td>da</td><td>ISO-8859-1</td></tr>
     *   <tr><td>de</td><td>ISO-8859-1</td></tr>
     *   <tr><td>el</td><td>ISO-8859-7</td></tr>
     *   <tr><td>en</td><td>ISO-8859-1</td></tr>
     *   <tr><td>es</td><td>ISO-8859-1</td></tr>
     *   <tr><td>et</td><td>ISO-8859-1</td></tr>
     *   <tr><td>fi</td><td>ISO-8859-1</td></tr>
     *   <tr><td>fr</td><td>ISO-8859-1</td></tr>
     *   <tr><td>hr</td><td>ISO-8859-2</td></tr>
     *   <tr><td>hu</td><td>ISO-8859-2</td></tr>
     *   <tr><td>is</td><td>ISO-8859-1</td></tr>
     *   <tr><td>it</td><td>ISO-8859-1</td></tr>
     *   <tr><td>iw</td><td>ISO-8859-8</td></tr>
     *   <tr><td>ja</td><td>Shift_JIS</td></tr>
     *   <tr><td>ko</td><td>EUC-KR</td></tr>    
     *   <tr><td>lt</td><td>ISO-8859-2</td></tr>
     *   <tr><td>lv</td><td>ISO-8859-2</td></tr>
     *   <tr><td>mk</td><td>ISO-8859-5</td></tr>
     *   <tr><td>nl</td><td>ISO-8859-1</td></tr>
     *   <tr><td>no</td><td>ISO-8859-1</td></tr>
     *   <tr><td>pl</td><td>ISO-8859-2</td></tr>
     *   <tr><td>pt</td><td>ISO-8859-1</td></tr>
     *   <tr><td>ro</td><td>ISO-8859-2</td></tr>
     *   <tr><td>ru</td><td>ISO-8859-5</td></tr>
     *   <tr><td>sh</td><td>ISO-8859-5</td></tr>
     *   <tr><td>sk</td><td>ISO-8859-2</td></tr>
     *   <tr><td>sl</td><td>ISO-8859-2</td></tr>
     *   <tr><td>sq</td><td>ISO-8859-2</td></tr>
     *   <tr><td>sr</td><td>ISO-8859-5</td></tr>
     *   <tr><td>sv</td><td>ISO-8859-1</td></tr>
     *   <tr><td>tr</td><td>ISO-8859-9</td></tr>
     *   <tr><td>uk</td><td>ISO-8859-5</td></tr>
     *   <tr><td>zh</td><td>GB2312</td></tr>
     *   <tr><td>zh_TW</td><td>Big5</td></tr>
     * </table>
     */
    public static void loadBuiltInCharsetMap() {
        charsetMap.clear();
        charsetMap.put("ar", "ISO-8859-6");
        charsetMap.put("be", "ISO-8859-5");
        charsetMap.put("bg", "ISO-8859-5");
        charsetMap.put("ca", "ISO-8859-1");
        charsetMap.put("cs", "ISO-8859-2");
        charsetMap.put("da", "ISO-8859-1");
        charsetMap.put("de", "ISO-8859-1");
        charsetMap.put("el", "ISO-8859-7");
        charsetMap.put("en", "ISO-8859-1");
        charsetMap.put("es", "ISO-8859-1");
        charsetMap.put("et", "ISO-8859-1");
        charsetMap.put("fi", "ISO-8859-1");
        charsetMap.put("fr", "ISO-8859-1");
        charsetMap.put("hr", "ISO-8859-2");
        charsetMap.put("hu", "ISO-8859-2");
        charsetMap.put("is", "ISO-8859-1");
        charsetMap.put("it", "ISO-8859-1");
        charsetMap.put("iw", "ISO-8859-8");
        charsetMap.put("ja", "Shift_JIS");
        charsetMap.put("ko", "EUC-KR");    
        charsetMap.put("lt", "ISO-8859-2");
        charsetMap.put("lv", "ISO-8859-2");
        charsetMap.put("mk", "ISO-8859-5");
        charsetMap.put("nl", "ISO-8859-1");
        charsetMap.put("no", "ISO-8859-1");
        charsetMap.put("pl", "ISO-8859-2");
        charsetMap.put("pt", "ISO-8859-1");
        charsetMap.put("ro", "ISO-8859-2");
        charsetMap.put("ru", "ISO-8859-5");
        charsetMap.put("sh", "ISO-8859-5");
        charsetMap.put("sk", "ISO-8859-2");
        charsetMap.put("sl", "ISO-8859-2");
        charsetMap.put("sq", "ISO-8859-2");
        charsetMap.put("sr", "ISO-8859-5");
        charsetMap.put("sv", "ISO-8859-1");
        charsetMap.put("tr", "ISO-8859-9");
        charsetMap.put("uk", "ISO-8859-5");
        charsetMap.put("zh", "GB2312");
        charsetMap.put("zh_TW", "Big5");
    }

    /**
     * Clears language-to-encoding map.
     * @see #loadBuiltInCharsetMap
     * @see #setCharset
     */
    public static void clearCharsetMap() {
        charsetMap.clear();
    }

    /**
     * Sets the character set encoding to use for templates of
     * a given locale. 
     *
     * @param locale locale
     * @param encoding encoding
     *
     * @see #clearCharsetMap
     * @see #loadBuiltInCharsetMap
     */
    public static void setCharset(Locale locale, String encoding) {
        charsetMap.put(locale.toString(), encoding);
    }

    /**
     * Gets the preferred character encoding for the given locale, or the 
     * default encoding if no encoding is set explicitly for the specified
     * locale. You can associate encodings with locales using 
     * {@link #setCharset(Locale, String)} or {@link #loadBuiltInCharsetMap()}.
     * @param loc the locale
     * @return the preferred character encoding for the locale.
     */
    public static String charsetFromLocale(Locale loc) {
        // Try for a full name match (may include country and variant)
        String charset = (String) charsetMap.get(loc.toString());
        if (charset == null) {
            if (loc.getVariant().length() > 0) {
                Locale l = new Locale(loc.getLanguage(), loc.getCountry());
                charset = (String) charsetMap.get(l.toString());
                if (charset != null) {
                    charsetMap.put(loc.toString(), charset);
                }
            } 
            charset = (String) charsetMap.get(loc.getLanguage());
            if (charset != null) {
                charsetMap.put(loc.toString(), charset);
            }
        }
        return charset;
    }

    /**
     * Builds a {@link java.util.Locale} from a String of the form en_US_foo into a Locale
     * with language "en", country "US" and variant "foo". This will parse the output of
     * {@link java.util.Locale#toString()}.
     *
     * @param localeStr     The locale String to parse.
     * @return requested Locale
     */
    public static Locale localeFromString(String localeStr) {
    	return localeFromString(localeStr, null);
    }
    
    /**
     * Builds a {@link java.util.Locale} from a String of the form en_US_foo into a Locale
     * with language "en", country "US" and variant "foo". This will parse the output of
     * {@link java.util.Locale#toString()}.
     *
     * @param localeStr     The locale String to parse.
     * @param defaultLocale The locale to use if localeStr is <tt>null</tt>.
     * @return requested Locale
     * @see #toLocale(String)
     */
    public static Locale localeFromString(String localeStr, Locale defaultLocale) {
        Locale locale = toLocale(localeStr);
        return locale != null ? locale : defaultLocale;
    }

    /**
     * Builds a {@link java.util.Locale} from a filename String of the form en_US_foo into a Locale
     * with language "en", country "US" and variant "foo". This will parse the output of
     * {@link java.util.Locale#toString()}.
     * 
     * @param filename filename
     * @return Locale
     */
	public static Locale localeFromFileName(String filename) {
		return localeFromFileName(filename, null);
	}
	
    /**
     * Builds a {@link java.util.Locale} from a filename String of the form en_US_foo into a Locale
     * with language "en", country "US" and variant "foo". This will parse the output of
     * {@link java.util.Locale#toString()}.
     * 
     * @param filename filename
     * @param defaultLocale The locale to use
     * @return Locale
     */
	public static Locale localeFromFileName(String filename, Locale defaultLocale) {
		return localeFromFileName(new File(filename), null);
	}
	
	/**
     * Builds a {@link java.util.Locale} from a filename String of the form en_US_foo into a Locale
     * with language "en", country "US" and variant "foo". This will parse the output of
     * {@link java.util.Locale#toString()}.
     * 
     * @param file file
     * @return Locale
     */
	public static Locale localeFromFileName(File file) {
		return localeFromFileName(file, null);
	}
	
    /**
     * Builds a {@link java.util.Locale} from a filename String of the form en_US_foo into a Locale
     * with language "en", country "US" and variant "foo". This will parse the output of
     * {@link java.util.Locale#toString()}.
     * 
     * @param file file
     * @param defaultLocale The locale to use
     * @return Locale
     */
	public static Locale localeFromFileName(File file, Locale defaultLocale) {
		String b = IOUtils.getFileNameBase(file);
		
		String[] sa = b.split("\\_");
		
		if (sa.length > 3) {
			if (sa[sa.length - 3].length() == 2 && sa[sa.length - 2].length() == 2) {
				return new Locale(sa[sa.length - 3], sa[sa.length - 2], sa[sa.length - 1]); 
			}
			else if (sa[sa.length - 2].length() == 2 && sa[sa.length - 1].length() == 2) {
				return new Locale(sa[sa.length - 2], sa[sa.length - 1]); 
			}
			else if (sa[sa.length - 1].length() == 2) {
				return new Locale(sa[sa.length - 1]); 
			}
		}
		else if (sa.length == 3) {
			if (sa[sa.length - 2].length() == 2 && sa[sa.length - 1].length() == 2) {
				return new Locale(sa[sa.length - 2], sa[sa.length - 1]); 
			}
			else if (sa[sa.length - 1].length() == 2) {
				return new Locale(sa[sa.length - 1]); 
			}
		}
		else if (sa.length == 2) {
			if (sa[sa.length - 1].length() == 2) {
				return new Locale(sa[sa.length - 1]); 
			}
		}
		return defaultLocale;
	}
	
}
