package de.duehl.vocabulary.japanese.ui.dialog.options.colors.eleven;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JPanel;

import de.duehl.basics.text.NumberString;
import de.duehl.swing.ui.GuiTools;
import de.duehl.swing.ui.components.selections.StringSelection;
import de.duehl.swing.ui.layout.VerticalLayout;
import de.duehl.vocabulary.japanese.common.color.VocableColors;
import de.duehl.vocabulary.japanese.common.color.data.ColorModificationType;
import de.duehl.vocabulary.japanese.common.color.data.DefaultColors;
import de.duehl.vocabulary.japanese.common.persistence.Options;

/**
 * Diese Klasse erzeugt die grafische Oberfläche zur Bearbeitung der Farben des Vokabel-Trainers.
 *
 * Es gibt elf Farben, diese beziehen sich auf die elf Möglichkeiten, wie man die letzten zehn
 * Abfragen beantwortet haben kann:
 *
 *      1. Keine Abfrage richtig beantwortet, zehn Abfragen falsch beantwortet.
 *      2. Eine Abfrage richtig beantwortet, neun Abfragen falsch beantwortet.
 *      3. Zwei Abfragen richtig beantwortet, acht Abfragen falsch beantwortet.
 *      4. Drei Abfragen richtig beantwortet, sieben Abfragen falsch beantwortet.
 *      5. Vier Abfragen richtig beantwortet, sechs Abfragen falsch beantwortet.
 *      6. Fünf Abfragen richtig beantwortet, fünf Abfragen falsch beantwortet.
 *      7. Sechs Abfragen richtig beantwortet, vier Abfragen falsch beantwortet.
 *      8. Sieben Abfragen richtig beantwortet, drei Abfragen falsch beantwortet.
 *      9. Acht Abfragen richtig beantwortet, zwei Abfragen falsch beantwortet.
 *     10. Neun Abfragen richtig beantwortet, eine Abfrage falsch beantwortet.
 *     11. Zehn Abfragen richtig beantwortet, keine Abfrage falsch beantwortet.
 *
 * Die Farben gehen von rot (schlecht) über gelb (mittel) bis grün (gut).
 *
 * @version 1.01     2024-08-15
 * @author Christian Dühl
 */

public class ElevenColorsOptionsUi {

    private static final String COLOR_MODIFICATION_EXPLANATION = """
            Hier werden Farben in der Form von sogenannten RGB-Farben verwendet.
            Das R steht für 'red', also Rot, das G für 'green', also Grün und das
            B für 'blue', also Blau.
            Jedem dieser Werte ist eine Zahl zwischen 0 und 255 (einschließlich)
            zugeordnet. Dabei entsprechen die Werte (0, 0, 0) der Farbe Schwarz und
            die Werte (255, 255, 255) der Farbe Weiß. (255, 0, 0) entspricht einem
            knalligen Rot und so weiter.

            Um aus den hinterlegten Farben Vorder- bzw. Hintergrundfarben zu machen,
            werden sie für Vordergrundfarben etwas abgedunkelt und für Hintergrund-
            Farben aufgehellt, so dass man alle Texte auf so einem Hintergrund noch
            gut lesen kann.

            Für diese Farb-Modifikation habe ich zwei unterschiedliche Vorgehensweisen,
            einmal eine 'additive' und einmal eine 'multiplikative'.

            Bei der additiven Farb-Modifikation wird zu jedem der drei Farbwerte (also
            für Rot, Grün und Blau) das festgelegte Delta addiert (für die Vordergrund-
            farben ist das Delta negativ, damit die Farben dunkler werden, für die
            Hintergrundfarben ist es positiv, damit die Farben heller werden).
            Dabei werden alle Werte unter 0 auf 0 und alle Werte über 255 auf 255 gesetzt.

            Bei der multiplikativen Farb-Modifikation wird für jeden der drei Farbwerte
            (also für Rot, Grün und Blau) das normale Intervall von [0, 255] auf
            [0, 255-|delta|] bzw. [0+|delta|, 255 skaliert, damit die Verhältnismäßig-
            keiten der Farben erhalten bleiben.
            """;


    /** Erzeugt die passenden Farben. */
    private final VocableColors vocableColors;

    /** Der Wert um den die Farben für eine Vordergrundfarbe abgedunkelt werden. */
    private int deltaForForegroundColor;

    /** Der Wert um den die Farben für eine Hintergrundfarbe aufgehellt werden. */
    private int deltaForBackgroundColor;

    /** Der Panel auf dem die Farben dargestellt werden. */
    private final JPanel panel;

    private final JCheckBox multiplicativeColorModificationCheckBox;
    private final StringSelection deltaForForegroundColorSelection;
    private final StringSelection deltaForBackgroundColorSelection;

    /** Die Anzeige-Elemente mit den Farben. */
    private List<OneOfElevenColorUi> colorUis;

    /**
     * Konstruktor.
     *
     * @param vocableColors
     *            Erzeugt die passenden Farben.
     */
    public ElevenColorsOptionsUi(VocableColors vocableColors) {
        this.vocableColors = vocableColors;

        panel = new JPanel();
        multiplicativeColorModificationCheckBox = new JCheckBox("Die multiplikative "
                + "Farb-Modifikation wird (statt der additiven) verwendet.");
        deltaForForegroundColorSelection = new StringSelection("Delta für die Modifikation von "
                + "Vordergrundfarben (zwischen -255 und 0). Je kleiner desto blasser.");
        deltaForBackgroundColorSelection = new StringSelection("Delta für die Modifikation von "
                + "Hintergrundfarben (zwischen 0 und 255). Je größer desto blasser.");
    }

