package de.duehl.vocabulary.japanese.data;

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

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.text.Text;
import de.duehl.swing.ui.components.selections.data.SearchWordSelectionInputs;
import de.duehl.vocabulary.japanese.data.symbol.Hiragana;
import de.duehl.vocabulary.japanese.grammar.AdjectiveSearchWords;
import de.duehl.vocabulary.japanese.tools.VocabularyTools;

import static de.duehl.vocabulary.japanese.grammar.VerbSearchWords.WOERERBUCHFORM;
import static de.duehl.vocabulary.japanese.grammar.AdjectiveSearchWords.I_ADJECTIVE_POSITIVE_PRESENCE;
import static de.duehl.vocabulary.japanese.grammar.AdjectiveSearchWords.NA_ADJECTIVE_POSITIVE_PRESENCE;
import static de.duehl.vocabulary.japanese.grammar.GrammarTools.PART_OF_SPEECH_VERB;
import static de.duehl.vocabulary.japanese.grammar.GrammarTools.PART_OF_SPEECH_ADJECTIVE;

/**
 * Diese Klasse stellt eine Vokabel dar, wie sie in den für alle Benutzer nutzbaren Dateien mit den
 * Vokabularien vorkommen. Diese entstammt aus einer der Vokabular-Dateien im Vokabel-Verzeichnis.
 *
 * Die Zuordnung zu den ergänzenden Daten des einzelnen Benutzers zu 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 InternalAdditionalVocableData.
 *
 * @version 1.01     2025-11-30
 * @author Christian Dühl
 */

public class Vocable {

    /** Die Vokabel in Hiragana oder Katakana. */
    private String kana;

    /** Die Vokabel in Hiragana oder Katakana für den Vergleich mit den Benutzereingaben. */
    private String compareKana;

    /** Das Wort als Kanji. */
    private String kanji;

    /** Die Vokabel in Kanji für den Vergleich mit den Benutzereingaben. */
    private String compareKanji;

    /** Das Wort als Romaji. */
    private String romaji;

    /** Die Vokabel in Romaji für den Vergleich mit den Benutzereingaben. */
    private String compareRomaji;

    /** Die Darstellung der Aussprache in Textform. */
    private String pronunciation;

    /** Die Liste der Übersetzungen. */
    private final List<String> translations;

    /** Die Liste der Übersetzungen für den Vergleich mit den Benutzereingaben. */
    private final List<String> compareTranslations;

    /** Die Aussprache als mp3-Datei ohne Pfad. */
    private String bareMp3;

    /** Die Aussprache als mp3-Datei mit Pfad. */
    private String mp3;

    /** Die Bemerkung. */
    private String comment;

    /** Die Suchbegriffe oder Schlagworte der Vokabel. */
    private List<String> searchWords;

    /** Die Wortarten der Vokabel. */
    private List<String> partsOfSpeech;

    /** Die Beschreibung des Vokabulars. */
    private String vocabularyDescription;

    /** Konstruktor. */
    public Vocable() {
        kana = "";
        compareKana = "";
        kanji = "";
        compareKanji = "";
        romaji = "";
        compareRomaji = "";
        pronunciation = "";
        translations = new ArrayList<>();
        compareTranslations = new ArrayList<>();
        bareMp3 = "";
        mp3 = "";
        comment = "";
        searchWords = new ArrayList<>();
        partsOfSpeech = new ArrayList<>();
        vocabularyDescription = "";
    }

    /** Getter für die Vokabel in Hiragana oder Katakana. */
    public String getKana() {
        return kana;
    }

    /** Setter für die Vokabel in Hiragana oder Katakana. */
    public Vocable setKana(String kana) {
        this.kana = kana;
        compareKana = VocabularyTools.createCompareTranslation(kana);
        return this;
    }

    /**
     * Getter für die Vokabel in Hiragana oder Katakana für den Vergleich mit den Benutzereingaben.
     */
    public String getCompareKana() {
        return compareKana;
    }

    /** Getter für das Wort als Kanji. */
    public String getKanji() {
        return kanji;
    }

    /** Setter für das Wort als Kanji. */
    public Vocable setKanji(String kanji) {
        this.kanji = kanji;
        compareKanji = VocabularyTools.createCompareTranslation(kanji);
        return this;
    }

    /** Getter für die Vokabel in Kanji für den Vergleich mit den Benutzereingaben. */
    public String getCompareKanji() {
        return compareKanji;
    }

