package de.duehl.vocabulary.japanese.logic.symbol.kana.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import de.duehl.basics.text.Text;
import de.duehl.math.stochastic.RandomSample;
import de.duehl.math.stochastic.RandomSampleWithoutPutBack;
import de.duehl.swing.ui.GuiTools;
import de.duehl.vocabulary.japanese.common.data.InternalAdditionalKanaData;
import de.duehl.vocabulary.japanese.data.FumikoDataStructures;
import de.duehl.vocabulary.japanese.data.symbol.Hiragana;
import de.duehl.vocabulary.japanese.logic.symbol.kana.internal.InternalKanaDataRequester;
import de.duehl.vocabulary.japanese.logic.symbol.kana.test.data.HiraganaForTestSelectionMethod;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;
import de.duehl.vocabulary.japanese.ui.dialog.kana.hiraganatest.HiraganaTestParameterChooser;
import de.duehl.vocabulary.japanese.ui.dialog.kana.hiraganatest.HiraganaTester;

/**
 * Diese Klasse lässt zunächst auswählen, ob einige oder alle Hiragana abgefragte werden sollen und
 * ob die Hiragana in der originalen Reihenfolge oder zufällig sortiert abgefragt werden sollen.
 *
 * Anschließend fragt es ein Hiragana nach dem anderen ab: Das Symbol wird angezeigt und der
 * Benutzer kann die Hepburn-Darstellung eingeben.
 *
 * Anhand der Hiragana-Enum-Objekten wird dann überprüft, ob die Daten korrekt eingegeben wurden
 * und im Dialog zur Bewertung der einen Abfrage werden alle Daten zu dem Hiragana angezeigt.
 *
 * @version 1.01     2025-11-20
 * @author Christian Dühl
 */

public class HiraganaTesterLogic {

    private static final boolean DEBUG = false;


    /** Die Datenstrukturen des Vokabeltrainers. */
    private final FumikoDataStructures dataStructures;

    /** Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers. */
    private final FumikoUiObjects uiObjects;

    /** Gibt an, ob man fortsetzen soll. */
    private boolean goAhead;

    /** Die Liste an Hiragana, aus denen ausgewählt wird. */
    private List<Hiragana> hiraganaList;

    /** Die Anzahl der abzufragenden Hiragana. */
    private int numberOfHiraganaToTest;

    /** Gibt an ob die abzufragenden Hiragana in der originalen Reihenfolge belassen werden sollen. */
    private boolean useOriginalHiraganaOrder;

    /** Die Liste der abzufragenden Hiragana. */
    private List<Hiragana> hiraganaToTest;

    /** Die Art wie Hiragana für die Abfrage ausgewählt werden. */
    private HiraganaForTestSelectionMethod selectionMethod;

    /** Die Liste der nicht zuletzt zehn mal richtig abgefragten Hiragana. */
    private List<Hiragana> notTenTimesGoodTestedHiraganaList;

    /**
     * Konstruktor.
     *
     * @param dataStructures
     *            Die Datenstrukturen des Vokabeltrainers.
     * @param uiObjects
     *            Die häufig verwendeten Funktionen der grafischen Oberfläche des Vokabeltrainers.
     */
    public HiraganaTesterLogic(FumikoDataStructures dataStructures, FumikoUiObjects uiObjects) {
        this.dataStructures = dataStructures;
        this.uiObjects = uiObjects;
    }

    /** Führt den Test durch. */
    public void test() {
        init();

        if (goAhead) askTestParameters();
        if (goAhead) determineHiraganaToTest();
        if (goAhead) startRealTest();
    }

    private void init() {
        goAhead = true;
        determineNotTenTimesGoodTestedHiraganaList();
    }

    private void determineNotTenTimesGoodTestedHiraganaList() {
        notTenTimesGoodTestedHiraganaList = new ArrayList<>();

        for (Hiragana hiragana : Hiragana.values()) {
            InternalKanaDataRequester requester = dataStructures.getInternalKanaDataRequester();
            InternalAdditionalKanaData data = requester.getInternalDataForHiragana(hiragana);
            if (data.getLastCorrectTestsCount() < 10) {
                notTenTimesGoodTestedHiraganaList.add(hiragana);
            }
        }
    }

