package de.duehl.vocabulary.japanese.ui.components.display;

import java.awt.Color;
import java.util.List;

import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

import de.duehl.basics.text.Text;
import de.duehl.swing.ui.GuiTools;
import de.duehl.vocabulary.japanese.common.persistence.TranslationCommentAndVocabularyDescriptionColorOptions;
import de.duehl.vocabulary.japanese.data.Vocable;

/**
 * Diese Klasse erzeugt eine Anzeige für die Übersetzungen, den Kommentar und die Beschreibung des
 * Vokabulars einer Vokabel.
 *
 * Nun auch die Suchbegriffe und die Wortarten.
 *
 * @version 1.01     2025-11-15
 * @author Christian Dühl
 */

public class TranslationCommentAndVocabularyDescriptionDisplay {

    private static final int COMMENT_CHARACTERS_PER_LINE = 60;
    private static final int TRANSLATION_COMMENT_CHARACTERS_PER_LINE = 45;


    private static final int FONT_SIZE_BASE = 18;

    /** Die Vokabel, welche angezeigt wird. */
    private final Vocable vocable;

    /**
     * Die TextPane in der die Übersetzungen, den Kommentar und die Beschreibung des Vokabulars
     * dargestellt werden.
     */
    private final JTextPane translationAndCommentPane;

    /** Die Hintergrundfarbe abhängig vom Erfolg der letzten Abfragen. */
    private final Color backgroundColor;

    /** Die Farb-Optionen. */
    private final TranslationCommentAndVocabularyDescriptionColorOptions options;

    /** Der Stil für normalen Text. */
    private Style regularStyle;

    /** Der Stil für Übersetzungen. */
    private Style translationStyle;

    /** Der Stil für den Kommentar. */
    private Style commentStyle;

    /** Der Stil für die Suchworte. */
    private Style searchWordsStyle;

    /** Der Stil für die Wortarten. */
    private Style partsOfSpeachStyle;

    /** Gibt an, ob die Übersetzungen angezeigt werden. */
    private boolean showTranslations;

    /** Gibt an, ob der Kommentar angezeigt wird. */
    private boolean showComment;

    /** Gibt an, ob die Beschreibung des Vokabulars angezeigt wird. */
    private boolean showVocabularyDescription;

    /** Gibt an, ob die Überschrift vor den Übersetzungen angezeigt wird. */
    private boolean showTranslationIntro;

    /**
     * Gibt an, ob in der Einzeldarstellung von Vokabeln und im Dialog zur Bewertung einer
     * Übersetzung alle Übersetzungen angezeigt werden.
     *
     * Dies kann bei vielen Übersetzungen zur Überlänge führen, so dass die Dialoge nach unten aus
     * dem Bildschirm ragen.
     */
    private boolean showAllTranslations;

    /**
     * Gibt an, wieviele Übersetzungen in der Einzeldarstellung von Vokabeln und im Dialog zur
     * Bewertung einer Übersetzung angezeigt werden, falls denn der Benutzer nicht alle
     * Übersetzungen anzeigen möchte (siehe showAllTranslations).
     *
     * Die Anzeige aller oder zu vieler Übersetzungen kann bei vielen Übersetzungen zur Überlänge
     * führen, so dass die Dialoge nach unten aus dem Bildschirm ragen. Mit dieser Einstellung kann
     * man abhängig von der Monitorauflösung eine passende Wahl treffen.
     */
    private int numberOfShownTranslations;

    /**
     * Konstruktor.
     *
     * @param vocable
     *            Die Vokabel, welche angezeigt wird.
     * @param translationAndCommentPane
     *            Die TextPane in der die Übersetzungen und den Kommentar dargestellt werden.
     * @param backgroundColor
     *            Die Hintergrundfarbe abhängig vom Erfolg der letzten Abfragen.
     * @param options
     *            Die Farb-Optionen.
     */
    public TranslationCommentAndVocabularyDescriptionDisplay(Vocable vocable,
            JTextPane translationAndCommentPane, Color backgroundColor,
            TranslationCommentAndVocabularyDescriptionColorOptions options) {
        this.vocable = vocable;
        this.translationAndCommentPane = translationAndCommentPane;
        this.backgroundColor = backgroundColor;
        this.options = options;

        showTranslations = true;
        showComment = true;
        showVocabularyDescription = true;
        showTranslationIntro = true;
        showAllTranslations = true;
        numberOfShownTranslations = -1;
    }

    /*
     * Aufgerufen von:
     *
     * TranslationEvaluationDialog - will Kürzung
     * VocableTester - zeigt keine Übersetzungen an - egal
     * VocableViewer - will Kürzung
     *
     * Was ist mit dem Detail-Dialog? - Der zeigt die Daten ganz anders an, nämlich in String- bzw. AreaSelections
     */

    /** Legt fest, dass die Übersetzungen nicht angezeigt werden. */
    public void hideTranslations() {
        showTranslations = false;
    }

    /** Legt fest, dass der Kommentar nicht angezeigt wird. */
    public void hideComment() {
        showComment = false;
    }