    /** Getter für das Wort als Romaji. */
    public String getRomaji() {
        return romaji;
    }

    /** Setter für das Wort als Romaji. */
    public Vocable setRomaji(String romaji) {
        this.romaji = romaji;
        compareRomaji = VocabularyTools.createCompareTranslation(romaji);
        return this;
    }

    /** Die Vokabel in Romaji für den Vergleich mit den Benutzereingaben. */
    public String getCompareRomaji() {
        return compareRomaji;
    }

    /** Getter für die Darstellung der Aussprache in Textform. */
    public String getPronunciation() {
        return pronunciation;
    }

    /** Setter für die Darstellung der Aussprache in Textform. */
    public Vocable setPronunciation(String pronunciation) {
        this.pronunciation = pronunciation;
        return this;
    }

    /** Getter für die Liste der Übersetzungen. */
    public List<String> getTranslations() {
        return translations;
    }

    /** Fügt die übergebene Übersetzung zur Liste der Übersetzungen hinzu. */
    public Vocable addToTranslations(String translation) {
        translations.add(translation);
        String compareTranslation = VocabularyTools.createCompareTranslation(translation);
        compareTranslations.add(compareTranslation);
        return this;
    }

    /** Getter für die Liste der Übersetzungen für den Vergleich mit den Benutzereingaben. */
    public List<String> getCompareTranslations() {
        return compareTranslations;
    }

    /** Getter für die Aussprache als mp3-Datei ohne Pfad. */
    public String getBareMp3() {
        return bareMp3;
    }

    /** Setter für die Aussprache als mp3-Datei ohne Pfad. */
    public Vocable setBareMp3(String bareMp3) {
        this.bareMp3 = bareMp3;
        return this;
    }

    /** Getter für die Aussprache als mp3-Datei mit Pfad. */
    public String getMp3() {
        return mp3;
    }

    /** Setter für die Aussprache als mp3-Datei mit Pfad. */
    public Vocable setMp3(String mp3) {
        this.mp3 = mp3;
        return this;
    }

    /** Getter für die Bemerkung. */
    public String getComment() {
        return comment;
    }

    /** Setter für die Bemerkung. */
    public Vocable setComment(String bemerkung) {
        this.comment = bemerkung;
        return this;
    }

    /** Getter für die Suchbegriffe oder Schlagworte der Vokabel. */
    public List<String> getSearchWords() {
        return searchWords;
    }

    /** Setter für die Suchbegriffe oder Schlagworte der Vokabel. */
    public void setSearchWords(List<String> searchWords) {
        this.searchWords = searchWords;
    }

    /** Getter für die Wortarten der Vokabel. */
    public List<String> getPartsOfSpeech() {
        return partsOfSpeech;
    }

    /** Setter für die Wortarten der Vokabel. */
    public void setPartsOfSpeech(List<String> partsOfSpeech) {
        this.partsOfSpeech = partsOfSpeech;
    }

    /** Getter für die Beschreibung des Vokabulars. */
    public String getVocabularyDescription() {
        return vocabularyDescription;
    }

    /** Setter für die Beschreibung des Vokabulars. */
    public Vocable setVocabularyDescription(String vocabularyDescription) {
        this.vocabularyDescription = vocabularyDescription;
        return this;
    }

    /**
     * Gibt an, ob der Suchbegriff mit den Sucheinstellungen in den Kana der Vokabel auftaucht.
     *
     * Für die erweiterte Suche.
     */
    public boolean kanaContains(SearchWordSelectionInputs kanaInputs) {
        return singleWordContains(kanaInputs, kana, compareKana);
    }

    /**
     * Gibt an, ob der Suchbegriff mit den Sucheinstellungen in den Kanji der Vokabel auftaucht.
     *
     * Für die erweiterte Suche.
     */
    public boolean kanjiContains(SearchWordSelectionInputs kanjiInputs) {
        return singleWordContains(kanjiInputs, kanji, compareKanji);
    }

    /**
     * Gibt an, ob der Suchbegriff mit den Sucheinstellungen in den Romaji der Vokabel auftaucht.
     *
     * Für die erweiterte Suche.
     */
    public boolean romajiContains(SearchWordSelectionInputs romajiInputs) {
        return singleWordContains(romajiInputs, romaji, compareRomaji);
    }

