package de.duehl.vocabulary.japanese.common.data;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import de.duehl.basics.datetime.date.ImmutualDate;
import de.duehl.basics.io.Charset;
import de.duehl.basics.io.FileHelper;
import de.duehl.basics.text.NumberString;
import de.duehl.basics.text.Text;

/**
 * Diese Klasse stellt die ergänzenden Daten zu einer Vokabel dar, die sich auf die Abfrage der
 * Vokabel durch einen speziellen Benutzer bezieht. Daher werden diese Daten auch nicht im Vokabel-
 * Verzeichnis abgelegt, sondern unterhalb des Home-Verzeichnisses des Benutzers.
 *
 * Die Zuordnung zu den eigentlichen Daten der Vokabel erfolgt über einen Schlüssel aus Kana und
 * erster Übersetzung, dies erledigt der Vokabel-Trainer beim Laden der Vokabeln.
 *
 * Siehe auch die Klasse Vocable.
 *
 * @version 1.01     2024-11-07
 * @author Christian Dühl
 */

public class InternalAdditionalVocableData {

    /** Das hier verwendete Datum, welches 'ungesehen' darstellt. */
    public static final ImmutualDate NOT_SEEN_DATE = new ImmutualDate("01.01.1970");


    /** Der Dateiname in der diese zusätzlichen Informationen gespeichert werden. */
    private String filename;

    /** Der Schlüssel basierend auf Kana und erster Übersetzung. */
    private String key;

    /** Das Datum an dem die Vokabel das erste Mal eingelesen wurde. */
    private ImmutualDate firstSeenDate;

    /** Der Zähler wie oft diese Vokabel von Japanisch nach Deutsch abgefragt wurde. */
    private int japaneseToGermanTestCount;

    /**
     * Der Zähler wie oft diese Vokabel von Japanisch nach Deutsch abgefragt und richtig
     * beantwortet wurde.
     */
    private int correctJapaneseToGermanTestCount;

    /** Das Datum an dem die Vokabel zuletzt von Japanisch nach Deutsch abgefragt wurde. */
    private ImmutualDate lastJapaneseToGermanTestDate;

    /**
     * Das Datum an dem die Vokabel zuletzt von Japanisch nach Deutsch abgefragt und richtig
     * beantwortet wurde.
     */
    private ImmutualDate lastCorrectJapaneseToGermanTestDate;

    /**
     * Die Liste mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von Japanisch nach Deutsch.
     *
     * Der letzte Eintrag ist das Ergebnis des neusten Tests.
     */
    private List<Boolean> lastTenJapaneseToGermanTestResults;

    /** Der Zähler wie oft diese Vokabel von Deutsch nach Japanisch abgefragt wurde. */
    private int germanToJapaneseTestCount;

    /**
     * Der Zähler wie oft diese Vokabel von Deutsch nach Japanisch abgefragt und richtig
     * beantwortet wurde.
     */
    private int correctGermanToJapaneseTestCount;

    /** Das Datum an dem die Vokabel zuletzt von Deutsch nach Japanisch abgefragt wurde. */
    private ImmutualDate lastGermanToJapaneseTestDate;

    /**
     * Das Datum an dem die Vokabel zuletzt von Deutsch nach Japanisch abgefragt und richtig
     * beantwortet wurde.
     */
    private ImmutualDate lastCorrectGermanToJapaneseTestDate;

    /**
     * Die Liste mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von Deutsch nach Japanisch.
     *
     * Der letzte Eintrag ist das Ergebnis des neusten Tests.
     */
    private List<Boolean> lastTenGermanToJapaneseTestResults;

    /** Konstruktor. */
    public InternalAdditionalVocableData() {
        filename = "";
        key = "";
        firstSeenDate = NOT_SEEN_DATE;

        japaneseToGermanTestCount = 0;
        correctJapaneseToGermanTestCount = 0;
        lastJapaneseToGermanTestDate = NOT_SEEN_DATE;
        lastCorrectJapaneseToGermanTestDate = NOT_SEEN_DATE;
        lastTenJapaneseToGermanTestResults = new ArrayList<>();

        germanToJapaneseTestCount = 0;
        correctGermanToJapaneseTestCount = 0;
        lastGermanToJapaneseTestDate = NOT_SEEN_DATE;
        lastCorrectGermanToJapaneseTestDate = NOT_SEEN_DATE;
        lastTenGermanToJapaneseTestResults = new ArrayList<>();
    }