    /** Legt fest, dass die Beschreibung des Vokabulars nicht angezeigt wird. */
    public void hideVocabularyDescription() {
        showVocabularyDescription = false;
    }

    /** Legt fest, dass die Überschrift vor den Übersetzungen nicht angezeigt wird. */
    public void hideTranslationIntro() {
        showTranslationIntro = false;
    }

    /**
     * Legt fest, wieviele Übersetzungen in der Einzeldarstellung von Vokabeln und im Dialog zur
     * Bewertung einer Übersetzung angezeigt werden.
     *
     * Die Anzeige aller oder zu vieler Übersetzungen kann bei vielen Übersetzungen zur Überlänge
     * führen, so dass die Dialoge nach unten aus dem Bildschirm ragen. Mit dieser Einstellung kann
     * man abhängig von der Monitorauflösung eine passende Wahl treffen.
     */
    public void setNumberOfShownTranslations(int numberOfShownTranslations) {
        showAllTranslations = false;
        this.numberOfShownTranslations = numberOfShownTranslations;
    }

    /** Initialisiert und befüllt die TextPane. */
    public void display() {
        initTextPane();
        createStyles(); // Was passiert, wenn es die schon gibt?
        clearContent();
        fillWithNewText();
    }

    private void initTextPane() {
        translationAndCommentPane.setEditable(false);
        GuiTools.respectTextPaneBackgroundInNimbusLookAndFeel(translationAndCommentPane);
        translationAndCommentPane.setBackground(backgroundColor);
    }

    /** Legt die Stile für die Auszeichnung fest. */
    private void createStyles() {
        StyledDocument document = translationAndCommentPane.getStyledDocument();
        Style defaultStyle = StyleContext.getDefaultStyleContext().
                getStyle(StyleContext.DEFAULT_STYLE);

        regularStyle = document.addStyle("regular", defaultStyle);
        StyleConstants.setForeground(regularStyle, options.getVocableColorStandard());
        StyleConstants.setBackground(regularStyle, backgroundColor);
        StyleConstants.setFontSize(regularStyle, FONT_SIZE_BASE);
        StyleConstants.setSpaceAbove(regularStyle, 1);
        StyleConstants.setSpaceBelow(regularStyle, 1);
        StyleConstants.setLeftIndent(regularStyle, 16);
        StyleConstants.setRightIndent(regularStyle, 16);
        StyleConstants.setFirstLineIndent(regularStyle, 16);
        StyleConstants.setFontFamily(regularStyle, "serif");
        StyleConstants.setAlignment(regularStyle, StyleConstants.ALIGN_LEFT);

        translationStyle = document.addStyle("translation", regularStyle);
        StyleConstants.setForeground(translationStyle, options.getVocableColorTranslation());
        StyleConstants.setBackground(translationStyle, backgroundColor);
        StyleConstants.setFontSize(translationStyle, FONT_SIZE_BASE + 4);
        StyleConstants.setBold(translationStyle, true);

        commentStyle = document.addStyle("comment", regularStyle);
        StyleConstants.setForeground(commentStyle, options.getVocableColorComment());
        StyleConstants.setBackground(commentStyle, backgroundColor);

        searchWordsStyle = document.addStyle("searchWords", regularStyle);
        StyleConstants.setForeground(searchWordsStyle, options.getVocableColorSearchWords());
        StyleConstants.setBackground(searchWordsStyle, backgroundColor);

        partsOfSpeachStyle = document.addStyle("partsOfSpeach", regularStyle);
        StyleConstants.setForeground(partsOfSpeachStyle, options.getVocableColorPartOfSpeach());
        StyleConstants.setBackground(partsOfSpeachStyle, backgroundColor);
    }

    private void clearContent() {
        try {
            StyledDocument document = translationAndCommentPane.getStyledDocument();
            document.remove(0, document.getLength());
        }
        catch (BadLocationException exception) {
            throw new RuntimeException("Probleme beim Löschen des Inhaltes des Dokument.",
                    exception);
        }
    }

    private void fillWithNewText() {
        try {
            tryToFillWithNewText();
        }
        catch (Exception exception) {
            /*
            GuiTools.informUser("Hier ging etwas schief", "Beim Erzeugen der Texte für die "
                    + "Übersetzungen und den Kommentar der Vokabel " + vocable.getKana()
                    + " gab es ein Problem.\n"
                    + "Noch einmal versuchen!");
            */
            throw new RuntimeException("Probleme beim Einfügen eines Stils ins Dokument.",
                    exception);
        }
    }

    private void tryToFillWithNewText() throws BadLocationException {
        if (showTranslations) {
            fillTranslations();
        }
        if (showComment) {
            fillComment();
            fillSearchWords();
            fillPartsOfSpeach();
        }
        if (showVocabularyDescription) {
            fillVocabularyDescription();
        }
    }

    private void fillTranslations() throws BadLocationException {
        List<String> translations = vocable.getTranslations();

        if (translations.size() == 1) {
            showOneTranslation(translations.get(0));
        }
        else {
            showMultipleTranslations(translations);
        }
    }