    /** Initialisiert das Element mit den Farb-Einstellungen aus den Optionen. */
    public void initWithOptions(Options options) {
        /*
         * Diese beiden Werte werden gespeichert, falls der Benutzer unverständliches oder falsches
         * eingibt:
         */
        deltaForForegroundColor = options.getDeltaForForegroundColor();
        deltaForBackgroundColor = options.getDeltaForBackgroundColor();

        deltaForForegroundColorSelection.setText(Integer.toString(deltaForForegroundColor));
        deltaForBackgroundColorSelection.setText(Integer.toString(deltaForBackgroundColor));

        multiplicativeColorModificationCheckBox.setSelected(
                ColorModificationType.MULTIPLICATIVE == options.getColorModificationType());

        initWithColors(options.getColors());
    }

    private void initWithColors(List<Color> colors) {
        initColorUis(colors);
        buildPanel();

        panel.validate();
        panel.invalidate();
        panel.repaint();
    }

    private void initColorUis(List<Color> colors) {
        if (colors.size() != VocableColors.NUMBER_OF_COLORS) {
            throw new RuntimeException("Es gibt nicht wie erwartet "
                    + VocableColors.NUMBER_OF_COLORS + " Farben, sondern " + colors.size() + ".");
        }

        colorUis = new ArrayList<>();
        int correct = 0;
        for (Color color : colors) {
            String title = correct + " / 10 richtig beantworete Abfragen";
            ++correct;
            OneOfElevenColorUi colorUi = new OneOfElevenColorUi(title, color, vocableColors);
            colorUis.add(colorUi);
        }
    }

    private void buildPanel() {
        panel.removeAll();
        panel.setLayout(new BorderLayout());

        panel.add(createColorOptionsPart(), BorderLayout.NORTH);
        panel.add(createColorsPart(), BorderLayout.CENTER);
        panel.add(createButtonPart(), BorderLayout.SOUTH);
    }

    private Component createColorOptionsPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new VerticalLayout(3, VerticalLayout.BOTH));

        panel.add(createExplanationButton());
        panel.add(deltaForForegroundColorSelection.getPanel());
        panel.add(deltaForBackgroundColorSelection.getPanel());
        panel.add(multiplicativeColorModificationCheckBox);

        return panel;
    }

    private Component createExplanationButton() {
        JButton button = new JButton("Erklärung zu den Farben und Modifikationen anzeigen");
        button.addActionListener(e -> showColorsAndModificationsExplanation());
        return button;
    }

    private void showColorsAndModificationsExplanation() {
        GuiTools.informUser("Erklärung zu den Farben und ihren Modifikationen",
                COLOR_MODIFICATION_EXPLANATION);
    }

    private Component createColorsPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new VerticalLayout(5, VerticalLayout.BOTH)); // 3

        for (OneOfElevenColorUi colorUi : colorUis) {
            panel.add(colorUi.getPanel());
        }

        return panel;
    }

    private Component createButtonPart() {
        JPanel panel = new JPanel();
        panel.setLayout(new VerticalLayout(3, VerticalLayout.BOTH));

        panel.add(createSetToSubduedColorsButton());
        panel.add(createSetToIntenseColorsButton());

        return panel;
    }

    private Component createSetToSubduedColorsButton() {
        JButton button = new JButton("Farben auf gedämpftere Werte setzen");
        button.addActionListener(e -> setToSubduedColors());
        return button;
    }

    private void setToSubduedColors() {
        initWithColors(DefaultColors.SUBDUED_COLORS);
    }

    private Component createSetToIntenseColorsButton() {
        JButton button = new JButton("Farben auf die intensive Werte setzen");
        button.addActionListener(e -> setToIntenseColors());
        return button;
    }

    private void setToIntenseColors() {
        initWithColors(DefaultColors.INTENSIVE_COLORS);
    }

    /** Gibt die vom Benutzer ggf. bearbeiteten Farben zurück. */
    public List<Color> getColors() {
        List<Color> colors = new ArrayList<>();

        for (OneOfElevenColorUi colorUi : colorUis) {
            Color color = colorUi.getColor();
            colors.add(color);
        }

        return colors;
    }

    /** Getter für den Wert um den die Farben für eine Vordergrundfarbe abgedunkelt werden. */
    public int getDeltaForForegroundColor() {
        String text = deltaForForegroundColorSelection.getTrimmedText();
        int delta = NumberString.parseIntIgnore(text, deltaForForegroundColor);
        if (delta > 0 || delta < -255) {
            return deltaForForegroundColor;
        }
        else {
            return delta;
        }
    }

    /** Getter für den Wert um den die Farben für eine Hintergrundfarbe aufgehellt werden. */
    public int getDeltaForBackgroundColor() {
        String text = deltaForBackgroundColorSelection.getTrimmedText();
        int delta = NumberString.parseIntIgnore(text, deltaForBackgroundColor);
        if (delta < 0 || delta > 255) {
            return deltaForBackgroundColor;
        }
        else {
            return delta;
        }
    }

    /**
     * Getter für die Art wie Farben für die Erzeugung von Vorder- bzw. Hintergrundfarben
     * modifiziert werden.
     */
    public ColorModificationType getColorModificationType() {
        if (multiplicativeColorModificationCheckBox.isSelected()) {
            return ColorModificationType.MULTIPLICATIVE;
        }
        else {
            return ColorModificationType.ADDITIVE;
        }
    }

    /** Getter für den Panel auf dem die Farben dargestellt werden. */
    public JPanel getPanel() {
        return panel;
    }

    /** Hinterlegt die Werte in den Optionen. */
    public void setIntoOptions(Options options) {
        options.setColors(getColors());
        options.setDeltaForForegroundColor(getDeltaForForegroundColor());
        options.setDeltaForBackgroundColor(getDeltaForBackgroundColor());
        options.setColorModificationType(getColorModificationType());

    }

}
