package de.duehl.basics.io.textfile.dictionary.io.reader;

/*
 * Copyright 2025 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import de.duehl.basics.io.textfile.StringsFromTextFileReader;
import de.duehl.basics.io.textfile.dictionary.Dictionary;
import de.duehl.basics.io.textfile.dictionary.DictionaryEntry;

/**
 * Diese Klasse liest eine Liste mit den Texten im Format des DictionaryFiles aus den Zeilen einer
 * Textdatei ein.
 *
 * Das Format sieht wie folgt aus:
 *
 * Englisches Wort
 *     Deutsches Wort 1
 *     Deutsches Wort 2
 *     Deutsches Wort 3
 *     Deutsches Wort 4
 *
 * Kommt in einer Datenzeile weiter hinten das Kommentarzeichen vor, so wird ab dort abgeschnitten
 * und getrimmt.
 *
 * @version 1.01     2025-07-08
 * @author Christian Dühl
 */

public class DictionaryFromTextFileReader {

    /** Der zum eigentlichen Einlesen verwendete Reader. */
    private final StringsFromTextFileReader reader;

    /** Gibt an, ob mehrfache identische Werte Fehler sind. */
    private boolean multipleEqualValuesAreError;

    /** Das eingelesene Wörterbuch. */
    private Dictionary dictionary;

    /** Gibt an, ob wir dabei sind, einen Wörterbucheintrag einzulesen. */
    private boolean dictionaryEntryIsOpened;

    /**
     * Der Wörterbucheintrag, der gerade eingelesen wird (falls dictionaryEntryIsOpened wahr ist).
     */
    private DictionaryEntry dictionaryEntry;

    /**
     * Konstruktor.
     *
     * @param reader
     *            Der zum eigentlichen Einlesen verwendete Reader.
     */
    public DictionaryFromTextFileReader(StringsFromTextFileReader reader) {
        this.reader = reader;
        reader.doNotTrimLines();
        multipleEqualValuesAreError = false;
    }

    /** Legt fest, dass der Reader ruhig sein soll. */
    public void beQuiet() {
        reader.beQuiet();
    }

    /** Legt fest, dass mehrfache identische Werte Fehler sind. */
    public void multipleEqualValuesAreError() {
        multipleEqualValuesAreError = true;
    }

    /**
     * Gibt an, dass die eingelesenen Zeilen in Kleinbuchstaben gewandelt werden.
     *
     * Hierbei wird Locale.GERMAN eingesetzt.
     */
    public void changeToLowerCase() {
        reader.changeToLowerCase();
    }

    /**
     * Liest die Listen aus der Datei ein. Das erste Wort in der Liste ist die Hauptwort und die
     * anderen die Übersetzungen.
     */
    public void read() {
        init();
        readAllLines();
        addLastDictionaryEntry();
    }

    private void init() {
        dictionary = new Dictionary();
        dictionaryEntryIsOpened = false;
        dictionaryEntry = null;
    }

    private void readAllLines() {
        for (String line : reader.read()) {
            workWithLine(line);
        }
    }

    private void workWithLine(String line) {
        if (!isLineIndentedComment(line)) {
            workWithNotEmptyLine(line);
        }
    }

    private boolean isLineIndentedComment(String line) {
        if (line.isBlank()) {
            return true;
        }
        else {
            /*
             * Da im StringsFromTextFileReader bereits hinten stehende Kommentar entfernt
             * wurden, aber im Falle von
             *     reader.doNotTrimLines();
             * wie es hier der Fall ist, die Leerzeichen davor stehen bleiben, wird hier überprüft,
             * ob es der Rest einer solchen Zeile ist.
             *
             * Daher kommt dies folgende gar nicht mehr mit
             *     return true;
             * zum Tragen, wird aber der Vollständigkeit halber stehen gelassen.
             */
            String trimmed = line.trim();
            return trimmed.startsWith(reader.getCommentChar());
        }
    }