    /** Getter für den Dateinamen in der diese zusätzlichen Informationen gespeichert werden. */
    public String getFilename() {
        return filename;
    }

    /** Setter für den Dateinamen in der diese zusätzlichen Informationen gespeichert werden. */
    public InternalAdditionalVocableData setFilename(String filename) {
        this.filename = filename;
        return this;
    }

    /** Getter für den Schlüssel basierend auf Kana und erster Übersetzung. */
    public String getKey() {
        return key;
    }

    /** Setter für den Schlüssel basierend auf Kana und erster Übersetzung. */
    public InternalAdditionalVocableData setKey(String key) {
        this.key = key;
        return this;
    }

    /** Getter für das Datum an dem die Vokabel das erste Mal eingelesen wurde. */
    public ImmutualDate getFirstSeenDate() {
        return firstSeenDate;
    }

    /** Setter für das Datum an dem die Vokabel das erste Mal eingelesen wurde. */
    public InternalAdditionalVocableData setFirstSeenDate(ImmutualDate firstSeenDate) {
        this.firstSeenDate = firstSeenDate;
        return this;
    }

    /** Getter für den Zähler, wie oft diese Vokabel von Japanisch nach Deutsch abgefragt wurde. */
    public int getJapaneseToGermanTestCount() {
        return japaneseToGermanTestCount;
    }

    /** Setter für den Zähler, wie oft diese Vokabel von Japanisch nach Deutsch abgefragt wurde. */
    public InternalAdditionalVocableData setJapaneseToGermanTestCount(
            int japaneseToGermanTestCount) {
        this.japaneseToGermanTestCount = japaneseToGermanTestCount;
        return this;
    }

    /**
     * Getter für den Zähler, wie oft diese Vokabel von Japanisch nach Deutsch abgefragt und
     * richtig beantwortet wurde.
     */
    public int getCorrectJapaneseToGermanTestCount() {
        return correctJapaneseToGermanTestCount;
    }

    /**
     * Setter für den Zähler, wie oft diese Vokabel von Japanisch nach Deutsch abgefragt und
     * richtig beantwortet wurde.
     */
    public InternalAdditionalVocableData setCorrectJapaneseToGermanTestCount(
            int correctJapaneseToGermanTestCount) {
        this.correctJapaneseToGermanTestCount = correctJapaneseToGermanTestCount;
        return this;
    }

    /**
     * Getter für das Datum an dem die Vokabel zuletzt von Japanisch nach Deutsch abgefragt wurde.
     */
    public ImmutualDate getLastJapaneseToGermanTestDate() {
        return lastJapaneseToGermanTestDate;
    }

    /**
     * Setter für das Datum an dem die Vokabel zuletzt von Japanisch nach Deutsch abgefragt wurde.
     */
    public InternalAdditionalVocableData setLastJapaneseToGermanTestDate(
            ImmutualDate lastJapaneseToGermanTestDate) {
        this.lastJapaneseToGermanTestDate = lastJapaneseToGermanTestDate;
        return this;
    }

    /**
     * Getter für das Datum an dem die Vokabel zuletzt von Japanisch nach Deutsch abgefragt und
     * richtig beantwortet wurde.
     */
    public ImmutualDate getLastCorrectJapaneseToGermanTestDate() {
        return lastCorrectJapaneseToGermanTestDate;
    }

    /**
     * Setter für das Datum an dem die Vokabel zuletzt von Japanisch nach Deutsch abgefragt und
     * richtig beantwortet wurde.
     */
    public InternalAdditionalVocableData setLastCorrectJapaneseToGermanTestDate(
            ImmutualDate lastCorrectJapaneseToGermanTestDate) {
        this.lastCorrectJapaneseToGermanTestDate = lastCorrectJapaneseToGermanTestDate;
        return this;
    }

