package de.duehl.basics.text;

/*
 * Copyright 2026 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import static org.junit.Assert.*;

import java.util.List;

import org.junit.Test;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.io.textfile.dictionary.Dictionary;
import de.duehl.basics.io.textfile.dictionary.DictionaryEntry;

public class SpellingErrorCorrectorTest {

    @Test
    public void correct01() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entry = new DictionaryEntry("foo");
        entry.addAlternative("bar");
        entry.addAlternative("baz");

        dictionary.addDictionaryEntry(entry);

        String text = "Dies ist ein Text mit foo, bar und baz.";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        String actual = corrector.correct(text);
        String expected = "Dies ist ein Text mit foo, foo und foo.";
        assertEquals(expected, actual);
    }

    @Test
    public void correct02() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entryFoo = new DictionaryEntry("foo");
        entryFoo.addAlternative("bar");
        entryFoo.addAlternative("baz");

        DictionaryEntry entryObst = new DictionaryEntry("Apfel");
        entryObst.addAlternative("Birne");
        entryObst.addAlternative("Zitrone");

        dictionary.addDictionaryEntry(entryFoo);
        dictionary.addDictionaryEntry(entryObst);

        String text = "Dies ist ein Text mit foo, bar und baz. "
                + "Auf dem Tisch liegen ein Apfel, eine Birne und einen Zitrone.";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        String actual = corrector.correct(text);
        String expected = "Dies ist ein Text mit foo, foo und foo. "
                + "Auf dem Tisch liegen ein Apfel, eine Apfel und einen Apfel.";
        assertEquals(expected, actual);
    }

    @Test
    public void correctList01() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entryFoo = new DictionaryEntry("foo");
        entryFoo.addAlternative("bar");
        entryFoo.addAlternative("baz");

        DictionaryEntry entryObst = new DictionaryEntry("Apfel");
        entryObst.addAlternative("Birne");
        entryObst.addAlternative("Zitrone");

        dictionary.addDictionaryEntry(entryFoo);
        dictionary.addDictionaryEntry(entryObst);

        List<String> texts = CollectionsHelper.buildListFrom(
                "Dies ist ein Text mit foo, bar und baz.",
                "Auf dem Tisch liegen ein Apfel, eine Birne und einen Zitrone.");
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        corrector.correct(texts);
        String expected1 = "Dies ist ein Text mit foo, foo und foo.";
        String expected2 = "Auf dem Tisch liegen ein Apfel, eine Apfel und einen Apfel.";
        assertEquals(expected1, texts.get(0));
        assertEquals(expected2, texts.get(1));
    }

    @Test
    public void correctionOfOverlappingDependsNotOnSortOrderInDictionary1() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entryObst = new DictionaryEntry("Apfel");
        entryObst.addAlternative("Birne");

        DictionaryEntry entryBaum = new DictionaryEntry("Kirschbaum");
        entryBaum.addAlternative("Apfelbaum");

        dictionary.addDictionaryEntry(entryObst);
        dictionary.addDictionaryEntry(entryBaum);

        String text = "Auf der Wiese steht ein Apfelbaum";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        String actual = corrector.correct(text);
        String expected = "Auf der Wiese steht ein Kirschbaum";
        assertEquals(expected, actual);
        // Das schlug vorher fehl, als nur in der Reihenfolge im Dictionary übersetzt wurde.
    }

    @Test
    public void correctionOfOverlappingDependsNotOnSortOrderInDictionary2() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entryBaum = new DictionaryEntry("Kirschbaum");
        entryBaum.addAlternative("Apfelbaum");

        DictionaryEntry entryObst = new DictionaryEntry("Apfel");
        entryObst.addAlternative("Birne");

        dictionary.addDictionaryEntry(entryBaum);
        dictionary.addDictionaryEntry(entryObst);

        String text = "Auf der Wiese steht ein Apfelbaum";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        String actual = corrector.correct(text);
        String expected = "Auf der Wiese steht ein Kirschbaum";
        assertEquals(expected, actual);
    }

    @Test
    public void getMispelledPhrases1() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entryFoo = new DictionaryEntry("foo");
        entryFoo.addAlternative("bar");
        entryFoo.addAlternative("baz");

        DictionaryEntry entryObst = new DictionaryEntry("Apfel");
        entryObst.addAlternative("Birne");
        entryObst.addAlternative("Zitrone");

        dictionary.addDictionaryEntry(entryFoo);
        dictionary.addDictionaryEntry(entryObst);

        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        List<String> mispelledPhrases = corrector.getMispelledPhrases();
        assertEquals(4, mispelledPhrases.size());
        assertEquals("Zitrone", mispelledPhrases.get(0));
        assertEquals("Birne", mispelledPhrases.get(1));
        assertEquals("bar", mispelledPhrases.get(2));
        assertEquals("baz", mispelledPhrases.get(3));
    }

    @Test
    public void getMispelledPhrases2() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entryObst = new DictionaryEntry("Apfel");
        entryObst.addAlternative("Birne");
        entryObst.addAlternative("Zitrone");

        DictionaryEntry entryFoo = new DictionaryEntry("foo");
        entryFoo.addAlternative("bar");
        entryFoo.addAlternative("baz");

        dictionary.addDictionaryEntry(entryObst);
        dictionary.addDictionaryEntry(entryFoo);

        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        List<String> mispelledPhrases = corrector.getMispelledPhrases();
        assertEquals(4, mispelledPhrases.size());
        assertEquals("Zitrone", mispelledPhrases.get(0));
        assertEquals("Birne", mispelledPhrases.get(1));
        assertEquals("bar", mispelledPhrases.get(2));
        assertEquals("baz", mispelledPhrases.get(3));
    }

    @Test (expected = RuntimeException.class)
    public void initializationErrorListWithoutMispelledPhrases() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entryObst = new DictionaryEntry("Apfel");
        entryObst.addAlternative("Birne");
        entryObst.addAlternative("Zitrone");

        DictionaryEntry entryFoo = new DictionaryEntry("foo");

        dictionary.addDictionaryEntry(entryObst);
        dictionary.addDictionaryEntry(entryFoo);

        new SpellingErrorCorrector(dictionary);
    }

    @Test /* (expected = RuntimeException.class)
            Das wirft nun keinen Fehler mehr, da das doppelte Einfügen bereits im DictionaryEntry
            verhindert wird.
          */
    public void initializationErrorListWithEqualMispelledWordInSameList() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entryObst = new DictionaryEntry("Apfel");
        entryObst.addAlternative("Birne");
        entryObst.addAlternative("Zitrone");

        DictionaryEntry entryFoo = new DictionaryEntry("foo");
        entryFoo.addAlternative("bar");
        entryFoo.addAlternative("bar"); // doppelt

        dictionary.addDictionaryEntry(entryObst);
        dictionary.addDictionaryEntry(entryFoo);

        new SpellingErrorCorrector(dictionary);
    }

    @Test (expected = RuntimeException.class)
    public void initializationErrorListWithEqualMispelledWordInDifferentList() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entryObst = new DictionaryEntry("Apfel");
        entryObst.addAlternative("Birne");
        entryObst.addAlternative("Zitrone");

        DictionaryEntry entryFoo1 = new DictionaryEntry("foo");
        entryFoo1.addAlternative("bar");

        DictionaryEntry entryFoo2 = new DictionaryEntry("baz");
        entryFoo2.addAlternative("bar");

        dictionary.addDictionaryEntry(entryObst);
        dictionary.addDictionaryEntry(entryFoo1);
        dictionary.addDictionaryEntry(entryFoo2);

        new SpellingErrorCorrector(dictionary);
    }

    @Test
    public void errorSearch() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry vorstandsvorsitzenderColonEntry = new DictionaryEntry(
                "Bestellt als Vorstandsvorsitzender:");
        vorstandsvorsitzenderColonEntry.addAlternative(
                "Bestellt als Vorstandsvorsitzende:");

        DictionaryEntry vorstandsvorsitzenderEntry = new DictionaryEntry(
                "Bestellt als Vorstandsvorsitzender");
        vorstandsvorsitzenderEntry.addAlternative(
                "Bestellt als 1. Vorstandsvorsitzender");
        vorstandsvorsitzenderEntry.addAlternative(
                "Bestellt als 1. Vorstandsvorsitzende");

        dictionary.addDictionaryEntry(vorstandsvorsitzenderColonEntry);
        dictionary.addDictionaryEntry(vorstandsvorsitzenderEntry);

        String text = "Bestellt als 1. Vorstandsvorsitzender:";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        String actual = corrector.correct(text);
        String expected = "Bestellt als Vorstandsvorsitzender:";
        assertEquals(expected, actual);
    }

    @Test
    public void correctLongThingsFirst() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entry1 = new DictionaryEntry("foo");
        entry1.addAlternative("Apfel");

        DictionaryEntry entry2 = new DictionaryEntry("bar");
        entry2.addAlternative("Apfelbaum");

        dictionary.addDictionaryEntry(entry1);
        dictionary.addDictionaryEntry(entry2);

        String text = "Auf der Wiese steht ein Apfelbaum.";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        String actual = corrector.correct(text);
        String expected = "Auf der Wiese steht ein bar.";
        assertEquals(expected, actual);
    }

    @Test
    public void doNotCorrectAlreadyCorrectedThings() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entry1 = new DictionaryEntry("Apfel");
        entry1.addAlternative("Birn");

        DictionaryEntry entry2 = new DictionaryEntry("Birnbaum");
        entry2.addAlternative("Apfelbaum");

        dictionary.addDictionaryEntry(entry1);
        dictionary.addDictionaryEntry(entry2);

        String text = "Auf der Wiese steht ein Apfelbaum.";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        String actual = corrector.correct(text);
        String expected = "Auf der Wiese steht ein Birnbaum.";
        assertEquals(expected, actual);
    }

    @Test
    public void correctMultiple() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entry = new DictionaryEntry("foo");
        entry.addAlternative("bar");
        entry.addAlternative("baz");

        dictionary.addDictionaryEntry(entry);

        String text = "Dies ist ein Text mit foo, bar und baz. Außerdem noch bar. Und baz.";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        String actual = corrector.correct(text);
        String expected = "Dies ist ein Text mit foo, foo und foo. Außerdem noch foo. Und foo.";
        assertEquals(expected, actual);
    }

    @Test
    public void correctReplaceLongTextsByShorts() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entry = new DictionaryEntry("foo");
        entry.addAlternative("Sehr sehr lange Phrase die bei der zweiten Ersetzung im Weg ist.");

        dictionary.addDictionaryEntry(entry);

        String text = "Dies ist ein Text mit zwei mal: 1. "
                + "Sehr sehr lange Phrase die bei der zweiten Ersetzung im Weg ist."
                + " und 2. "
                + "Sehr sehr lange Phrase die bei der zweiten Ersetzung im Weg ist.";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        String actual = corrector.correct(text);
        String expected = "Dies ist ein Text mit zwei mal: 1. "
                + "foo"
                + " und 2. "
                + "foo";
        assertEquals(expected, actual);
    }

    @Test
    public void setMaskCharacter() {
        Dictionary dictionary = new Dictionary();

        DictionaryEntry entry = new DictionaryEntry("bu");
        entry.addAlternative("ぷ");

        dictionary.addDictionaryEntry(entry);

        String text = "Dies ist ein Text mit ぷ.";
        SpellingErrorCorrector corrector = new SpellingErrorCorrector(dictionary);
        corrector.setMaskCharacter("#");
        String actual = corrector.correct(text);
        String expected = "Dies ist ein Text mit bu.";
        assertEquals(expected, actual);
    }

}