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.Katakana;
import de.duehl.vocabulary.japanese.logic.symbol.kana.internal.InternalKanaDataRequester;
import de.duehl.vocabulary.japanese.logic.symbol.kana.test.data.KatakanaForTestSelectionMethod;
import de.duehl.vocabulary.japanese.ui.data.FumikoUiObjects;
import de.duehl.vocabulary.japanese.ui.dialog.kana.katakanatest.KatakanaTestParameterChooser;
import de.duehl.vocabulary.japanese.ui.dialog.kana.katakanatest.KatakanaTester;

/**
 * Diese Klasse lässt zunächst auswählen, ob einige oder alle Katakana abgefragte werden sollen und
 * ob die Katakana in der originalen Reihenfolge oder zufällig sortiert abgefragt werden sollen.
 *
 * Anschließend fragt es ein Katakana nach dem anderen ab: Das Symbol wird angezeigt und der
 * Benutzer kann die Hepburn-Darstellung eingeben.
 *
 * Anhand der Katakana-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 Katakana angezeigt.
 *
 * @version 1.01     2025-11-20
 * @author Christian Dühl
 */

public class KatakanaTesterLogic {

    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 Katakana, aus denen ausgewählt wird. */
    private List<Katakana> katakanaList;

    /** Die Anzahl der abzufragenden Katakana. */
    private int numberOfKatakanaToTest;

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

    /** Die Liste der abzufragenden Katakana. */
    private List<Katakana> katakanaToTest;

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

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

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

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

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

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

    private void determineNotTenTimesGoodTestedKatakanaList() {
        notTenTimesGoodTestedKatakanaList = new ArrayList<>();

        InternalKanaDataRequester requester = dataStructures.getInternalKanaDataRequester();
        for (Katakana katakana : Katakana.values()) {
            InternalAdditionalKanaData data = requester.getInternalDataForKatakana(katakana);
            if (data.getLastCorrectTestsCount() < 10) {
                notTenTimesGoodTestedKatakanaList.add(katakana);
            }
        }
    }

    private void askTestParameters() {
        KatakanaTestParameterChooser chooser = new KatakanaTestParameterChooser(
                notTenTimesGoodTestedKatakanaList, uiObjects.getGuiLocation(),
                uiObjects.getProgramImage());
        chooser.setVisible(true);
        if (chooser.isApplied()) {
            katakanaList = chooser.getKatakanaList();
            numberOfKatakanaToTest = chooser.getNumberOfKatakanaToTest();
            useOriginalKatakanaOrder = chooser.isUseOriginalKatakanaOrder();
            selectionMethod = chooser.getSelectionMethod();
            if (DEBUG) Text.say("numberOfKatakanaToTest  = " + numberOfKatakanaToTest);
            if (DEBUG) Text.say("sortKatakanaToTest      = " + useOriginalKatakanaOrder);

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

    private void determineKatakanaToTest() {
        switch (selectionMethod) {
            case RANDOM_BY_NUMBER:
                determineKatakanaToTestByRandomNumber();
                return;
            case ALL:
                useAllKatakana();
                return;
            case LAST_N:
                useLastNKatakana();
                return;
            case NOT_TEN_TIMES_GOOD_TESTED:
                useNotTenTimesGoodTestedKatakanaList();
                return;
            case UNKNOWN:
            default:
                goAhead = false;
        }
    }

    private void determineKatakanaToTestByRandomNumber() {
        List<Integer> katakanaPositionsSample = createKatakanaPositionSample();
        fillKatakanaToTestWithKatakanaPositionSample(katakanaPositionsSample);
    }

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

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

        if (useOriginalKatakanaOrder) {
            Collections.sort(katakanaPositionsSample);
        }

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

        return katakanaPositionsSample;
    }

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

        for (int katakanaPosition : katakanaPositionsSample) {
            int katakanaIndex = katakanaPosition - 1;
            Katakana katakana = katakanaList.get(katakanaIndex);
            katakanaToTest.add(katakana);
        }
    }

    private void useAllKatakana() {
        katakanaToTest = new ArrayList<>();
        katakanaToTest.addAll(katakanaList);

        if (!useOriginalKatakanaOrder) {
            Collections.shuffle(katakanaToTest);
        }
    }

    private void useLastNKatakana() {
        katakanaToTest = new ArrayList<>();

        int size = katakanaList.size();

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

        for (int index = startIndex; index <= size - 1; ++index) {
            Katakana katakana = katakanaList.get(index);
            katakanaToTest.add(katakana);
        }

        if (!useOriginalKatakanaOrder) {
            Collections.shuffle(katakanaToTest);
        }
    }

    private void useNotTenTimesGoodTestedKatakanaList() {
        katakanaToTest = new ArrayList<>();
        katakanaToTest.addAll(notTenTimesGoodTestedKatakanaList);

        if (!useOriginalKatakanaOrder) {
            Collections.shuffle(katakanaToTest);
        }
    }

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

}