    /**
     * Getter für die Anzeige Liste mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von
     * Japanisch nach Deutsch in der Tabelle.
     *
     * Der letzte Eintrag ist das Ergebnis des neusten Tests.
     */
    public String getLastTenJapaneseToGermanTestResultsAsStorageString() {
        return lastTenJapaneseToGermanTestResultsToStorageString();
    }

    /**
     * Getter für die Liste mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von Japanisch
     * nach Deutsch.
     *
     * Der letzte Eintrag ist das Ergebnis des neusten Tests.
     */
    public List<Boolean> getLastTenJapaneseToGermanTestResults() {
        return lastTenJapaneseToGermanTestResults;
    }

    /** Gibt zurück, wie oft die Vokabel zuletzt von Japanisch nach Deutsch abgefragt wurde. */
    public int getLastTenJapaneseToGermanTestsCount() {
        return lastTenJapaneseToGermanTestResults.size();
    }

    /**
     * Gibt zurück, wie oft die Vokabel zuletzt von Japanisch nach Deutsch richtig übersetzt wurde.
     */
    public int getLastCorrectJapaneseToGermanTestsCount() {
        return getLastCorrectTestsCount(lastTenJapaneseToGermanTestResults);
    }

    /**
     * Berechnet aus einer Liste von Booleans aus den letzten zehn Abfragen die Anzahl der
     * richtigen Abfragen.
     */
    public static int getLastCorrectTestsCount(List<Boolean> lastTenTestResults) {
        int correctCount = 0;
        for (boolean correct : lastTenTestResults) {
            if (correct) {
                ++correctCount;
            }
        }
        return correctCount;
    }

    /** Getter für den Zähler, wie oft diese Vokabel von Deutsch nach Japanisch abgefragt wurde. */
    public int getGermanToJapaneseTestCount() {
        return germanToJapaneseTestCount;
    }

    /** Setter für den Zähler, wie oft diese Vokabel von Deutsch nach Japanisch abgefragt wurde. */
    public InternalAdditionalVocableData setGermanToJapaneseTestCount(
            int germanToJapaneseTestCount) {
        this.germanToJapaneseTestCount = germanToJapaneseTestCount;
        return this;
    }

    /**
     * Getter für den Zähler, wie oft diese Vokabel von Deutsch nach Japanisch abgefragt und
     * richtig beantwortet wurde.
     */
    public int getCorrectGermanToJapaneseTestCount() {
        return correctGermanToJapaneseTestCount;
    }

    /**
     * Setter für den Zähler, wie oft diese Vokabel von Deutsch nach Japanisch abgefragt und
     * richtig beantwortet wurde.
     */
    public InternalAdditionalVocableData setCorrectGermanToJapaneseTestCount(
            int correctGermanToJapaneseTestCount) {
        this.correctGermanToJapaneseTestCount = correctGermanToJapaneseTestCount;
        return this;
    }

    /**
     * Getter für das Datum an dem die Vokabel zuletzt von Deutsch nach Japanisch abgefragt wurde.
     */
    public ImmutualDate getLastGermanToJapaneseTestDate() {
        return lastGermanToJapaneseTestDate;
    }

    /**
     * Setter für das Datum an dem die Vokabel zuletzt von Deutsch nach Japanisch abgefragt wurde.
     */
    public InternalAdditionalVocableData setLastGermanToJapaneseTestDate(
            ImmutualDate lastGermanToJapaneseTestDate) {
        this.lastGermanToJapaneseTestDate = lastGermanToJapaneseTestDate;
        return this;
    }

    /**
     * Getter für das Datum an dem die Vokabel zuletzt von Deutsch nach Japanisch abgefragt und
     * richtig beantwortet wurde.
     */
    public ImmutualDate getLastCorrectGermanToJapaneseTestDate() {
        return lastCorrectGermanToJapaneseTestDate;
    }

