package de.duehl.basics.autodetect.ent;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.text.NumberString;
import de.duehl.basics.text.Text;

/**
 * Diese Klasse ist die abstrakte Basis einer Klasse, welche einen Text (ent) bearbeitet.
 *
 * Es werden Kommentare zur Auffindung des Begriffs gesammelt.
 *
 * @version 1.01     2022-07-25
 * @author Christian Dühl
 */

public abstract class EntWorker extends AutoCommentAppender {

    public static final List<String> SEPARATORS = CollectionsHelper.buildListFrom(",", ".", "|",
            "<", ">", "(", ")", "[", "]", "{", "}", ":", ";");

    /**
     * Ursprünglich der rohe Text, dieser wird/wurde bei der Analyse aber verändert und Teile durch
     * die Entitäten wie <<street>> ersetzt.
     */
    protected String ent;

    /**
     * Konstruktor.
     *
     * @param ent
     *            Ursprünglich der rohe Text, dieser wird/wurde bei der Analyse aber verändert und
     *            Teile durch die Entitäten wie <<street>> oder <<street:2>> ersetzt.
     */
    public EntWorker(String ent) {
        super();
        this.ent = ent;
    }

    /**
     * Getter für den veränderten Text, in diesem wurden bei der Analyse Teile durch die Entitäten
     * wie <<street>> ersetzt.
     */
    public final String getEnt() {
        return ent;
    }

    /**
     * Setter für den ursprünglich rohen Text, dieser wird/wurde bei der Analyse aber verändert
     * und Teile durch die Entitäten wie <<street>> ersetzt.
     */
    public void setEnt(String ent) {
        this.ent = ent;
    }

    /** Stellt fest, ob ent dem übergebenen Pattern entspricht. */
    protected final boolean entMatches(Pattern pattern) {
        return entMatches(ent, pattern);
    }

    /** Stellt fest, ob ent dem übergebenen Pattern entspricht. */
    public static final boolean entMatches(String ent, Pattern pattern) {
        Matcher matcher = pattern.matcher(ent);
        return matcher.matches();
    }

    /**
     * Gibt an, ob der nicht bekannte Anteil in ent leer ist, also ob alles analysiert werden
     * konnte.
     */
    protected boolean isNotDetectedPartEmpty() {
        return isNotDetectedPartEmpty(ent);
    }

    /**
     * Gibt an, ob der nicht bekannte Anteil in ent leer ist, also ob alles analysiert werden
     * konnte.
     */
    public static boolean isNotDetectedPartEmpty(String ent) {
        String notDetected = determineNotDetectedPart(ent);
        return notDetected.isEmpty();
    }

    /** Gibt an, ob der nicht bekannte Anteil in ent leer oder "und" ist. */
    protected boolean isNotDetectedPartEmptyOrUnd() {
        return isNotDetectedPartEmptyOrUnd(ent);
    }

    /** Gibt an, ob der nicht bekannte Anteil in ent leer oder "und" ist. */
    public static boolean isNotDetectedPartEmptyOrUnd(String ent) {
        String notDetected = determineNotDetectedPart(ent);
        return notDetected.isEmpty()
                || "und".equals(notDetected)
                || ".".equals(notDetected) && ent.endsWith(".")
                ;
    }

    /** Gibt an, ob der nicht bekannte Anteil in ent leer oder "und" oder "&" ist. */
    protected boolean isNotDetectedPartEmptyOrUndOrAmp() {
        return isNotDetectedPartEmptyOrUndOrAmp(ent);
    }

    /** Gibt an, ob der nicht bekannte Anteil in ent leer oder "und" oder "&" ist. */
    public static boolean isNotDetectedPartEmptyOrUndOrAmp(String ent) {
        String notDetected = determineNotDetectedPart(ent);
        return notDetected.isEmpty()
                || "und".equals(notDetected)
                || "&".equals(notDetected)
                || ".".equals(notDetected) && ent.endsWith(".")
                ;
    }

    /** Gibt an, ob der nicht bekannte Anteil in ent ein Subjektiv ist. */
    protected boolean isNotDetectedPartSubstantive() {
        return isNotDetectedPartSubstantive(ent);
    }

