package de.duehl.vocabulary.japanese.tools;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import de.duehl.basics.io.FileHelper;
import de.duehl.basics.text.Text;
import de.duehl.vocabulary.japanese.data.Vocable;
import de.duehl.vocabulary.japanese.data.Vocabulary;
import de.duehl.vocabulary.japanese.logic.ownlists.groups.OwnListGroup;

/**
 * Diese Klasse stellt Hilfsmethoden (abgesehen von allem, was Farben betrifft) rund um den
 * Vokabeltrainer zur Verfügung.
 *
 * @version 1.01     2025-06-25
 * @author Christian Dühl
 */

public class VocabularyTools {

    /** Erzeugt aus dem Dateinamen einer Datei mit Vokabeln eine Beschreibung. */
    public static String vocabularyFilenameToDescription(String filename) {
        String bareFilename = FileHelper.getBareName(filename);
        String descriptionWithUnderscores = FileHelper.removeExtension(bareFilename);
        String description = descriptionWithUnderscores.replace("_", " ");
        if (description.startsWith("Vokabeln ")) {
            description = description.substring("Vokabeln ".length());
        }
        return description;
    }

    /** Gibt ein Vokabular aus. */
    public static void printVocabularies(List<Vocabulary> vocabularies) {
        System.out.println("Liste der Vokabulare:");
        System.out.println();
        for (Vocabulary vocabulary : vocabularies) {
            System.out.println(vocabulary.toNiceString());
            System.out.println();
        }
    }

    /** Erzeugt zu einer Vokabel einen auf Kana und erster Übersetzung basierenden Schlüssel. */
    public static String createVocableKey(Vocable vocable) {
        /*
         * Wegen VocabularyAndSoundFileFinder#removeVocablesWithoutKanaOrTranslation() wissen wir,
         * dass beides gefüllt ist und es mindestens eine Übersetzung gibt.
         */
        String kana1 = vocable.getKana();
        String translation1 = vocable.getTranslations().get(0);
        String key = kana1 + translation1;
        key = key.replace(" ", "_");
        key = key.replace("?", "_");
        key = key.replace(":", "#DOPPELPUNKT#");
        key = key.replace("/", "_");
        key = key.replace("\\", "_");
        key = key.replace("\"", "_");
        key = key.replace("'", "_");
        /*
         * Vielleicht muss man noch mehr ersetzen, damit sich der Schlüssel als Teil eines
         * Dateinamens eignet.
         */
        return key;
    }

    /** Spielt die übergebene MP3-Datei in einem eigenen Thread ab. */
    public static void playMp3(String mp3) {
        if (!mp3.isBlank() && FileHelper.isFile(mp3)) {
            de.duehl.mp3.player.MP3Player player = new de.duehl.mp3.player.MP3Player();
            player.playMP3InOwnThread(mp3);
        }
    }

    /**
     * Erzeugt zu einer Übersetzung eine Version für den Vergleich mit den Benutzereingaben.
     *
     * Der erzeugte Vergleichswert enthält keine Leerzeichen und Fragezeichen mehr, außerdem werden
     * alle Buchstaben in kleine Buchstaben umgewandelt. Das ist sinnvoll für Fälle wie "zu Hause"
     * <-> "Zuhause". Die Fragezeichen sind am Ende von Sätzen mit "ka" natürlich sinnvoll, aber ob
     * man sie mit eingibt oder nicht, sei hier egal.
     *
     * @param translation
     *            Die Übersetzung, zu der ein Wert für den Vergleich erzeugt werden soll.
     * @return Der erzeugte Vergleichswert.
     */
    public static String createCompareTranslation(String translation) {
        String compareTranslation = translation;

        compareTranslation = Text.toLowerCase(compareTranslation);
        compareTranslation = removePuncuationMarksAndSpaces(compareTranslation);

        return compareTranslation;
    }

    /** Entfernt Satzzeichen aus der eingegebenen Übersetzung etc. */
    public static String removePuncuationMarksAndSpaces(String translation) {
        String compareTranslation = translation;

        compareTranslation = compareTranslation.replace(" ", "");

        return removePuncuationMarks(compareTranslation);
    }

    /** Entfernt Satzzeichen aus der eingegebenen Übersetzung etc. */
    public static String removePuncuationMarks(String translation) {
        String compareTranslation = translation;

        compareTranslation = compareTranslation.replace("-", "");

        return removePuncuationMarksWithoutHyphen(compareTranslation);
    }

    /** Entfernt Satzzeichen aus der eingegebenen Übersetzung etc. */
    public static String removePuncuationMarksWithoutHyphen(String translation) {
        String compareTranslation = translation;

        compareTranslation = compareTranslation.replace("?", "");
        compareTranslation = compareTranslation.replace("!", "");
        compareTranslation = compareTranslation.replace(",", "");
        compareTranslation = compareTranslation.replace(".", "");
        compareTranslation = compareTranslation.replace("'", "");
        compareTranslation = compareTranslation.replace("\"", "");
        compareTranslation = compareTranslation.replace("\\", "");
        compareTranslation = compareTranslation.replace("/", "");
        compareTranslation = compareTranslation.replace("#", "");
        compareTranslation = compareTranslation.replace("+", "");
        compareTranslation = compareTranslation.replace(";", "");

        return compareTranslation;
    }