    /**
     * Setter für das Datum an dem die Vokabel zuletzt von Deutsch nach Japanisch abgefragt und
     * richtig beantwortet wurde.
     */
    public InternalAdditionalVocableData setLastCorrectGermanToJapaneseTestDate(
            ImmutualDate lastCorrectGermanToJapaneseTestDate) {
        this.lastCorrectGermanToJapaneseTestDate = lastCorrectGermanToJapaneseTestDate;
        return this;
    }

    /**
     * Getter für die Anzeige Liste mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von
     * Deutsch nach Japanisch in der Tabelle.
     *
     * Der letzte Eintrag ist das Ergebnis des neusten Tests.
     */
    public String getLastTenGermanToJapaneseTestResultsAsStorageString() {
        return lastTenGermanToJapaneseTestResultsToStorageString();
    }

    /**
     * Getter für die Liste mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von Japanisch
     * nach Deutsch.
     *
     * Der letzte Eintrag ist das Ergebnis des neusten Tests.
     */
    public List<Boolean> getLastTenGermanToJapaneseTestResults() {
        return lastTenGermanToJapaneseTestResults;
    }

    /** Gibt zurück, wie oft die Vokabel zuletzt von Deutsch nach Japanisch abgefragt wurde. */
    public int getLastGermanToJapaneseTestsCount() {
        return lastTenGermanToJapaneseTestResults.size();
    }

    /**
     * Gibt zurück, wie oft die Vokabel zuletzt von Deutsch nach Japanisch richtig übersetzt wurde.
     */
    public int getLastCorrectGermanToJapaneseTestsCount() {
        return getLastCorrectTestsCount(lastTenGermanToJapaneseTestResults);
    }

    /**
     * Erzeugt einen String mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von Japanisch
     * nach Deutsch.
     *
     * Enthält '+' für richtig beantwortete und '-' für falsch beantwortete Abfragen.
     *
     * Das letzte Zeichen ist das Ergebnis des neusten Tests.
     */
    String lastTenJapaneseToGermanTestResultsToStorageString() { // nicht privat für tests
        return lastTenTestResultsToStorageString(lastTenJapaneseToGermanTestResults);
    }

    /**
     * Erzeugt einen String mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von Deutsch nach
     * Japanisch.
     *
     * Enthält '+' für richtig beantwortete und '-' für falsch beantwortete Abfragen.
     *
     * Das letzte Zeichen ist das Ergebnis des neusten Tests.
     */
    String lastTenGermanToJapaneseTestResultsToStorageString() { // nicht privat für tests
        return lastTenTestResultsToStorageString(lastTenGermanToJapaneseTestResults);
    }

    /**
     * Erzeugt aus einer Liste von Booleans die den letzten zehn Tests entsprechen einen String
     * bestehend aus "+"- und "-"-Zeichen.
     */
    public static String lastTenTestResultsToStorageString(List<Boolean> lastTenTestResults) {
        StringBuilder builder = new StringBuilder();
        for (boolean correct : lastTenTestResults) {
            if (correct) {
                builder.append("+");
            }
            else {
                builder.append("-");
            }
        }
        return builder.toString();
    }

    /**
     * Initialisiert aus einen String mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von
     * Japanisch nach Deutsch die Liste mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von
     * Japanisch nach Deutsch.
     *
     * @param storageString
     *            Der String mit den letzten Ergebnissen. Enthält '+' für richtig beantwortete und
     *            '-' für falsch beantwortete Abfragen. Das letzte Zeichen ist das Ergebnis des
     *            neusten Tests.
     */
    public InternalAdditionalVocableData initLastTenJapaneseToGermanTestResultsFromStorageString(
            String storageString) { // public für tests
        initLastTenTestResultsFromStorageString(lastTenJapaneseToGermanTestResults, storageString);
        return this;
    }

    /**
     * Initialisiert aus einen String mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von
     * Deutsch nach Japanisch die Liste mit den letzten 10 (oder weniger) Abfrage-Ergebnissen von
     * Deutsch nach Japanisch.
     *
     * @param storageString
     *            Der String mit den letzten Ergebnissen. Enthält '+' für richtig beantwortete und
     *            '-' für falsch beantwortete Abfragen. Das letzte Zeichen ist das Ergebnis des
     *            neusten Tests.
     */
    public InternalAdditionalVocableData initLastTenGermanToJapaneseTestResultsFromStorageString(
            String storageString) {
        initLastTenTestResultsFromStorageString(lastTenGermanToJapaneseTestResults, storageString);
        return this;
    }

