package de.duehl.vocabulary.japanese.io;

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

import de.duehl.basics.io.Charset;
import de.duehl.basics.io.FileHelper;
import de.duehl.basics.text.definitions.DefinitionLineParser;
import de.duehl.swing.ui.GuiTools;
import de.duehl.vocabulary.japanese.data.OwnList;
import de.duehl.vocabulary.japanese.data.Vocable;

/**
 * Diese Klasse liest eine Datei mit einer eigenen Liste ein.
 *
 * @version 1.01     2024-12-10
 * @author Christian Dühl
 */

public class OwnListReader {

    static final String OWN_LIST_CATEGORY_KEY = "own-list-category";
    static final String OWN_LIST_SUB_CATEGORY_KEY = "own-list-sub-category";

    public static final String DEFAULT_OWN_LIST_CATEGORY = "Default";
    public static final String DEFAULT_OWN_LIST_SUB_CATEGORY = "Default";


    /** Der Name der eigenen Liste. */
    private final String name;

    /** Das Verzeichnis der Vokabeln nach ihren Schlüsseln. */
    private final Map<String, Vocable> keyToVocable;

    /** Der Dateiname der eigenen Liste (wird im Normalfall aus dem Namen gebildet). */
    private String filename;

    /** Die Liste mit den eingelesenen Zeilen aus der Datei mit der eigenen Liste. */
    private List<String> lines;

    /** Die Liste mit den Vokabeln der eigenen Liste. */
    private List<Vocable> vocables;

    /** Die Kategorie zu der die eigene Liste gehört. */
    private String ownListCategory;

    /** Die Unterkategorie zu der die eigene Liste gehört. */
    private String ownListSubCategory;

    /** Die eigene Liste, die hier eingelesen wird. */
    private OwnList ownList;

    /**
     * Gibt an, ob ein spezieller Dateiname (außerhalb des Verzeichnisses mit den eigenen Listen)
     * für die automatisch gepflegten Listen der falsch abgefragten Vokabeln verwendet werden soll.
     */
    private boolean specialFilenameForWrongTestedVocables;

    /**
     * Konstruktor.
     *
     * @param name
     *            Der Name der eigenen Liste.
     * @param keyToVocable
     *            Das Verzeichnis der Vokabeln nach ihren Schlüsseln.
     */
    public OwnListReader(String name, Map<String, Vocable> keyToVocable) {
        this.name = name;
        this.keyToVocable = keyToVocable;

        ownListCategory = DEFAULT_OWN_LIST_CATEGORY;
        ownListSubCategory = DEFAULT_OWN_LIST_SUB_CATEGORY;

        specialFilenameForWrongTestedVocables = false;
    }

    /**
     * Legt fest, dass ein spezieller Dateiname (außerhalb des Verzeichnisses mit den eigenen
     * Listen) für die automatisch gepflegten Listen der falsch abgefragten Vokabeln verwendet
     * werden soll.
     *
     * @param filename
     *            Der Dateiname der eigenen Liste.
     */
    public void useSpecialFilenameForWrongTestedVocables(String filename) {
        this.filename = filename;
        specialFilenameForWrongTestedVocables = true;
    }

    /** Liest die eigene Liste ein. */
    public void read() {
        init();
        createFilename();
        readLines();
        parseLines();
        createOwnList();
    }

    private void init() {
        vocables = new ArrayList<>();
    }

    private void createFilename() {
        if (!specialFilenameForWrongTestedVocables) {
            filename = OwnList.determineFilename(name);
        }
    }

    private void readLines() {
        lines = FileHelper.readFileToList(filename, Charset.UTF_8);
    }

    private void parseLines() {
        int keyPosition = 0;

        for (String line : lines) {
            if (line.startsWith(OWN_LIST_CATEGORY_KEY)) {
                parseOnwListCategory(line);
            }
            else if (line.startsWith(OWN_LIST_SUB_CATEGORY_KEY)) {
                parseOnwListSubCategory(line);
            }
            else {
                ++keyPosition;
                parseKey(line, keyPosition);
            }
        }
    }