    /**
     * Entfernt den Anfang der Beschreibung der Vokabularien ("B1_K04_1_", "VHS_A1.1_" oder "X_").
     *
     * @param description
     *            Die zu verändernde Beschreibung eines Vokabulariums oder einer eigenen Liste.
     * @param hideLessStartOfVocabularyDescriptionForVhs
     *            Gibt an, ob bei Vokabularien und Listen aus der VHS weniger vom Anfang der
     *            Beschreibung der Vokabularien (entfernt "VHS_A1.1_" statt "VHS_A1.1_01_01_")
     *            ausgeblendet werden soll.
     * @return Gekürzte Beschreibung.
     */
    public static String hideStartOfVocabularyDescription(String description,
            boolean hideLessStartOfVocabularyDescriptionForVhs) {
        if (hideLessStartOfVocabularyDescriptionForVhs && description.contains("VHS")) {
            return VocabularyTools.hideLessStartOfVocabularyDescriptionForVhs(
                    description);
        }
        else {
            return VocabularyTools.hideStartOfVocabularyDescriptionStandard(description);
        }
    }

    private static final String NAME_START_REGEX = createNameStartRegex();

    static String createNameStartRegex() {
        List<String> nameStarts = new ArrayList<>();
        for (OwnListGroup olg : OwnListGroup.values()) {
            String nameStart = olg.getNameStart();
            nameStart = nameStart.strip();
            nameStart = nameStart.replace(" ", "[_ ]");
            nameStarts.add(nameStart);
        }

        return Text.joinWithPipe(nameStarts);
    }

    private static final Pattern DESCRIPTIONS_START_PATTERN = Pattern.compile(""
            + "^"
            + "(?:"
                + "[A-Z]+\\d+"
                + "[_ ]"
                + "[A-Z]\\d+"
                + "[_ ]"
                + "\\d+"
            + "|"
                + "VHS"
                + "[_ ]"
                + "[A-Z]\\d\\.\\d"
                + "[_ ]"
                + "\\d+"
                + "[_ ]"
                + "\\d+"
            + "|"
                + NAME_START_REGEX
            + ")"
            + "[_ ]"
            );

    /** Entfernt den Anfang der Beschreibung der Vokabularien ("B1_K04_1_" oder "X_"). */
    static String hideStartOfVocabularyDescriptionStandard(String description) {
        Matcher matcher = DESCRIPTIONS_START_PATTERN.matcher(description);
        if (matcher.find()) {
            return description.substring(matcher.group().length());
        }
        else {
            return description;
        }
    }

    private static final Pattern DESCRIPTIONS_START_PATTERN_VHS = Pattern.compile(""
            + "^"
            + "(?:"
                + "VHS"
                + "[_ ]"
                + "[A-Z]\\d\\.\\d"
            + ")"
            + "[_ ]"
            );

    /**
     * Entfernt im Falle von VHS (nur dann wird diese Methode aufgerufen) weniger vom
     * Anfang der Beschreibung der Vokabularien (entfernt "VHS_A1.1_" statt
     * "VHS_A1.1_01_01_").
     */
    static String hideLessStartOfVocabularyDescriptionForVhs(String description) {
        Matcher matcher = DESCRIPTIONS_START_PATTERN_VHS.matcher(description);
        if (matcher.find()) {
            return description.substring(matcher.group().length());
        }
        else {
            return description;
        }
    }

    /** Erstellt aus den Vokabularen eine Liste der Kategorien. */
    public static List<String> determineCategories(List<? extends Vocabulary> vocabularies) {
        List<String> vocabularyCategories = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            String category = vocabulary.getCategory();
            if (!vocabularyCategories.contains(category)) {
                vocabularyCategories.add(category);
            }
        }

        Collections.sort(vocabularyCategories);

        return vocabularyCategories;
    }

    /** Erstellt aus den Vokabularen eine Liste der Unterkategorien. */
    public static List<String> determineSubCategories(List<? extends Vocabulary> vocabularies) {
        List<String> vocabularySubCategories = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            String subCategory = vocabulary.getSubCategory();
            if (!vocabularySubCategories.contains(subCategory)) {
                vocabularySubCategories.add(subCategory);
            }
        }

        Collections.sort(vocabularySubCategories);

        return vocabularySubCategories;
    }

    /**
     * Erstellt aus den Vokabularen eine Liste der Unterkategorien die zur angegebenen Kategorie
     * gehören.
     *
     * @param vocabularies
     *            Die Liste der Vokabularien.
     * @param category
     *            Die Kategorie, zu der die zugehörigen Unterkategorien ermittelt werden sollen.
     */
    public static List<String> determineSubCategoriesOfCategory(
            List<? extends Vocabulary> vocabularies, String category) {
        List<String> vocabularySubCategoriesForCategory = new ArrayList<>();

        for (Vocabulary vocabulary : vocabularies) {
            String vocabularyCategory = vocabulary.getCategory();
            if (vocabularyCategory.equals(category)) {
                String subCategory = vocabulary.getSubCategory();
                if (!vocabularySubCategoriesForCategory.contains(subCategory)) {
                    vocabularySubCategoriesForCategory.add(subCategory);
                }
            }
        }

        Collections.sort(vocabularySubCategoriesForCategory);

        return vocabularySubCategoriesForCategory;
    }

}
