package de.duehl.vocabulary.japanese.logic.symbol.kana.internal.io;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.duehl.basics.io.FileHelper;
import de.duehl.basics.text.NumberString;
import de.duehl.vocabulary.japanese.common.data.InternalAdditionalKanaData;
import de.duehl.vocabulary.japanese.common.persistence.SessionManager;
import de.duehl.vocabulary.japanese.data.symbol.Hiragana;
import de.duehl.vocabulary.japanese.data.symbol.Katakana;
import de.duehl.vocabulary.japanese.logic.symbol.kana.internal.RealInternalKanaDataRequester;
import de.duehl.vocabulary.japanese.startup.ui.data.MessageAppender;

import static de.duehl.vocabulary.japanese.logic.symbol.kana.internal.RealInternalKanaDataRequester.*;

/**
 * Diese Klasse liest zu allen Kana die internen, benutzerabhängigen Daten ein.
 *
 * @version 1.01     2025-11-22
 * @author Christian Dühl
 */

public class InternalKanaDataReader {

    public static final String INTERNAL_KANA_DATA_DIRECTORY = FileHelper.concatPathes(
            SessionManager.VOCABLE_TRAINER_DIRECTORY, "internal_kana_data");

    /**
     * Die Extension für den Dateien mit den benutzerabhängigen, internen Daten zu jedem Kana.
     */
    private static final String INTERNAL_KANA_DATA_EXTENSION = ".ikad";


    /** Das Objekt zum Anhängen von Nachrichten an den Startup-Log. */
    private final MessageAppender messageAppender;

    /** Das Verzeichnis der internen Daten zu einem Kana nach dem zugehörigen Kana. */
    private Map<String, InternalAdditionalKanaData> key2InternalKanaDataMap;

    /** Das Objekt das zu einem Kana die internen, benutzerabhängigen Daten abrufen kann. */
    private RealInternalKanaDataRequester requester;

    /** Die Zeilen mit Warnungen. */
    private final List<String> warningLines;

    /**
     * Konstruktor.
     *
     * @param messageAppender
     *            Das Objekt zum Anhängen von Nachrichten an den Startup-Log.
     */
    public InternalKanaDataReader(MessageAppender messageAppender) {
        this.messageAppender = messageAppender;
        warningLines = new ArrayList<>();
    }

    /** Liest die Daten ein. */
    public void read() {
        readInternalKanaData();
        checkForNewHiragana();
        checkForNewKatakana();
        checkForUnusedInternalKanaData();
        createRequester();
    }

    /** Lädt alle bereits bekannten internen Datensätze zu Kana. */
    private void readInternalKanaData() {
        appendMessage("Lese interne benutzerabhängige Daten zu allen bekannten Kana ...");
        FileHelper.createDirectoryIfNotExists(INTERNAL_KANA_DATA_DIRECTORY);

        List<String> internalFiles = FileHelper.findFilesInMainDirectoryNio2WithExtensions(
                INTERNAL_KANA_DATA_DIRECTORY, INTERNAL_KANA_DATA_EXTENSION);
        appendMessage(NumberString.taupu(internalFiles.size())
                + " interne Kana-Datensätze gefunden.");
        appendMessage("Lese interne Kana-Datensätze ein ...");

        List<InternalAdditionalKanaData> internalAdditionalKanaDatas = new ArrayList<>();
        for (String filename : internalFiles) {
            InternalAdditionalKanaData data = InternalAdditionalKanaData.load(filename);
            internalAdditionalKanaDatas.add(data);
        }
        appendMessage(NumberString.taupu(internalAdditionalKanaDatas.size())
                + " interne Kana-Datensätze eingelesen.");

        key2InternalKanaDataMap = new HashMap<>();
        for (InternalAdditionalKanaData data : internalAdditionalKanaDatas) {
            String key = data.getKey();
            if (key2InternalKanaDataMap.containsKey(key)) {
                throw new RuntimeException("Der Schlüssel '" + key + "' kommt mehrfach vor!");
            }
            key2InternalKanaDataMap.put(key, data);
        }
        appendMessage("Verzeichnis mit " + NumberString.taupu(key2InternalKanaDataMap.size())
                + " Verweisen von Schlüssel auf interne Kana-Datensätze aufgebaut.");
    }