    /** Gibt an, ob der nicht bekannte Anteil in ent ein Subjektiv ist. */
    public static boolean isNotDetectedPartSubstantive(String ent) {
        String notDetected = determineNotDetectedPart(ent);
        return Text.isSubstantive(notDetected);
    }

    /** Bestimmt den nicht bekannten Teil in ent. */
    protected String determineNotDetectedPart() {
        return determineNotDetectedPart(ent);
    }

    /** Bestimmt den nicht bekannten Teil in ent. */
    public static final String determineNotDetectedPart(String ent) {
        String notDetected = ent;
        notDetected = notDetected.replaceAll(">>.$", ">>");
        notDetected = notDetected.replaceAll("<<[^>]+>>", " ");
        for (String separator : SEPARATORS) {
            notDetected = notDetected.replace(separator, " ");
        }
        notDetected = notDetected.replace("-", " ");
        notDetected = Text.stripWhitespace(notDetected);
        return notDetected;
    }

    /** Erzeugt die Struktur der Anfänge der Platzhalter wie "vn", "nn" aus ent. */
    protected List<String> buildFoundStructure(List<String> replaceFrontParts) {
        return buildFoundStructure(ent, replaceFrontParts);
    }

    /** Erzeugt die Struktur der Anfänge der Platzhalter wie "vn", "nn" aus der Eingabe. */
    public static List<String> buildFoundStructure(String entLike, List<String> replaceFrontParts) {
        List<String> foundStructure = new ArrayList<>();

        int startIndex = 0;
        boolean searchOn = true;
        while (searchOn) {
            int index = entLike.indexOf("<<", startIndex);
            if (index == -1) {
                searchOn = false;
            }
            else {
                startIndex = index + 2;
                String after = entLike.substring(startIndex);
                for (String replaceFrontPart : replaceFrontParts) {
                    if (after.startsWith(replaceFrontPart + ":")) {
                        foundStructure.add(replaceFrontPart);
                    }
                }
            }
        }

        return foundStructure;
    }

    /** Erzeugt eine Liste der Positionen aus den Entitäten "<<vn:7>>" aus ent. */
    protected List<Integer> buildFoundPlaceholderIndicesList(List<String> replaceFrontParts) {
        return buildFoundPlaceholderIndicesList(ent, replaceFrontParts);
    }

    /** Erzeugt eine Liste der Positionen aus den Entitäten "<<vn:7>>" aus der Eingabe. */
    public static final List<Integer> buildFoundPlaceholderIndicesList(String entLike,
            List<String> replaceFrontParts) {
        List<Integer> placeholderIndices = new ArrayList<>();

        int startIndex = 0;
        boolean searchOn = true;
        while (searchOn) {
            int index = entLike.indexOf("<<", startIndex);
            if (index == -1) {
                searchOn = false;
            }
            else {
                startIndex = index + 2;
                String after = entLike.substring(startIndex);
                for (String replaceFrontPart : replaceFrontParts) {
                    if (after.startsWith(replaceFrontPart + ":")) {
                        after = after.substring(replaceFrontPart.length());
                        if (after.startsWith(":")) {
                            after = after.substring(1);
                            int number = NumberString.findNaturalNumberAtStart(after);
                            if (number > -1) {
                                placeholderIndices.add(number);
                            }
                        }
                    }
                }
            }
        }

        return placeholderIndices;
    }

    /**
     * Zählt sie oft ein ein Anfang eines Platzhalter wie "vn", "nn" in der erzeugten Struktur
     * dieser vorkommt, vgl. buildFoundStructure().
     */
    public static final int countInFoundStructure(List<String> foundStructure, String frontPart) {
        int count = 0;
        for (String frontPartInStructure : foundStructure) {
            if (frontPart.equals(frontPartInStructure)) {
                ++count;
            }
        }

        return count;
    }

    /**
     * Gibt den Index des ersten Vorkommens des übergebenen Anfangs eines Platzhalter wie "vn",
     * "nn" in der übergebenen Struktur zurück. Vgl. buildFoundStructure().
     */
    public static final int determineFirstIndexInFoundStructure(List<String> foundStructure,
            String frontPart) {
        for (int index = 0; index < foundStructure.size(); ++index) {
            String frontPartInStructure = foundStructure.get(index);
            if (frontPart.equals(frontPartInStructure)) {
                return index;
            }
        }

        return -1;
    }

}