    private void showOneTranslation(String translation) throws BadLocationException {
        String intro;
        if (showTranslationIntro) {
            intro = "Übersetzung: ";
        }
        else {
            intro = "";
        }
        addTranslation(intro, translation);
    }

    private void showMultipleTranslations(List<String> translations) throws BadLocationException {
        StyledDocument document = translationAndCommentPane.getStyledDocument();
        if (showTranslationIntro) {
            document.insertString(document.getLength(), "Übersetzungen:\n", regularStyle);
        }
        int number = 0;
        int numberOfTranslations = translations.size();
        for (String translation : translations) {
            if (number > 0) {
                document.insertString(document.getLength(), "\n", regularStyle);
            }
            ++number;
            String intro = number + ") ";
            addTranslation(intro, translation);
            if (!showAllTranslations
                    && number >= numberOfShownTranslations) {
                if (number < numberOfTranslations) {
                    document.insertString(document.getLength(), "\n", regularStyle);
                    addThereAreMoreTranslation(numberOfTranslations - number);
                }
                break;
            }
        }
    }

    private void addTranslation(String intro, String translation) throws BadLocationException {
        StyledDocument document = translationAndCommentPane.getStyledDocument();
        document.insertString(document.getLength(), intro, regularStyle);
        String translationWithLineBreaks = Text.addLineBreaks(translation,
                TRANSLATION_COMMENT_CHARACTERS_PER_LINE, intro.length());
        document.insertString(document.getLength(), translationWithLineBreaks, translationStyle);
    }

    private void addThereAreMoreTranslation(int numberOfNotShownTranslations)
            throws BadLocationException {
        String text = "... und " + numberOfNotShownTranslations + " weitere Übersetzungen";
        StyledDocument document = translationAndCommentPane.getStyledDocument();
        document.insertString(document.getLength(), text, regularStyle);
    }

    private void fillComment() throws BadLocationException {
        String comment = vocable.getComment();
        if (!comment.isEmpty()) {
            comment = Text.addLineBreaks(comment, COMMENT_CHARACTERS_PER_LINE);
            comment = Text.removeLineBreakAtEndIfEndsWith(comment);

            StyledDocument document = translationAndCommentPane.getStyledDocument();
            document.insertString(document.getLength(), "\n", regularStyle);
            document.insertString(document.getLength(), comment, commentStyle);
        }
    }

    /*
     *  TODO Prüfen ob es hier noch leere Bereiche unten gibt, Wenn hier wirklich umgebrochen wird.
     *
     *  Ja, z.B. bei
     *
     *  失礼ですが、 何歳 です か。 (しつれいですが、 なんさい です か。)
     *  in B1 K03 1 6/8 in der Einzeldarstellung.
     *  Vielleicht liegt das Problem aber auch da?
     */

    private void fillSearchWords() {
        List<String> searchWords = vocable.getSearchWords();
        if (!searchWords.isEmpty()) {
            String title;
            if (searchWords.size() == 1) {
                title = "Suchbegriff";
            }
            else {
                title = "Suchbegriffe";
            }
            String searchWordsString = title + ": " + Text.joinWithCommaAndBlank(searchWords);
            searchWordsString = Text.addLineBreaks(searchWordsString, COMMENT_CHARACTERS_PER_LINE);
            searchWordsString = Text.removeLineBreakAtEndIfEndsWith(searchWordsString);

            try { // Warum braucht man das hier, aber nicht anderswo?
                StyledDocument document = translationAndCommentPane.getStyledDocument();
                document.insertString(document.getLength(), "\n", regularStyle);
                document.insertString(document.getLength(), searchWordsString, searchWordsStyle);
            }
            catch (BadLocationException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void fillPartsOfSpeach() {
        List<String> partsOfSpeech = vocable.getPartsOfSpeech();
        if (!partsOfSpeech.isEmpty()) {
            String title;
            if (partsOfSpeech.size() == 1) {
                title = "Wortart";
            }
            else {
                title = "Wortarten";
            }
            String partsOfSpeechString = title + ": " + Text.joinWithCommaAndBlank(partsOfSpeech);
            partsOfSpeechString = Text.addLineBreaks(partsOfSpeechString, COMMENT_CHARACTERS_PER_LINE);
            partsOfSpeechString = Text.removeLineBreakAtEndIfEndsWith(partsOfSpeechString);

            try { // Warum braucht man das hier, aber nicht anderswo?
                StyledDocument document = translationAndCommentPane.getStyledDocument();
                document.insertString(document.getLength(), "\n", regularStyle);
                document.insertString(document.getLength(), partsOfSpeechString, partsOfSpeachStyle);
            }
            catch (BadLocationException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void fillVocabularyDescription() throws BadLocationException {
        String text = "(Aus dem Vokabular '" + vocable.getVocabularyDescription() + "')";
        StyledDocument document = translationAndCommentPane.getStyledDocument();
        document.insertString(document.getLength(), "\n", regularStyle);
        document.insertString(document.getLength(), text, regularStyle);
    }

}