    /**
     * Befüllt aus einem String mit den letzten zehn Abfragen, bestehend aus "+"- und "-"-Zeichen,
     * wieder eine Liste von Boolean.
     */
    public static void initLastTenTestResultsFromStorageString(List<Boolean> lastTenTestResults,
            String storageString) {
        lastTenTestResults.clear();
        for (int index = 0; index < storageString.length(); ++index) {
            String plusMinus = storageString.substring(index, index + 1);
            if (plusMinus.equals("+")) {
                lastTenTestResults.add(true);
            }
            else if (plusMinus.equals("-")) {
                lastTenTestResults.add(false);
            }
            else {
                throw new RuntimeException(
                        "Der Storage-String '" + storageString + "' enthält unzuläsige Zeichen.");
            }
        }
    }

    /** Fügt eine Abfrage der Vokabel von Japanisch nach Deutsch hinzu. */
    public void testedJapaneseToGerman(boolean correct) {
        testedJapaneseToGermanWithoutSave(correct);
        save();
    }

    void testedJapaneseToGermanWithoutSave(boolean correct) {
        ImmutualDate today = new ImmutualDate();

        increaseJapaneseToGermanTestCount();
        setLastJapaneseToGermanTestDate(today);
        if (correct) {
            increaseCorrectJapaneseToGermanTestCount();
            setLastCorrectJapaneseToGermanTestDate(today);
        }

        lastTenJapaneseToGermanTestResults.add(correct);
        while (lastTenJapaneseToGermanTestResults.size() > 10) {
            lastTenJapaneseToGermanTestResults.remove(0);
        }
    }

    /** Erhöht den Zähler wie oft diese Vokabel von Japanisch nach Deutsch abgefragt wurde. */
    private void increaseJapaneseToGermanTestCount() {
        ++japaneseToGermanTestCount;
    }

    /**
     * Erhöht den Zähler wie oft diese Vokabel von Japanisch nach Deutsch abgefragt und richtig
     * beantwortet wurde.
     */
    private void increaseCorrectJapaneseToGermanTestCount() {
        ++correctJapaneseToGermanTestCount;
    }

    /** Fügt eine Abfrage der Vokabel von Deutsch nach Japanisch hinzu. */
    public void testedGermanToJapanese(boolean correct) {
        testedGermanToJapaneseWithoutSave(correct);
        save();
    }

    void testedGermanToJapaneseWithoutSave(boolean correct) {
        ImmutualDate today = new ImmutualDate();

        increaseGermanToJapaneseTestCount();
        setLastGermanToJapaneseTestDate(today);
        if (correct) {
            increaseCorrectGermanToJapaneseTestCount();
            setLastCorrectGermanToJapaneseTestDate(today);
        }

        lastTenGermanToJapaneseTestResults.add(correct);
        while (lastTenGermanToJapaneseTestResults.size() > 10) {
            lastTenGermanToJapaneseTestResults.remove(0);
        }
    }

    /** Erhöht den Zähler wie oft diese Vokabel von Deutsch nach Japanisch abgefragt wurde. */
    private void increaseGermanToJapaneseTestCount() {
        ++germanToJapaneseTestCount;
    }

    /**
     * Erhöht den Zähler wie oft diese Vokabel von Deutsch nach Japanisch abgefragt und richtig
     * beantwortet wurde.
     */
    private void increaseCorrectGermanToJapaneseTestCount() {
        ++correctGermanToJapaneseTestCount;
    }

    private static final int OLD_NUMBER_OF_LINES = 7;

    private static final int NEW_NUMBER_OF_LINES = 12;