    /**
     * Gibt an, ob der Suchbegriff mit den Sucheinstellungen in der Aussprache der Vokabel
     * auftaucht.
     *
     * Für die erweiterte Suche.
     */
    public boolean pronunciationContains(SearchWordSelectionInputs pronunciationInputs) {
        String comparePronunciation = VocabularyTools.createCompareTranslation(pronunciation);
        return singleWordContains(pronunciationInputs, pronunciation, comparePronunciation);
    }

    /**
     * Gibt an, ob der Suchbegriff mit den Sucheinstellungen in den Übersetzungen der Vokabel
     * auftaucht.
     *
     * Für die erweiterte Suche.
     */
    public boolean translationsContains(SearchWordSelectionInputs translationInputs) {
        return listOfWordsContains(translationInputs, translations, compareTranslations);
    }

    /**
     * Gibt an, ob der Suchbegriff mit den Sucheinstellungen in dem Kommentar der Vokabel
     * auftaucht.
     *
     * Für die erweiterte Suche.
     */
    public boolean commentContains(SearchWordSelectionInputs commentInputs) {
        String compareComment = VocabularyTools.createCompareTranslation(comment);
        return singleWordContains(commentInputs, comment, compareComment);
    }

    /**
     * Gibt an, ob der Suchbegriff mit den Sucheinstellungen in den Suchbegriffen der Vokabel
     * auftaucht.
     *
     * Für die erweiterte Suche.
     */
    public boolean searchWordsContains(SearchWordSelectionInputs searchWordInputs) {
        return listOfWordsContains(searchWordInputs, searchWords);
    }

    /**
     * Gibt an, ob der Suchbegriff mit den Sucheinstellungen in den Wortarten der Vokabel
     * auftaucht.
     *
     * Für die erweiterte Suche.
     */
    public boolean partOfSpeechContains(SearchWordSelectionInputs partOfSpeechInputs) {
        return listOfWordsContains(partOfSpeechInputs, partsOfSpeech);
    }

    /**
     * Gibt an, ob der Suchbegriff mit den Sucheinstellungen in den Feldern der Vokabel auftaucht.
     *
     * Für die erweiterte Suche.
     */
    public boolean contains(SearchWordSelectionInputs searchInputs) {
        return kanaContains(searchInputs)
                || kanjiContains(searchInputs)
                || romajiContains(searchInputs)
                || pronunciationContains(searchInputs)
                || translationsContains(searchInputs)
                || commentContains(searchInputs)
                || searchWordsContains(searchInputs)
                || partOfSpeechContains(searchInputs)
                ;
        /*
         * Falls das mal zu langsam wird, muss man hier einmal das Vergleichswort zur searchInputs
         * bilden und die Methode singleWordContains() ergänzen um eine, der man das
         * Vergleichswort bereits übergibt.
         *
         * String searchCompareWord = VocabularyTools.createCompareTranslation(inputs.getText());
         * Aber dann muss ich die ganzen Innenleben der oben aufgerufenen Methoden hier nachbilden
         * auch wenn das nicht kompliziert ist...
         */
    }

    /**
     * Sucht nach den Suchkriterien in dem Inhalt eines Feldes.
     *
     * @param inputs
     *            Die Suchkriterien.
     * @param originalValue
     *            Der originale Inhalt des Feldes.
     * @param compareValue
     *            Der Vergleichs-Inhalt des Feldes.
     * @return Wahrheitswert.
     */
    private boolean singleWordContains(SearchWordSelectionInputs inputs, String originalValue,
            String compareValue) {
        String search;
        String value;
        if (inputs.isWholeWordSearch()) {
            if (inputs.isCaseSensitiveSearch()) {
                search = inputs.getText();
                value = originalValue;
            }
            else {
                search = Text.toLowerCase(inputs.getText());
                value = Text.toLowerCase(originalValue);
                /*
                 * Falls das irgendwann zu langsam wird, kann man die nur in Kleinschreibweise
                 * geänderten Ausprägungen ebenfalls in der Vokabel mit abspeichern und hier
                 * entsprechend übergeben. Aber im Moment ist es auch so schnell genug.
                 */
            }
        }
        else if (inputs.isCaseSensitiveSearch()) {
            search = VocabularyTools.removePuncuationMarks(inputs.getText());
            value = VocabularyTools.removePuncuationMarks(originalValue);
            /*
             * Falls das irgendwann zu langsam wird, kann man die Ausprägungen ohne Satzzeichen
             * ebenfalls in der Vokabel mit abspeichern und hier entsprechend übergeben. Aber im
             * Moment ist es auch so schnell genug.
             */
        }
        else {
            search = VocabularyTools.createCompareTranslation(inputs.getText());
            value = compareValue;
        }

        if (inputs.isWholeWordSearch()) {
            if (value.contains(search)) { // möglichst wenig Regex-Prüfungen!
                Pattern pattern = Pattern.compile("\\b" + Pattern.quote(search) + "\\b");
                Matcher matcher = pattern.matcher(value);
                return (matcher.find());
            }
            else {
                return false;
            }
        }
        else {
            return value.contains(search);
        }
    }