    private void checkForNewHiragana() {
        int newInternalDataCreationCounter = 0;

        for (Hiragana hiragana : Hiragana.values()) {
            String key = HIRAGANA_PRE + hiragana.name();

            if (!key2InternalKanaDataMap.containsKey(key)) {
                InternalAdditionalKanaData data = new InternalAdditionalKanaData();
                data.setKey(key);
                String internalDataFilename = FileHelper.concatPathes(INTERNAL_KANA_DATA_DIRECTORY,
                        key + INTERNAL_KANA_DATA_EXTENSION);
                data.setFilename(internalDataFilename);

                appendMessage("neu: " + internalDataFilename);
                ++newInternalDataCreationCounter;
                data.save();

                key2InternalKanaDataMap.put(key, data);
            }
        }
        appendMessage(NumberString.taupu(newInternalDataCreationCounter)
                + " neue interne Hiragana-Daten angelegt.");
    }

    private void checkForNewKatakana() {
        int newInternalDataCreationCounter = 0;

        for (Katakana katakana : Katakana.values()) {
            String key = KATAKANA_PRE + katakana.name();

            if (!key2InternalKanaDataMap.containsKey(key)) {
                InternalAdditionalKanaData data = new InternalAdditionalKanaData();
                data.setKey(key);
                String internalDataFilename = FileHelper.concatPathes(INTERNAL_KANA_DATA_DIRECTORY,
                        key + INTERNAL_KANA_DATA_EXTENSION);
                data.setFilename(internalDataFilename);

                appendMessage("neu: " + internalDataFilename);
                ++newInternalDataCreationCounter;
                data.save();

                key2InternalKanaDataMap.put(key, data);
            }
        }
        appendMessage(NumberString.taupu(newInternalDataCreationCounter)
                + " neue interne Katakana-Daten angelegt.");
    }

    private void checkForUnusedInternalKanaData() {
        List<String> unusedKanaKeys = new ArrayList<>();
        unusedKanaKeys.addAll(key2InternalKanaDataMap.keySet());

        for (Hiragana hiragana : Hiragana.values()) {
            String key = HIRAGANA_PRE + hiragana.name();

            if (unusedKanaKeys.contains(key)) {
                unusedKanaKeys.remove(key);
            }
        }

        for (Katakana katakana : Katakana.values()) {
            String key = KATAKANA_PRE + katakana.name();

            if (unusedKanaKeys.contains(key)) {
                unusedKanaKeys.remove(key);
            }
        }

        if (unusedKanaKeys.isEmpty()) {
            appendMessage("Alle interne Daten zu Kana wurden einem Kana zugeordnet.");
        }
        else {
            String interneDaten = NumberString.germanPlural(unusedKanaKeys.size(),
                    "interne Daten", "internes Datum");
            String welche = NumberString.germanPlural(unusedKanaKeys.size(),
                    "welche", "welches");
            warningLines.add("Es gibt " + NumberString.taupu(unusedKanaKeys.size())
                    + " " + interneDaten + " zu Kana, " + welche
                    + " keinem Kana zugeordnet wurden:");
            for (String unusedKey : unusedKanaKeys) {
                warningLines.add("    " + unusedKey);
            }

            for (String warningLine : warningLines) {
                appendMessage(warningLine);
            }
        }
    }

    private void createRequester() {
        requester = new RealInternalKanaDataRequester(key2InternalKanaDataMap);
    }

    private void appendMessage(String message) {
        messageAppender.appendMessage(message);
    }

    /**
     * Getter für das Objekt das zu einer Kana die internen, benutzerabhängigen Daten abrufen
     * kann.
     */
    public RealInternalKanaDataRequester getRequester() {
        return requester;
    }

    /** Die Zeilen mit Warnungen. */
    public List<String> getWarningLines() {
        return warningLines;
    }

}
