package gui;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.io.UnsupportedEncodingException;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.Callable;

public final class I18N {
    private static final ObjectProperty<Locale> locale;

    static {
        locale = new SimpleObjectProperty<>(getDefaultLocale());
        locale.addListener((observable, oldValue, newValue) -> Locale.setDefault(newValue));
    }

    /**
     * get the supported Locales.
     *
     * @return List of Locale objects.
     */
    public static List<Locale> getSupportedLocales() {
        return new ArrayList<>(Arrays.asList(new Locale.Builder().setLanguage("sl").setRegion("SI").build(), Locale.ENGLISH));
    }
    /**
     * get the default locale. This is the systems default if contained in the supported locales, english otherwise.
     *
     * @return
     */
    public static Locale getDefaultLocale() {
        Locale sysDefault = Locale.getDefault();
        return getSupportedLocales().contains(sysDefault) ? sysDefault : new Locale.Builder().setLanguage("sl").setRegion("SI").build();
    }
    public static Locale getLocale() {
        return locale.get();
    }
    public static void setLocale(Locale locale) {
        localeProperty().set(locale);
        Locale.setDefault(locale);
    }
    public static ObjectProperty<Locale> localeProperty() {
        return locale;
    }
    /**
     * gets the string with the given key from the resource bundle for the current locale and uses it as first argument
     * to MessageFormat.format, passing in the optional args and returning the result.
     *
     * @param key
     *         message key
     * @param args
     *         optional arguments for the message
     * @return localized formatted string
     */
    public static String get(final String key, final Object... args) {
        ResourceBundle bundle = ResourceBundle.getBundle("message", getLocale());
        String val = bundle.getString(key);
        return MessageFormat.format(val, args);
    }

    public static String getDefaultLocaleItem(final String key, final Object... args) {
        ResourceBundle bundle = ResourceBundle.getBundle("message", getDefaultLocale());
        String val = bundle.getString(key);
        return MessageFormat.format(val, args);
    }

    public static ObservableList<String> getObject(final ArrayList<String> keys, final Object... args) {
        ResourceBundle bundle = ResourceBundle.getBundle("message", getLocale());

        ArrayList<String> results = new ArrayList<>();
        for(String key : keys){
            String val = bundle.getString(key);
            results.add(val);
        }

        return FXCollections.observableArrayList(results);
    }
    /**
     * creates a String binding to a localized String for the given message bundle key
     *
     * @param key
     *         key
     * @return String binding
     */
    public static StringBinding createStringBinding(final String key, Object... args) {
        return Bindings.createStringBinding(() -> get(key, args), locale);
    }

    /**
     * creates a String binding to a localized String for the given message bundle key
     *
     * @param keys
     *         key
     * @return ObjectBinding
     */
    public static ObjectBinding createObjectBinding(final ArrayList<String> keys, Object... args) {
        return Bindings.createObjectBinding(() -> getObject(keys, args), locale);
    }


    public static String getIndependent(final String key, Locale locale, final Object... args) {
        ResourceBundle bundle = ResourceBundle.getBundle("message", locale);
        String val = bundle.getString(key);
        return MessageFormat.format(val, args);
    }

    public static String getRootValue(String oldValue, ArrayList<String> nGramComputeForLetters) {
        Locale loc;
        if(getLocale().equals(Locale.ENGLISH)) {
            loc = new Locale.Builder().setLanguage("sl").setRegion("SI").build();
        } else {
            loc = Locale.ENGLISH;
        }

        for (String el : nGramComputeForLetters){
            if (oldValue.equals(getIndependent(el, loc))){
                return el;
            }
        }

        // in case translated language doesn't contain specified word, try original language
        for (String el : nGramComputeForLetters){
            if (oldValue.equals(get(el))){
                return el;
            }
        }

        return null;
    }

    public static String getTranslatedValue(String oldValue, ArrayList<String> nGramComputeForLetters) {
        Locale loc;
        if(getLocale().equals(Locale.ENGLISH)) {
            loc = new Locale.Builder().setLanguage("sl").setRegion("SI").build();
        } else {
            loc = Locale.ENGLISH;
        }

        for (String el : nGramComputeForLetters){
            if (oldValue.equals(getIndependent(el, loc))){
                return get(el);
            }
        }

        // in case translated language doesn't contain specified word, try original language
        for (String el : nGramComputeForLetters){
            if (oldValue.equals(get(el))){
                return get(el);
            }
        }

        return null;
    }

    /**
     * Returns translated FXCollection
     *
     * @param words
     *         function called on every change
     * @return ObjectBinding
     */
    public static ObservableList<String> translatedObservableList(ArrayList<String> words){
        ArrayList<String> translatedWords = new ArrayList<>();
        for (String word : words){
            translatedWords.add(get(word));
        }

        return FXCollections.observableArrayList(translatedWords);
    }
}
