package de.duehl.basics.autodetect.tools;

/*
 * Copyright 2024 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 java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import de.duehl.basics.autodetect.AutoDetectionHelper;
import de.duehl.basics.text.NumberString;

/**
 * Diese Klasse ersetzt hintereinander stehende, gefundene Dinge wie Vornamen, Nachnamen, Titel.
 *
 * @version 1.01     2024-01-26
 * @author Christian Dühl
 */

public class EntityJoiner {

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

    /**
     * Kürzel das vorn vor der Position im Entitätenplatzhalter steht, z.B. 'func' in <<func:3>>.
     */
    private final String replacementFrontPart;


    /**
     * Das Suchpattern für zwei aufeinanderfolgende Begriffe in ent. Er wird erwartet, dass es im
     * Pattern drei Klammerausdrücke gibt, die erste die den zu ersetzenden Bereich kennzeichnet
     * und darin zwei weitere für die Positionen der beiden Begriffe. Dinge außerhalb des
     * gefangenen Bereichs werden in Ent auch wieder eingefügt.
     */
    private final Pattern pattern;

    /**
     * Liste in der die Begriffe stehen und in der der neue, zusammengefügte Begriff abgelegt wird.
     */
    private final List<String> things;

    /** Wird zum Auto-Kommentar hinzugefügt. */
    private final String autoCommentAdditional;

    /**
     * Hier entstehen die Kommentare zur automatischen Vergabe, sie werden später denen der
     * automatischen Bearbeitung hinzugefügt.
     */
    private final StringBuilder autoComment;

    /** Nummer der Klammer in der die beiden zu verbindenen Entitäten stehen. Defaultwert ist 1. */
    private int braceGroupNumber;

    /**
     * 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>> ersetzt.
     * @param replacementFrontPart
     *            Kürzel das vorn vor der Position im Entitätenplatzhalter steht, z.B. 'func' in
     *            <<func:3>>.
     * @param pattern
     *            Das Suchpattern für zwei aufeinanderfolgende Begriffe in ent. Er wird erwartet,
     *            dass es im Pattern vier Klammerausdrücke gibt, die erste die den zu ersetzenden
     *            Bereich kennzeichnet und darin drei weitere für die Positionen der beiden
     *            Begriffe sowie des dazwischen stehenden Verbinders.
     *            Beispiel:
     *                "(" + VN_REGEX + "(-)" + VN_REGEX + ")"
     *            Dinge außerhalb des gefangenen Bereichs werden in Ent auch wieder eingefügt.
     * @param things
     *            Liste in der die Begriffe stehen und in der der neue, zusammengefügte Begriff
     *            abgelegt wird.
     * @param autoCommentAdditional
     *            Wird zum Auto-Kommentar hinzugefügt.
     */
    public EntityJoiner(String ent, String replacementFrontPart, Pattern pattern, //String join,
            List<String> things, String autoCommentAdditional) {
        this.ent = ent;
        this.replacementFrontPart = replacementFrontPart;
        this.pattern = pattern;
        this.things = things;
        this.autoCommentAdditional = autoCommentAdditional;
        autoComment = new StringBuilder();
        braceGroupNumber = 1;
    }

    /**
     * Setter für die Nummer der Klammer in der die beiden zu verbindenen Entitäten stehen. Der
     * Defaultwert ist 1.
     *
     * Normalerweise sieht das übergebene Pattern so aus:
     *     "(" + VN_REGEX + "(-)" + VN_REGEX + ")"
     * Dann wird standardmäßig der Inhalt der ersten Klammer bearbeitet, ohne dass man diese
     * Methode aufruft.
     *
     * Allerdings wird diese Methode für Pattern wie
     *     VN_REGEX + " (" + NN_REGEX + "( )" + NN_REGEX + ")" + END_OR_PERSON_SEPARATOR_REGEX
     * benötigt, in denen der gewünschte zu fangende Ausdruck (NN_REGEX) nicht der erste ist.
     * Da wird dann 2 übergeben, damit der Klammerausdruck mit den Nachnamen bearbeitet wird.
     */
    public void setBraceGroupNumber(int braceGroupNumber) {
        this.braceGroupNumber = braceGroupNumber;
    }

    /** Führt die Ersetzung hintereinander stehender Dinge durch. */
    public void join() {
        boolean replacing = true;
        while (replacing) {
            replacing = false;
            Matcher matcher = pattern.matcher(ent);
            if (matcher.find()) {
                replacing = true;
                String beforeMatch = ent.substring(0, matcher.start());
                String afterMatch = ent.substring(matcher.end());
                String matchStart = ent.substring(matcher.start(), matcher.start(braceGroupNumber));
                String matchEnd = ent.substring(matcher.end(braceGroupNumber), matcher.end());

                String thingPosition1String = matcher.group(braceGroupNumber + 1);
                String between = matcher.group(braceGroupNumber + 2);
                String thingPosition2String = matcher.group(braceGroupNumber + 3);

                int thingPosition1 = NumberString.parseInt(thingPosition1String);
                int thingPosition2 = NumberString.parseInt(thingPosition2String);

                int thingIndex1 = thingPosition1 - 1;
                int thingIndex2 = thingPosition2 - 1;

                String thing1 = things.get(thingIndex1);
                String thing2 = things.get(thingIndex2);

                String newThing = thing1 + between + thing2;

                things.set(thingIndex1, newThing);

                things.remove(thingIndex2); // -> repairHigherPositions

                String regex = "<<" + replacementFrontPart + ":(\\d+)>>";
                beforeMatch = EntityHelper.repairHigherPositions(beforeMatch, regex, thingPosition2);
                afterMatch = EntityHelper.repairHigherPositions(afterMatch, regex, thingPosition2);
                matchStart = EntityHelper.repairHigherPositions(matchStart, regex, thingPosition2);
                matchEnd = EntityHelper.repairHigherPositions(matchEnd, regex, thingPosition2);

                if (thingPosition1 > thingPosition2) {
                    --thingPosition1;
                }

                ent = beforeMatch + matchStart
                        + AutoDetectionHelper.createEntity(replacementFrontPart, thingPosition1)
                        + matchEnd + afterMatch;
                appendAutoComment(autoCommentAdditional);
            }
        }
    }

    /** Fügt etwas an den sich aufbauenden Kommentar zur automatischen Bearbeitung an. */
    private void appendAutoComment(String appendix) {
        autoComment.append(" ").append(appendix);
    }

    /**
     * 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;
    }

    /** Gibt den Inhalt des Auto-Kommentars zurück. */
    public final String getAutoComment() {
        return autoComment.toString();
    }

    /**
     * Erzeugt den Standard-Kommentar, der zum Auto-Kommentar hinzugefügt wird.
     *
     * @param replacementFrontPart
     *            Kürzel das vorn vor der Position im Entitätenplatzhalter steht, z.B. 'func' in
     *            <<func:3>>.
     * @param join
     *            Leerzeichen oder Bindestrich zum Zusammenfügen der beiden Begriffe.
     * @return Den zu ergänzenden Kommentar für den Auto-Kommentar, so etwas wie "[func func
     *         joined]" oder "[vn-vn joined]".
     */
    public static String createStandardAdditionalComment(String replacementFrontPart, String join) {
        return "[" + replacementFrontPart + join + replacementFrontPart + " joined]";
    }

}