    private void parseOnwListCategory(String line) {
        DefinitionLineParser parser = new DefinitionLineParser(line, filename);
        parser.parse();
        String key = parser.getKey();
        String value = parser.getValue();
        if (!key.equalsIgnoreCase(OWN_LIST_CATEGORY_KEY)) {
            throw new RuntimeException(""
                  + "Die Zeile mit der Kategorie der eigenen Liste enthält den falschen "
                  + "Schlüssel.\n"
                  + "\t" + "filename = '" + filename + "'.\n"
                  + "\t" + "Zeile    = '" + line + "'.\n"
                  + "\t" + "key      = '" + key + "'.\n"
                  + "\t" + "erwartet = '" + OWN_LIST_CATEGORY_KEY + "'.\n"
                  + "Die Groß-/Kleinschreibung spielt keine Rolle.");
        }
        if (ownListCategory.equals(DEFAULT_OWN_LIST_CATEGORY)) {
            ownListCategory = value;
        }
        else {
            throw new RuntimeException(""
                    + "Es gibt mehr als eine Kategorie der eigenen Liste.\n"
                    + "\t" + "filename   = '" + filename + "'.\n"
                    + "\t" + "Kategorie1 = '" + ownListCategory + "'.\n"
                    + "\t" + "Kategorie2 = '" + value + "'.\n");
        }
    }

    private void parseOnwListSubCategory(String line) {
        DefinitionLineParser parser = new DefinitionLineParser(line, filename);
        parser.parse();
        String key = parser.getKey();
        String value = parser.getValue();
        if (!key.equalsIgnoreCase(OWN_LIST_SUB_CATEGORY_KEY)) {
            throw new RuntimeException(""
                  + "Die Zeile mit der Unterkategorie der eigenen Liste enthält den falschen "
                  + "Schlüssel.\n"
                  + "\t" + "filename = '" + filename + "'.\n"
                  + "\t" + "Zeile    = '" + line + "'.\n"
                  + "\t" + "key      = '" + key + "'.\n"
                  + "\t" + "erwartet = '" + OWN_LIST_SUB_CATEGORY_KEY + "'.\n"
                  + "Die Groß-/Kleinschreibung spielt keine Rolle.");
        }
        if (ownListSubCategory.equals(DEFAULT_OWN_LIST_SUB_CATEGORY)) {
            ownListSubCategory = value;
        }
        else {
            throw new RuntimeException(""
                    + "Es gibt mehr als eine Unterkategorie der eigenen Liste.\n"
                    + "\t" + "filename        = '" + filename + "'.\n"
                    + "\t" + "Unterkategorie1 = '" + ownListSubCategory + "'.\n"
                    + "\t" + "Unterkategorie2 = '" + value + "'.\n");
        }
    }

    private void parseKey(String key, int keyPosition) {
        /*
         * Es kann sein, dass man an den Vokabularien etwas geändert hat, so dass ein
         * solcher Schlüssel nicht gefunden wird. Daher muss das hier geprüft werden!
         */
        if (keyToVocable.containsKey(key)) {
            Vocable vocable = keyToVocable.get(key);
            vocables.add(vocable);
        }
        else {
            String message = ""
                    + "In der Liste '" + name + "' ist an Position " + keyPosition
                    + " der Schlüssel '" + key + "' eingetragen.\n"
                    + "Zu diesem Schlüssel ist aber keine Vokabel mehr bekannt.\n"
                    + "Daher wird er nicht für die Liste '" + name + "' verwendet!\n\n"
                    + "Wollen Sie fortsetzen? Anderenfalls wird das Programm augenblicklich "
                    + "beendet.";
            System.out.println(message + "\n");
            boolean goOn = GuiTools.askUser("Unbekannter Schlüssel", message);
            if (!goOn) {
                System.out.println("Abbruch durch den Benutzer.");
                System.exit(1);
            }
        }
    }

    private void createOwnList() {
        ownList = new OwnList(name, ownListCategory, ownListSubCategory, vocables);
    }

    /** Getter für die eigene Liste, die hier eingelesen wird. */
    public OwnList getOwnList() {
        return ownList;
    }

}
