package de.duehl.vocabulary.japanese.logic.wrongtested;

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

import de.duehl.basics.io.FileHelper;
import de.duehl.basics.text.Text;
import de.duehl.vocabulary.japanese.data.OwnList;
import de.duehl.vocabulary.japanese.data.Vocable;
import de.duehl.vocabulary.japanese.data.Vocabulary;
import de.duehl.vocabulary.japanese.io.OwnListReader;
import de.duehl.vocabulary.japanese.io.OwnListWriter;
import de.duehl.vocabulary.japanese.logic.internal.InternalDataRequester;

import static de.duehl.vocabulary.japanese.common.persistence.SessionManager.VOCABLE_TRAINER_DIRECTORY;
import static de.duehl.vocabulary.japanese.data.OwnList.OWN_LISTS_EXTENSION;

/**
 * Diese Klasse stellt die Logik zu zwei speziellen Listen von Vokabeln dar.
 *
 * Die Listen beinhalten falsch abgefragte Vokabeln. Sie werden automatisch gefüllt und auch wieder
 * automatisch geleert, wenn eine Vokabel zuletzt zehn mal richtig abgefragt wurde.
 *
 * Die eine Liste beinhaltet die Vokabeln aus Gruppen, die andere Liste alle anderen Vokabeln.
 *
 * @version 1.01     2024-09-03
 * @author Christian Dühl
 */

public class WrongTestedVocables {

    /** Das benutzerspezifische Verzeichnis, in dem die eigenen Listen liegen. */
    public static final String WRONG_TESTED_VOCABLE_LISTS_DIRECTORY =
            FileHelper.concatPathes(VOCABLE_TRAINER_DIRECTORY, "wrong_tested_vocable_lists");

    private static final String WRONG_TESTED_VOCABLE_GROUP_LIST_NAME = "Falsch übersetzt - Gruppen";
    private static final String WRONG_TESTED_VOCABLE_OTHER_LIST_NAME = "Falsch übersetzt - Andere";


    /** Die Liste mit den bekannten Vokabularien. */
    private final List<Vocabulary> vocabularies;

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

    /** Das Objekt das zu einer Vokabel die internen, benutzerabhängigen Daten abrufen kann. */
    private final InternalDataRequester requester;

    /** Die automatisch gepflegte Liste mit falsch abgefragten Vokabeln aus Gruppen. */
    private OwnList wrongTestedGroupVocables;

    /** Die automatisch gepflegte Liste mit falsch abgefragten Vokabeln aus anderen Vokabularien. */
    private OwnList wrongTestedOtherVocables;

    /**
     * Konstruktor.
     *
     * @param vocabularies
     *            Die Liste mit den bekannten Vokabularien.
     * @param keyToVocable
     *            Das Verzeichnis der Vokabeln nach ihren Schlüsseln.
     * @param requester
     *            Das Objekt das zu einer Vokabel die internen, benutzerabhängigen Daten abrufen
     *            kann.
     */
    public WrongTestedVocables(List<Vocabulary> vocabularies, Map<String, Vocable> keyToVocable,
            InternalDataRequester requester) {
        this.vocabularies = vocabularies;
        this.keyToVocable = keyToVocable;
        this.requester = requester;

        load();
    }

    private void load() {
        FileHelper.createDirectoryIfNotExists(WRONG_TESTED_VOCABLE_LISTS_DIRECTORY);

        wrongTestedGroupVocables = loadViaName(WRONG_TESTED_VOCABLE_GROUP_LIST_NAME);
        wrongTestedOtherVocables = loadViaName(WRONG_TESTED_VOCABLE_OTHER_LIST_NAME);
    }

    private OwnList loadViaName(String name) {
        String filename = createFilenameFromName(name);

        if (FileHelper.exists(filename)) {
            return readExistingList(name, filename);
        }
        else {
            return createAndStoreNewList(name, filename);
        }
    }

    private String createFilenameFromName(String name) {
        String bareFilename = Text.createJavaVariableName(name) + OWN_LISTS_EXTENSION;
        String filename = FileHelper.concatPathes(WRONG_TESTED_VOCABLE_LISTS_DIRECTORY,
                bareFilename);
        return filename;
    }

    private OwnList readExistingList(String name, String filename) {
        OwnListReader reader = new OwnListReader(name, keyToVocable);
        reader.useSpecialFilenameForWrongTestedVocables(filename);
        reader.read();
        return reader.getOwnList();
    }

    private OwnList createAndStoreNewList(String name, String filename) {
        OwnList ownList = new OwnList(name, OwnListReader.DEFAULT_OWN_LIST_CATEGORY,
                OwnListReader.DEFAULT_OWN_LIST_SUB_CATEGORY, new ArrayList<>());
        storeList(ownList, filename);
        return ownList;
    }

    private void storeList(OwnList ownList, String filename) {
        OwnListWriter writer = new OwnListWriter(ownList, requester);
        writer.useSpecialFilenameForWrongTestedVocables(filename);
        writer.write();
    }

    /** Fügt eine Vokabel zur passenden Liste hinzu. */
    public void addVocableToWrongTestedVocablesList(Vocable vocable) {
        String name;
        OwnList ownList;
        if (isFromGroup(vocable)) {
            name = WRONG_TESTED_VOCABLE_GROUP_LIST_NAME;
            ownList = wrongTestedGroupVocables;
        }
        else {
            name = WRONG_TESTED_VOCABLE_OTHER_LIST_NAME;
            ownList = wrongTestedOtherVocables;
        }

        List<Vocable> vocables = ownList.getVocables();
        if (!vocables.contains(vocable)) {
            vocables.add(vocable);
        }

        String filename = createFilenameFromName(name);
        storeList(ownList, filename);
    }

    /** Entfernt eine Vokabel aus der passenden Liste. */
    public void removeVocableFromWrongTestedVocablesList(Vocable vocable) {
        String name;
        OwnList ownList;
        if (isFromGroup(vocable)) {
            name = WRONG_TESTED_VOCABLE_GROUP_LIST_NAME;
            ownList = wrongTestedGroupVocables;
        }
        else {
            name = WRONG_TESTED_VOCABLE_OTHER_LIST_NAME;
            ownList = wrongTestedOtherVocables;
        }

        List<Vocable> vocables = ownList.getVocables();
        vocables.remove(vocable);

        String filename = createFilenameFromName(name);
        storeList(ownList, filename);
    }

    private boolean isFromGroup(Vocable vocable) {
        Vocabulary vocabulary = determineVocabularyWithVocable(vocable);
        String subCategory = vocabulary.getSubCategory();
        return subCategory.contains(" Groups");
    }

    private Vocabulary determineVocabularyWithVocable(Vocable vocable) {
        for (Vocabulary vocabulary : vocabularies) {
            if (vocabulary.getVocables().contains(vocable)) {
                return vocabulary;
            }
        }

        throw new RuntimeException(
                "Die Vokabel " + vocable + " wurde in keinem Vokabular gefunden.");
    }

    /** Getter für die automatisch gepflegte Liste mit falsch abgefragten Vokabeln aus Gruppen. */
    public OwnList getWrongTestedGroupVocables() {
        return wrongTestedGroupVocables;
    }

    /**
     * Getter für die automatisch gepflegte Liste mit falsch abgefragten Vokabeln aus anderen
     * Vokabularien.
     */
    public OwnList getWrongTestedOtherVocables() {
        return wrongTestedOtherVocables;
    }

}