    /**
     * Sucht nach den Suchkriterien in einer Liste von Inhalten eines Feldes.
     *
     * @param inputs
     *            Die Suchkriterien.
     * @param originalValues
     *            Die originalen Inhalte des Feldes.
     * @return Wahrheitswert.
     */
    private boolean listOfWordsContains(SearchWordSelectionInputs inputs,
            List<String> originalValues) {
        List<String> compareValues = new ArrayList<>();
        for (String originalValue : originalValues) {
            String compareValue = VocabularyTools.createCompareTranslation(originalValue);
            compareValues.add(compareValue);
        }
        return listOfWordsContains(inputs, originalValues, compareValues);
    }

    /**
     * Sucht nach den Suchkriterien in einer Liste von Inhalten eines Feldes.
     *
     * @param inputs
     *            Die Suchkriterien.
     * @param originalValues
     *            Die originalen Inhalte des Feldes.
     * @param compareValues
     *            Die Vergleichs-Inhalte des Feldes.
     * @return Wahrheitswert.
     */
    private boolean listOfWordsContains(SearchWordSelectionInputs inputs,
            List<String> originalValues, List<String> compareValues) {
        if (originalValues.size() != compareValues.size()) {
            throw new IllegalArgumentException("Die beiden Listen haben verschieden viele "
                    + "Elemente!\n"
                    + "Originale Werte:\n"
                    + CollectionsHelper.listListNice(originalValues)
                    + "Vergleichs-Werte:\n"
                    + CollectionsHelper.listListNice(compareValues)
                    );
        }

        for (int index = 0; index < originalValues.size(); ++index) {
            String originalValue = originalValues.get(index);
            String compareValue = compareValues.get(index);
            if (singleWordContains(inputs, originalValue, compareValue)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Gibt an, ob der Suchbegriff in den Feldern der Vokabel auftaucht.
     *
     * Für die normale Suche.
     */
    public boolean contains(String search) {
        String compareSearch = VocabularyTools.createCompareTranslation(search);
        boolean contains = containsInternal(compareSearch);
        if (!contains) {
            contains = containsInternal(search);
        }
        return contains;
    }

    private boolean containsInternal(String compareSearch) {
        return compareKana.contains(compareSearch)
                || compareKanji.contains(compareSearch)
                || compareRomaji.contains(compareSearch)
                || pronunciation.contains(compareSearch)
                || translationsContainsInternal(compareSearch)
                || bareMp3containsInternal(compareSearch) // Ohne barename findet er auch in den
                                                          // anderen Verzeichnissen Begriffe!
                || comment.contains(compareSearch)
                || searchWordsContainsInternal(compareSearch)
                || partOfSpeechContainsInternal(compareSearch)
                ;
    }

    private boolean translationsContainsInternal(String compareSearch) {
        for (String translation : translations) {
            if (translation.contains(compareSearch)) {
                return true;
            }
        }
        for (String compareTranslation : compareTranslations) {
            if (compareTranslation.contains(compareSearch)) {
                return true;
            }
        }
        return false;
    }

    private boolean bareMp3containsInternal(String compareSearch) {
        return bareMp3.contains(compareSearch);
    }

    private boolean searchWordsContainsInternal(String compareSearch) {
        for (String searchWord : searchWords) {
            if (searchWord.contains(compareSearch)) {
                return true;
            }
        }
        return false;
    }

    private boolean partOfSpeechContainsInternal(String compareSearch) {
        for (String partOfSpeech : partsOfSpeech) {
            if (partOfSpeech.contains(compareSearch)) {
                return true;
            }
        }
        return false;
    }

    /** Prüft, ob die Vokabel ein Verb ist. */
    public boolean isVerb() {
        return partsOfSpeech.contains(PART_OF_SPEECH_VERB);
    }

    /** Prüft, ob die Vokabel ein Verb in Wörterbuchform ist. */
    public boolean isVerbInWoerterbuchform() {
        return isVerb() && searchWords.contains(WOERERBUCHFORM);
    }

    /** Prüft, ob die Vokabel ein Adjektiv ist. */
    public boolean isAdjective() {
        return partsOfSpeech.contains(PART_OF_SPEECH_ADJECTIVE);
    }

    /** Prüft, ob die Vokabel ein Adjektiv in Positiver Gegenwart (I- oder NA-Adjektiv) ist. */
    public boolean isAdjectivInPositivePresence() {
        return isIAdjectivInPositivePresence() || isNaAdjectivInPositivePresence();
    }

    /** Prüft, ob die Vokabel ein I-Adjektiv in Positiver Gegenwart ist. */
    public boolean isIAdjectivInPositivePresence() {
        return isAdjective() && searchWords.contains(I_ADJECTIVE_POSITIVE_PRESENCE);
    }

    /** Prüft, ob die Vokabel ein NA-Adjektiv in Positiver Gegenwart  ist. */
    public boolean isNaAdjectivInPositivePresence() {
        return isAdjective() && searchWords.contains(NA_ADJECTIVE_POSITIVE_PRESENCE);
    }

    /** Prüft ob die Vokabel ein I-Adjektiv ist. */
    public boolean isIAdjective() {
        if (isAdjective()) {
            for (String searchWord : searchWords) {
                if (AdjectiveSearchWords.SORTED_I_ADJEKTIVE_SEARCH_WORDS.contains(searchWord)) {
                    return true;
                }
            }
            return false;
        }
        else {
            return false;
        }
    }

    /** Prüft ob die Vokabel ein Na-Adjektiv ist. */
    public boolean isNaAdjective() {
        if (isAdjective()) {
            for (String searchWord : searchWords) {
                if (AdjectiveSearchWords.SORTED_NA_ADJEKTIVE_SEARCH_WORDS.contains(searchWord)) {
                    return true;
                }
            }
            return false;
        }
        else {
            return false;
        }
    }

    @Override
    public String toString() {
        return "Vocable [" + "\n"
                + "    kana=" + kana + ", " + "\n"
                + "    kanji=" + kanji + ", " + "\n"
                + "    romaji=" + romaji + ", " + "\n"
                + "    pronunciation=" + pronunciation + ", " + "\n"
                + "    translations=" + translations + ", " + "\n"
                + "    compareTranslations=" + compareTranslations + ", " + "\n"
                + "    bareMp3=" + bareMp3 + ", " + "\n"
                + "    mp3=" + mp3 + ", " + "\n"
                + "    comment=" + comment + ", " + "\n"
                + "    searchWords=" + searchWords + ", " + "\n"
                + "    partsOfSpeech=" + partsOfSpeech + ", " + "\n"
                + "    vocabularyDescription=" + vocabularyDescription  + "\n"
                + "]";
    }

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

        String firstLineInsertion =
                Text.multipleString(" ", Math.max(0, numberOfInsertionSpaces - 4));
        String insertion = Text.multipleString(" ", numberOfInsertionSpaces);

        builder.append(firstLineInsertion + "Vocable:\n");
        builder.append(insertion + "Kana                 : " + kana + "\n");
        builder.append(insertion + "Kanji                : " + kanji + "\n");
        builder.append(insertion + "romaji               : " + romaji + "\n");
        builder.append(insertion + "pronunciation        : " + pronunciation + "\n");
        if (translations.size() == 1) {
            builder.append(insertion + "translation          : " + translations.get(0) + "\n");
        }
        else {
            builder.append(insertion + "translations :\n");
            for (String translation : translations) {
                builder.append(insertion + "    " + translation + "\n");
            }
        }
        builder.append(insertion + "bareMp3              : " + bareMp3 + "\n");
        builder.append(insertion + "mp3                  : " + mp3 + "\n");
        builder.append(insertion + "comment              : " + comment + "\n");
        if (partsOfSpeech.size() == 0) {
            builder.append(insertion + "partOfSpeech         : -\n");
        }
        if (partsOfSpeech.size() == 1) {
            builder.append(insertion + "partOfSpeech          : " + partsOfSpeech.get(0) + "\n");
        }
        else {
            builder.append(insertion + "partOfSpeech :\n");
            for (String partOfSpeech : partsOfSpeech) {
                builder.append(insertion + "    " + partOfSpeech + "\n");
            }
        }
        if (searchWords.size() == 0) {
            builder.append(insertion + "searchWords          : -\n");
        }
        if (searchWords.size() == 1) {
            builder.append(insertion + "searchWords          : " + searchWords.get(0) + "\n");
        }
        else {
            builder.append(insertion + "searchWords :\n");
            for (String searchWord : searchWords) {
                builder.append(insertion + "    " + searchWord + "\n");
            }
        }
        builder.append(insertion + "vocabularyDescription: " + vocabularyDescription + "\n");

        return builder.toString();
    }

    /**
     * Gibt die Kana in japanischen Anführungszeichen 「 ... 」 zurück.
     *
     * Ist kana leer, sind es nur die Anführungszeichen ohne Inhalt.
     */
    public String getKanaInJapaneseQuotes() {
        return "「" + kana + "」";
    }

    /**
     * Gibt die Kanji in japanischen Anführungszeichen 「 ... 」 zurück.
     *
     * Ist kanji leer, sind es nur die Anführungszeichen ohne Inhalt.
     */
    public String getKanjiInJapaneseQuotes() {
        return "「" + kanji + "」";
    }

    /**
     * Erzeugt eine hübsche Darstellung der Vokabel mit Kanji, Kana und Romaji in japanischen und
     * runden Klammern.
     *
     * Wenn Kanji leer oder gleich Kana ist, wird ein entsprechend angepasster Test erzeugt.
     */
    public String getKanjiKanaRomajiWithJapaneseBraces() {
        String text = "";

        boolean showKanji = !kanji.isEmpty() && !kanji.equals(kana);
        if (showKanji) {
            text += getKanjiInJapaneseQuotes() + " (";
        }
        text += getKanaInJapaneseQuotes();
        if (showKanji) {
            text += ", ";
        }
        else {
            text += " (";
        }
        text += romaji + ")";

        return text;
    }

    /** Gibt an, ob das Ende der Vokabel zu いる/える-Verben passt. */
    public boolean hasIruEruEnd() {
        if (kana.length() < 2) {
            return false;
        }

        String lastChar = kana.substring(kana.length() - 1, kana.length());
        if (!lastChar.equals(Hiragana.RU.getCharacter())) {
            return false;
        }

        String beforeLastChar = kana.substring(kana.length() - 2, kana.length() - 1);

        List<Hiragana> iruEruHaraganaList = new ArrayList<>();
        iruEruHaraganaList.addAll(Hiragana.getHiraganaEndingWithI());
        iruEruHaraganaList.addAll(Hiragana.getHiraganaEndingWithE());

        for (Hiragana iruEruHiragana : iruEruHaraganaList) {
            if (beforeLastChar.equals(iruEruHiragana.getCharacter())) {
                return true;
            }
        }

        return false;
    }

    /** Gibt an, ob die Kana der Vokabel auf die übergebene Hiragana enden. */
    public boolean endWithHiragana(Hiragana hiragana) {
        if (kana.length() < 1) {
            return false;
        }

        String lastChar = kana.substring(kana.length() - 1, kana.length());
        return lastChar.equals(hiragana.getCharacter());
    }

    @Override
    public int hashCode() {
        return Objects.hash(bareMp3, comment, compareTranslations, kana, kanji, mp3, partsOfSpeech,
                pronunciation, romaji, searchWords, translations, vocabularyDescription);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Vocable other = (Vocable) obj;
        return Objects.equals(bareMp3, other.bareMp3) && Objects.equals(comment, other.comment)
                && Objects.equals(compareTranslations, other.compareTranslations)
                && Objects.equals(kana, other.kana) && Objects.equals(kanji, other.kanji)
                && Objects.equals(mp3, other.mp3)
                && Objects.equals(partsOfSpeech, other.partsOfSpeech)
                && Objects.equals(pronunciation, other.pronunciation)
                && Objects.equals(romaji, other.romaji)
                && Objects.equals(searchWords, other.searchWords)
                && Objects.equals(translations, other.translations)
                && Objects.equals(vocabularyDescription, other.vocabularyDescription);
    }

}
