/* UtilLib - Copyright (c) 2014-2024 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.ini;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Map;
import java.util.function.BiConsumer;


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

    private IniParser() {

    }

    public static void parseIni(InputStream inputStream, Map<String, String> stringMap) throws IOException {
        Reader reader = new InputStreamReader(inputStream, "UTF-8");
        parseIni(reader, stringMap);
    }

    public static void parseIni(InputStream inputStream, BiConsumer<String, String> consumer) throws IOException {
        Reader reader = new InputStreamReader(inputStream, "UTF-8");
        parseIni(reader, consumer);
    }

    public static void parseIni(Reader reader, Map<String, String> stringMap) throws IOException {
        parseIni(reader, (key, value) -> {
            stringMap.put(key, value);
        });
    }

    public static void parseIni(Reader reader, BiConsumer<String, String> consumer) throws IOException {
        BufferedReader bufReader;
        if (!(reader instanceof BufferedReader)) {
            bufReader = new BufferedReader(reader);
        } else {
            bufReader = (BufferedReader) reader;
        }
        String line;
        while ((line = bufReader.readLine()) != null) {
            parseLine(line, consumer);
        }
    }

    public static void parseLine(String line, BiConsumer<String, String> consumer) {
        line = line.trim();
        if (line.length() == 0) {
            return;
        }
        switch (line.charAt(0)) {
            case ';':
            case '#':
            case '!':
            case '[':
                return;
        }
        int idx = line.indexOf('=');
        if (idx < 1) {
            return;
        }
        String key = line.substring(0, idx).trim();
        String value = line.substring(idx + 1).trim();
        value = parseValue(value, 0, value.length());
        consumer.accept(key, value);
    }

    private static String parseValue(CharSequence charSequence, int start, int len) {
        int stopIndex = start + len;
        StringBuilder buf = new StringBuilder();
        for (int i = start; i < stopIndex; i++) {
            int precedingIndices = i - start;
            int remainingIndices = stopIndex - i - 1;
            char carac = charSequence.charAt(i);
            switch (carac) {
                case '\\':
                    i = testAntiSlash(charSequence, i, remainingIndices, buf);
                    break;
                default:
                    buf.append(carac);
            }
        }
        return buf.toString();
    }

    private static int testAntiSlash(CharSequence charSequence, int currentIndex, int remainingIndices, StringBuilder buf) {
        if (remainingIndices == 0) {
            buf.append('\\');
            return currentIndex;
        }
        char nextChar = charSequence.charAt(currentIndex + 1);
        switch (nextChar) {
            case '\\':
                buf.append('\\');
                return currentIndex + 1;
            case 'n':
                buf.append('\n');
                return currentIndex + 1;
            case 'r':
                buf.append('\r');
                return currentIndex + 1;
            case 't':
                buf.append('\t');
                return currentIndex + 1;
            case 'u':
                //Unicode
                int min = currentIndex + 2 + Math.min(4, remainingIndices - 1);
                int p = 0;
                int result = 0;
                for (int i = currentIndex + 2; i < min; i++) {
                    char carac = charSequence.charAt(i);
                    int digit = Character.digit(carac, 16);
                    if (digit >= 0) {
                        result = result * 16;
                        result = result + digit;
                        p++;
                    }
                }
                if (p == 0) {
                    buf.append("\\u");
                } else {
                    buf.append((char) result);
                }
                return currentIndex + 1 + p;
            default:
                buf.append('\\');
                return currentIndex;
        }
    }

}