    /**
     * Speichert die Daten dieses Objekts.
     *
     * Der Dateiname wird nicht in der Datei selbst mit abgespeichert, sondern nur in dem Objekt.
     *
     * @param filename Dateiname der einzulesenden Datei.
     * @return Eingelesenes Objekt
     */
    public static InternalAdditionalVocableData load(String filename) {
        List<String> lines = FileHelper.readFileToList(filename, Charset.UTF_8);

        if (lines.size() == OLD_NUMBER_OF_LINES) {
            return loadFromFormatWithOnlyJapaneseToGerman(filename, lines);
        }
        else if (lines.size() == NEW_NUMBER_OF_LINES) {
            return loadFromFormatWithBothDirections(filename, lines);
        }
        else {
            throw new RuntimeException("Die Datei " + filename + " enthält nicht wie erwartet "
                    + OLD_NUMBER_OF_LINES + " oder " + NEW_NUMBER_OF_LINES + " Zeilen.");
        }
    }

    private static InternalAdditionalVocableData loadFromFormatWithOnlyJapaneseToGerman(
            String filename, List<String> lines) {
        int index = 0;
        String key = lines.get(index++);
        String japaneseToGermanTestCountString = lines.get(index++);
        String correctJapaneseToGermanTestCountString = lines.get(index++);
        String firstSeenDateString = lines.get(index++);
        String lastJapaneseToGermanTestDateString = lines.get(index++);
        String lastJapaneseToGermanCorrectTestDateString = lines.get(index++);
        String lastTenJapaneseToGermanTestResultsStorageString = lines.get(index++);

        int japaneseToGermanTestCount = NumberString.parseInt(japaneseToGermanTestCountString);
        int correctJapaneseToGermanTestCount =
                NumberString.parseInt(correctJapaneseToGermanTestCountString);

        ImmutualDate firstSeenDate = new ImmutualDate(firstSeenDateString);
        ImmutualDate lastJapaneseToGermanTestDate =
                new ImmutualDate(lastJapaneseToGermanTestDateString);
        ImmutualDate lastCorrectJapaneseToGermanTestDate =
                new ImmutualDate(lastJapaneseToGermanCorrectTestDateString);

        InternalAdditionalVocableData data = new InternalAdditionalVocableData();
        data.setFilename(filename);
        data.setKey(key);
        data.setFirstSeenDate(firstSeenDate);
        data.setJapaneseToGermanTestCount(japaneseToGermanTestCount);
        data.setCorrectJapaneseToGermanTestCount(correctJapaneseToGermanTestCount);
        data.setLastJapaneseToGermanTestDate(lastJapaneseToGermanTestDate);
        data.setLastCorrectJapaneseToGermanTestDate(lastCorrectJapaneseToGermanTestDate);
        data.initLastTenJapaneseToGermanTestResultsFromStorageString(
                lastTenJapaneseToGermanTestResultsStorageString);
        return data;
    }

