/* UtilLib - Copyright (c) 2008-2026 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.localisation;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.text.ParseException;
import java.util.AbstractList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.RandomAccess;
import java.util.Set;
import net.mapeadores.util.annotation.Nullable;
import net.mapeadores.util.exceptions.InternalResourceException;
import net.mapeadores.util.ini.IniParser;
import net.mapeadores.util.logging.SourceMessage;
import net.mapeadores.util.misc.ArrayUtils;


/**
 *
 * @author Vincent Calame
 */
public final class LocalisationUtils {

    public final static List<Object> EMPTY_VALUELIST = Collections.emptyList();
    public final static List<Message> EMPTY_MESSAGELIST = Collections.emptyList();
    public final static List<SourceMessage> EMPTY_SOURCEMESSAGELIST = Collections.emptyList();
    public final static UserLangContext DEFAULT_LANGCONTEXT = toUserLangContext(Lang.getDefault());
    private final static Set<String> EMPTY_SET = Collections.emptySet();
    private final static Object[] EMPTY = new Object[0];
    private final static Lang FRENCH = Lang.build("fr");
    private final static Map<Lang, String> langVoMap;

    static {
        try (InputStream is = LocalisationUtils.class.getResourceAsStream("vo_lang.ini")) {
            Map<String, String> iniMap = new HashMap<String, String>();
            IniParser.parseIni(is, iniMap);
            langVoMap = new HashMap<Lang, String>();
            for (Map.Entry<String, String> entry : iniMap.entrySet()) {
                try {
                    Lang lang = Lang.parse(entry.getKey());
                    langVoMap.put(lang, entry.getValue());
                } catch (ParseException pe) {

                }
            }
        } catch (IOException ioe) {
            throw new InternalResourceException(ioe);
        }
    }

    private LocalisationUtils() {
    }

    public static String getKibiOctet(Locale locale) {
        String language = locale.getLanguage();
        if (language.equals("fr")) {
            return "Kio";
        } else {
            return "KiB";
        }
    }

    public static String getKibiOctet(Lang lang) {
        Lang rootLang = lang.getRootLang();
        if (rootLang.equals(FRENCH)) {
            return "Kio";
        } else {
            return "KiB";
        }
    }

    public static String getMebiOctet(Locale locale) {
        String language = locale.getLanguage();
        if (language.equals("fr")) {
            return "Mio";
        } else {
            return "MiB";
        }
    }

    public static String getMebiOctet(Lang lang) {
        Lang rootLang = lang.getRootLang();
        if (rootLang.equals(FRENCH)) {
            return "Mio";
        } else {
            return "MiB";
        }
    }

    public static String getVOTitle(Lang lang) {
        return langVoMap.get(lang);
    }

    public static Message toMessage(String messageKey) {
        if (messageKey == null) {
            throw new IllegalArgumentException("messageKey is null");
        }
        return new InternalMessage(messageKey, EMPTY);
    }

    public static Message toMessage(String messageKey, Object... messageValues) {
        if (messageKey == null) {
            throw new IllegalArgumentException("messageKey is null");
        }
        if (messageValues == null) {
            messageValues = EMPTY;
        }
        return new InternalMessage(messageKey, messageValues);
    }

    public static String cleanMessageKey(String messageKey) {
        if (messageKey.startsWith("_ ")) {
            messageKey = messageKey.substring(2);
        }
        return messageKey;
    }

    public static Locale getDefaultFormatLocale(LangContext langContext) {
        if (langContext instanceof ListLangContext) {
            return ((ListLangContext) langContext).get(0).getFormatLocale();
        } else if (langContext instanceof UserLangContext) {
            return ((UserLangContext) langContext).getFormatLocale();
        } else {
            return Locale.getDefault();
        }
    }

    public static UserLangContext toUserLangContext(Lang workingLang) {
        return new SimpleUserLangContext(workingLang);
    }

    public static UserLangContext toUserLangContext(Lang workingLang, @Nullable Locale locale) {
        if (locale == null) {
            return new SimpleUserLangContext(workingLang);
        } else {
            return new InternalUserLangContext(workingLang, locale, null);
        }
    }

    public static UserLangContext toUserLangContext(Lang workingLang, Locale formatLocale, LangPreference langPreference) {
        if ((formatLocale == null) && (langPreference == null)) {
            return new SimpleUserLangContext(workingLang);
        } else {
            return new InternalUserLangContext(workingLang, formatLocale, langPreference);
        }
    }

    public static Set<String> scanMessageKeys(String text) {
        try {
            return scanMessageKeys(new BufferedReader(new StringReader(text)));
        } catch (IOException ioe) {
            return EMPTY_SET;
        }
    }

    public static Set<String> scanMessageKeys(InputStream inputStream, String charset) throws IOException {
        return scanMessageKeys(new BufferedReader(new InputStreamReader(inputStream, charset)));
    }