    private void askTestParameters() {
        HiraganaTestParameterChooser chooser = new HiraganaTestParameterChooser(
                notTenTimesGoodTestedHiraganaList, uiObjects.getGuiLocation(),
                uiObjects.getProgramImage());
        chooser.setVisible(true);
        if (chooser.isApplied()) {
            hiraganaList = chooser.getHiraganaList();
            numberOfHiraganaToTest = chooser.getNumberOfHiraganaToTest();
            useOriginalHiraganaOrder = chooser.isUseOriginalHiraganaOrder();
            selectionMethod = chooser.getSelectionMethod();
            if (DEBUG) Text.say("numberOfHiraganaToTest  = " + numberOfHiraganaToTest);
            if (DEBUG) Text.say("sortHiraganaToTest      = " + useOriginalHiraganaOrder);

            if (hiraganaList.isEmpty()) {
                String title = "Leer Menge an Hiragana ausgewählt";
                String message = "Sie haben eine leere Menge an abzufragenden Hiragana ausgewählt, "
                        + "daher ist nichts abzufragen.";
                GuiTools.informUser(title, message);
                goAhead = false;
            }
        }
        else {
            goAhead = false;
        }
    }

    private void determineHiraganaToTest() {
        switch (selectionMethod) {
            case RANDOM_BY_NUMBER:
                determineHiraganaToTestByRandomNumber();
                return;
            case ALL:
                useAllHiragana();
                return;
            case LAST_N:
                useLastNHiragana();
                return;
            case NOT_TEN_TIMES_GOOD_TESTED:
                useNotTenTimesGoodTestedHiraganaList();
                return;
            case UNKNOWN:
            default:
                goAhead = false;
        }
    }

    private void determineHiraganaToTestByRandomNumber() {
        List<Integer> hiraganaPositionsSample = createHiraganaPositionSample();
        fillHiraganaToTestWithHiraganaPositionSample(hiraganaPositionsSample);
    }

    /**
     * Erstellt eine zufällige Menge von Positionen zwischen 1 und der Anzahl der bekannten Hiragana
     * ohne zurücklegen.
     *
     * Falls die originale Reihenfolge benutzt werden soll, werden die Positionen sortiert.
     */
    private List<Integer> createHiraganaPositionSample() {
        int from = 1;
        int to = hiraganaList.size();
        int sampleSize = Math.min(hiraganaList.size(), numberOfHiraganaToTest);

        RandomSample randomSample = new RandomSampleWithoutPutBack(from, to, sampleSize);
        randomSample.drawSample();
        List<Integer> hiraganaPositionsSample = randomSample.getSample();

        if (useOriginalHiraganaOrder) {
            Collections.sort(hiraganaPositionsSample);
        }

        if (DEBUG) Text.say("hiraganaPositionsSample = " + hiraganaPositionsSample);

        return hiraganaPositionsSample;
    }

    /**
     * Erstellt aus der Positionen zwischen 1 und der Anzahl der bekannten Hiragana die entsprechende
     * Liste mit den Hiragana.
     *
     * @param hiraganaPositionsSample
     *            Liste mit den Positionen zwischen 1 und der Anzahl der bekannten Hiragana.
     */
    private void fillHiraganaToTestWithHiraganaPositionSample(List<Integer> hiraganaPositionsSample) {
        hiraganaToTest = new ArrayList<>();

        for (int hiraganaPosition : hiraganaPositionsSample) {
            int hiraganaIndex = hiraganaPosition - 1;
            Hiragana hiragana = hiraganaList.get(hiraganaIndex);
            hiraganaToTest.add(hiragana);
        }
    }

    private void useAllHiragana() {
        hiraganaToTest = new ArrayList<>();
        hiraganaToTest.addAll(hiraganaList);

        if (!useOriginalHiraganaOrder) {
            Collections.shuffle(hiraganaToTest);
        }
    }

    private void useLastNHiragana() {
        hiraganaToTest = new ArrayList<>();

        int size = hiraganaList.size();

        int startIndex = Math.max(0, size - numberOfHiraganaToTest);

        for (int index = startIndex; index <= size - 1; ++index) {
            Hiragana hiragana = hiraganaList.get(index);
            hiraganaToTest.add(hiragana);
        }

        if (!useOriginalHiraganaOrder) {
            Collections.shuffle(hiraganaToTest);
        }
    }

    private void useNotTenTimesGoodTestedHiraganaList() {
        hiraganaToTest = new ArrayList<>();
        hiraganaToTest.addAll(notTenTimesGoodTestedHiraganaList);

        if (!useOriginalHiraganaOrder) {
            Collections.shuffle(hiraganaToTest);
        }
    }

    private void startRealTest() {
        HiraganaTester tester = new HiraganaTester(hiraganaToTest, dataStructures, uiObjects);
        tester.setVisible(true);
    }

}