    private static InternalAdditionalVocableData loadFromFormatWithBothDirections(
            String filename, List<String> lines) {
        int index = 0;
        String key = lines.get(index++);
        String firstSeenDateString = lines.get(index++);
        ImmutualDate firstSeenDate = new ImmutualDate(firstSeenDateString);

        String japaneseToGermanTestCountString = lines.get(index++);
        String correctJapaneseToGermanTestCountString = lines.get(index++);
        String lastJapaneseToGermanTestDateString = lines.get(index++);
        String lastJapaneseToGermanCorrectTestDateString = lines.get(index++);
        String lastTenJapaneseToGermanTestResultsStorageString = lines.get(index++);

        int japaneseToGermanTestCount = NumberString.parseInt(japaneseToGermanTestCountString);
        int correctJapaneseToGermanTestCount =
                NumberString.parseInt(correctJapaneseToGermanTestCountString);

        ImmutualDate lastJapaneseToGermanTestDate =
                new ImmutualDate(lastJapaneseToGermanTestDateString);
        ImmutualDate lastCorrectJapaneseToGermanTestDate =
                new ImmutualDate(lastJapaneseToGermanCorrectTestDateString);

        String germanToJapaneseTestCountString = lines.get(index++);
        String correctGermanToJapaneseTestCountString = lines.get(index++);
        String lastGermanToJapaneseTestDateString = lines.get(index++);
        String lastGermanToJapaneseCorrectTestDateString = lines.get(index++);
        String lastTenGermanToJapaneseTestResultsStorageString = lines.get(index++);

        int germanToJapaneseTestCount = NumberString.parseInt(germanToJapaneseTestCountString);
        int correctGermanToJapaneseTestCount =
                NumberString.parseInt(correctGermanToJapaneseTestCountString);

        ImmutualDate lastGermanToJapaneseTestDate =
                new ImmutualDate(lastGermanToJapaneseTestDateString);
        ImmutualDate lastCorrectGermanToJapaneseTestDate =
                new ImmutualDate(lastGermanToJapaneseCorrectTestDateString);

        InternalAdditionalVocableData data = new InternalAdditionalVocableData();
        data.setFilename(filename);
        data.setKey(key);
        data.setFirstSeenDate(firstSeenDate);

        data.setJapaneseToGermanTestCount(japaneseToGermanTestCount);
        data.setCorrectJapaneseToGermanTestCount(correctJapaneseToGermanTestCount);
        data.setLastJapaneseToGermanTestDate(lastJapaneseToGermanTestDate);
        data.setLastCorrectJapaneseToGermanTestDate(lastCorrectJapaneseToGermanTestDate);
        data.initLastTenJapaneseToGermanTestResultsFromStorageString(
                lastTenJapaneseToGermanTestResultsStorageString);

        data.setGermanToJapaneseTestCount(germanToJapaneseTestCount);
        data.setCorrectGermanToJapaneseTestCount(correctGermanToJapaneseTestCount);
        data.setLastGermanToJapaneseTestDate(lastGermanToJapaneseTestDate);
        data.setLastCorrectGermanToJapaneseTestDate(lastCorrectGermanToJapaneseTestDate);
        data.initLastTenGermanToJapaneseTestResultsFromStorageString(
                lastTenGermanToJapaneseTestResultsStorageString);

        return data;
    }

    /**
     * Speichert die Daten dieses Objekts.
     *
     * Sollte nicht außerhalb vom StartupLoader und dieser Klasse aufgerufen werden!
     *
     * Der Dateiname wird nicht in der Datei selbst mit abgespeichert, sondern nur in dem Objekt.
     */
    public void save() {
        List<String> lines = new ArrayList<>();
        lines.add(key);
        lines.add(firstSeenDate.toString());

        lines.add(Integer.toString(japaneseToGermanTestCount));
        lines.add(Integer.toString(correctJapaneseToGermanTestCount));
        lines.add(lastJapaneseToGermanTestDate.toString());
        lines.add(lastCorrectJapaneseToGermanTestDate.toString());
        lines.add(lastTenJapaneseToGermanTestResultsToStorageString());

        lines.add(Integer.toString(germanToJapaneseTestCount));
        lines.add(Integer.toString(correctGermanToJapaneseTestCount));
        lines.add(lastGermanToJapaneseTestDate.toString());
        lines.add(lastCorrectGermanToJapaneseTestDate.toString());
        lines.add(lastTenGermanToJapaneseTestResultsToStorageString());

        FileHelper.writeLinesToFile(lines, filename, Charset.UTF_8);
    }

    @Override
    public String toString() {
        return "InternalAdditionalVocableData [filename=" + filename + ", key=" + key
                + ", firstSeenDate=" + firstSeenDate + ", japaneseToGermanTestCount="
                + japaneseToGermanTestCount + ", correctJapaneseToGermanTestCount="
                + correctJapaneseToGermanTestCount + ", lastJapaneseToGermanTestDate="
                + lastJapaneseToGermanTestDate + ", lastCorrectJapaneseToGermanTestDate="
                + lastCorrectJapaneseToGermanTestDate + ", lastTenJapaneseToGermanTestResults="
                + lastTenJapaneseToGermanTestResults + ", germanToJapaneseTestCount="
                + germanToJapaneseTestCount + ", correctGermanToJapaneseTestCount="
                + correctGermanToJapaneseTestCount + ", lastGermanToJapaneseTestDate="
                + lastGermanToJapaneseTestDate + ", lastCorrectGermanToJapaneseTestDate="
                + lastCorrectGermanToJapaneseTestDate + ", lastTenGermanToJapaneseTestResults="
                + lastTenGermanToJapaneseTestResults + "]";
    }