    private void workWithNotEmptyLine(String line) {
        checkIfPhraseIsKnownInAlreadyCollectedDictionaryEntries(line);

        if (isIndented(line)) {
            workWithAlternativesLine(line);
        }
        else {
            workWithMainWordLine(line);
        }
    }

    /**
     * Hier überprüft man, dass in einem dictionaryEntry keine doppelten Phrasen vorkommen.
     */
    private void checkIfPhraseIsKnownInAlreadyCollectedDictionaryEntries(String line) {
        if (multipleEqualValuesAreError
                && dictionary.containsEntryWithMainWordOrAlternative(line)) {
            throw new RuntimeException("Hauptwort oder Alternative ist in verschiedenen Blöcken "
                    + "vorhanden: '" + dictionaryEntry + "'.");
        }
    }

    private boolean isIndented(String line) {
        if (line.startsWith("    ")) {
            String test = line.substring(4);
            if (test.startsWith(" ")) {
                throw new IllegalArgumentException(""
                        + "Zeile '" + line + "' startet mit mehr als vier Leerzeichen.\n"
                        + "\t" + "Dateiname   : " + reader.getFilename() + "\n"
                        + "\t" + "Zeilennummer: " + reader.getLineNumber() + "\n");
            }
            if (test.startsWith("\t")) {
                throw new IllegalArgumentException(""
                        + "Zeile '" + line + "' startet mit vier Leerzeichen und einem Tabulator.\n"
                        + "\t" + "Dateiname   : " + reader.getFilename() + "\n"
                        + "\t" + "Zeilennummer: " + reader.getLineNumber() + "\n");
            }
            return true;
        }
        else {
            if (line.startsWith(" ")) {
                throw new IllegalArgumentException(""
                        + "Zeile '" + line + "' startet mit weniger als vier Leerzeichen.\n"
                        + "\t" + "Dateiname   : " + reader.getFilename() + "\n"
                        + "\t" + "Zeilennummer: " + reader.getLineNumber() + "\n");
            }
            if (line.startsWith("\t")) {
                throw new IllegalArgumentException(""
                        + "Zeile '" + line + "' startet mit einem Tabulator.\n"
                        + "\t" + "Dateiname   : " + reader.getFilename() + "\n"
                        + "\t" + "Zeilennummer: " + reader.getLineNumber() + "\n");
            }
            return false;
        }
    }

    private void workWithMainWordLine(String line) {
        if (dictionaryEntryIsOpened) {
            addDictionaryEntry();
        }

        String mainWord = line.trim();
        dictionaryEntryIsOpened = true;
        dictionaryEntry = new DictionaryEntry(mainWord);
    }

    private void workWithAlternativesLine(String line) {
        if (!dictionaryEntryIsOpened) {
            throw new IllegalArgumentException("Zeile '" + line + "' startet mit vier "
                    + "Leerzeichen, aber es fehlt das Hauptwort davor!.");
        }
        String alternative = line.trim();
        if (!dictionaryEntry.contains(alternative)) {
            dictionaryEntry.addAlternative(alternative);
        }
        else if (multipleEqualValuesAreError) {
            /*
             * Hier überprüft man, dass in einem dictionaryEntry keine doppelten Phrasen vorkommen.
             */
            throw new RuntimeException("Die Alternative '" + alternative
                    + "' ist in einem Block doppelt vorhanden: '" + dictionaryEntry + "'.");
        }
    }

    private void addLastDictionaryEntry() {
        if (dictionaryEntryIsOpened) {
            addDictionaryEntry();
        }
    }

    private void addDictionaryEntry() {
        dictionary.addDictionaryEntry(dictionaryEntry);
        dictionaryEntryIsOpened = false;
    }

    /** Getter für das eingelesene Wörterbuch. */
    public Dictionary getDictionary() {
        return dictionary;
    }

}