    public static List<Message> wrap(Message[] array) {
        return new MessageList(array);
    }

    public static LangPreference toLangPreference(Lang lang) {
        if (lang == null) {
            throw new IllegalArgumentException("lang is null");
        }
        return new UniqueLangPreference(lang);
    }

    public static String joinValues(Object... messageValues) {
        if (messageValues == null) {
            return "";
        }
        StringBuilder buf = new StringBuilder();
        for (Object value : messageValues) {
            if (value != null) {
                buf.append(value.toString());
            }
        }
        return buf.toString();
    }

    public static String joinValues(Message message) {
        return LocalisationUtils.joinValues(message.getMessageValues());
    }

    private static Set<String> scanMessageKeys(BufferedReader reader) throws IOException {
        Set<String> set = new LinkedHashSet<String>();
        String line;
        while ((line = reader.readLine()) != null) {
            checkLine(line, set);
        }
        if (set.isEmpty()) {
            return EMPTY_SET;
        }
        return set;
    }

    private static void checkLine(String line, Set<String> set) {
        int idx = line.indexOf("\"_ ");
        while (idx != -1) {
            int idx2 = line.indexOf("\"", idx + 3);
            if (idx2 == -1) {
                break;
            }
            String key = line.substring(idx + 1, idx2);
            set.add(key);
            idx = line.indexOf("\"_ ", idx2 + 1);
        }
        idx = line.indexOf("'_ ");
        while (idx != -1) {
            int idx2 = line.indexOf("'", idx + 3);
            if (idx2 == -1) {
                break;
            }
            String key = line.substring(idx + 1, idx2);
            set.add(key);
            idx = line.indexOf("'_ ", idx2 + 1);
        }
    }


    private static class SimpleUserLangContext implements UserLangContext {

        private final Lang workingLang;
        private final LangPreference langPreference;
        private final Locale formatLocale;

        private SimpleUserLangContext(Lang workingLang) {
            this.workingLang = workingLang;
            this.langPreference = LocalisationUtils.toLangPreference(workingLang);
            this.formatLocale = workingLang.toLocale();
        }

        @Override
        public Lang getWorkingLang() {
            return workingLang;
        }

        @Override
        public LangPreference getLangPreference() {
            return langPreference;
        }

        @Override
        public Locale getFormatLocale() {
            return formatLocale;
        }

        @Override
        public boolean isDefaultLocale() {
            return true;
        }

        @Override
        public boolean isDefaultLangPreference() {
            return true;
        }

    }


    private static class InternalUserLangContext implements UserLangContext {

        private final Lang workingLang;
        private final LangPreference langPreference;
        private final Locale formatLocale;

        private InternalUserLangContext(Lang workingLang, Locale formatLocale, LangPreference langPreference) {
            this.workingLang = workingLang;
            if (langPreference == null) {
                this.langPreference = LocalisationUtils.toLangPreference(workingLang);
            } else {
                this.langPreference = langPreference;
            }
            if (formatLocale == null) {
                this.formatLocale = workingLang.toLocale();
            } else {
                this.formatLocale = formatLocale;
            }
        }

        @Override
        public Lang getWorkingLang() {
            return workingLang;
        }

        @Override
        public LangPreference getLangPreference() {
            return langPreference;
        }

        @Override
        public Locale getFormatLocale() {
            return formatLocale;
        }

    }


    private static class InternalMessage implements Message {

        private final String messageKey;
        private final Object[] messageValues;
        private final List<Object> valueList;

        private InternalMessage(String messageKey, Object[] messageValues) {
            this.messageKey = messageKey;
            this.messageValues = messageValues;
            this.valueList = ArrayUtils.wrap(messageValues);
        }

        @Override
        public String getMessageKey() {
            return messageKey;
        }

        @Override
        public List<Object> getMessageValueList() {
            return valueList;
        }

        @Override
        public Object[] getMessageValues() {
            int length = messageValues.length;
            Object[] result = new Object[length];
            System.arraycopy(messageValues, 0, result, 0, length);
            return result;
        }

    }


    private static class MessageList extends AbstractList<Message> implements RandomAccess {

        private final Message[] array;

        private MessageList(Message[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public Message get(int index) {
            return array[index];
        }

    }


    private static class UniqueLangPreference extends AbstractList<Lang> implements LangPreference {

        private final Lang lang;

        private UniqueLangPreference(Lang lang) {
            this.lang = lang;
        }

        @Override
        public int size() {
            return 1;
        }

        @Override
        public Lang get(int index) {
            if (index == 0) {
                return lang;
            } else {
                throw new IndexOutOfBoundsException();
            }
        }

        @Override
        public Lang getFirstLang() {
            return lang;
        }

    }

}