    /** Erzeugt eine leserliche, schöne Ausgabe. */
    public String toNiceString(int numberOfInsertionSpaces) {
        StringBuilder builder = new StringBuilder();

        String insertion = Text.multipleString(" ", numberOfInsertionSpaces);

        builder.append(insertion + "InternalAdditionalVocableData:\n");
        builder.append(insertion + "filename           : " + filename + "\n");
        builder.append(insertion + "key                : " + key + "\n");
        builder.append(insertion + "firstSeenDate      : " + firstSeenDate + "\n");

        builder.append(insertion + "japaneseToGermanTestCount          : "
                + japaneseToGermanTestCount + "\n");
        builder.append(insertion + "correctJapaneseToGermanTestCount   : "
                + correctJapaneseToGermanTestCount + "\n");
        builder.append(insertion + "lastJapaneseToGermanTestDate       : "
                + lastJapaneseToGermanTestDate + "\n");
        builder.append(insertion + "lastCorrectJapaneseToGermanTestDate: "
                + lastCorrectJapaneseToGermanTestDate + "\n");
        builder.append(insertion + "lastTenJapaneseToGermanTestResults : "
                + lastTenJapaneseToGermanTestResultsToStorageString() + "\n");

        builder.append(insertion + "germanToJapaneseTestCount          : "
                + germanToJapaneseTestCount + "\n");
        builder.append(insertion + "correctGermanToJapaneseTestCount   : "
                + correctGermanToJapaneseTestCount + "\n");
        builder.append(insertion + "lastGermanToJapaneseTestDate       : "
                + lastGermanToJapaneseTestDate + "\n");
        builder.append(insertion + "lastCorrectGermanToJapaneseTestDate: "
                + lastCorrectGermanToJapaneseTestDate + "\n");
        builder.append(insertion + "lastTenGermanToJapaneseTestResults : "
                + lastTenGermanToJapaneseTestResultsToStorageString() + "\n");

        return builder.toString();
    }

    @Override
    public int hashCode() {
        return Objects.hash(correctGermanToJapaneseTestCount, correctJapaneseToGermanTestCount,
                filename, firstSeenDate, germanToJapaneseTestCount, japaneseToGermanTestCount, key,
                lastCorrectGermanToJapaneseTestDate, lastCorrectJapaneseToGermanTestDate,
                lastGermanToJapaneseTestDate, lastJapaneseToGermanTestDate,
                lastTenGermanToJapaneseTestResults, lastTenJapaneseToGermanTestResults);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        InternalAdditionalVocableData other = (InternalAdditionalVocableData) obj;
        return correctGermanToJapaneseTestCount == other.correctGermanToJapaneseTestCount
                && correctJapaneseToGermanTestCount == other.correctJapaneseToGermanTestCount
                && Objects.equals(filename, other.filename)
                && Objects.equals(firstSeenDate, other.firstSeenDate)
                && germanToJapaneseTestCount == other.germanToJapaneseTestCount
                && japaneseToGermanTestCount == other.japaneseToGermanTestCount
                && Objects.equals(key, other.key)
                && Objects.equals(lastCorrectGermanToJapaneseTestDate,
                        other.lastCorrectGermanToJapaneseTestDate)
                && Objects.equals(lastCorrectJapaneseToGermanTestDate,
                        other.lastCorrectJapaneseToGermanTestDate)
                && Objects.equals(lastGermanToJapaneseTestDate, other.lastGermanToJapaneseTestDate)
                && Objects.equals(lastJapaneseToGermanTestDate, other.lastJapaneseToGermanTestDate)
                && Objects.equals(lastTenGermanToJapaneseTestResults,
                        other.lastTenGermanToJapaneseTestResults)
                && Objects.equals(lastTenJapaneseToGermanTestResults,
                        other.lastTenJapaneseToGermanTestResults);
    }

}
