package de.duehl.basics.replacements;

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

import de.duehl.basics.text.NumberString;

/**
 * Diese Klasse stellt Hilfsmethoden rund um vorgenommene Ersetzungen in einem Text, in dem
 * bestimmte Teile durch Platzhalter der Form <<begriff:1>> ersetzt wurden zur Verfügung.
 *
 * @version 1.01     2024-02-29
 * @author Christian Dühl
 */

public class ReplacementHelper {

    /**
     * Ermittelt aus einem Matcher, der an der übergebenen Gruppennummer die 1-basierte Position in
     * einem Platzhalter der Art <<begriff:1>>, also hier die "1", gefangen hat, aus der
     * zugehörigen Liste mit den Platzhaltern entsprechenden Werten den richtigen Wert.
     *
     * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     * Achtung, besser die Klasse PlaceholderResolver nutzen!
     * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     *
     * @param matcher
     *            Ein Matcher, der 1-basierte Positionen von Listenelementen gefangen hat.
     * @param groupNumber
     *            Die Nummer der Gruppe im Matcher, zu der wir den zugehörigen Wert erhalten
     *            wollen.
     * @param list
     *            Liste mit den zu den Positionen passenden Werten.
     * @return Wert an der Stelle (index = gefangene Position - 1) der Liste.
     */
    public static <T> T matcherGroupWithPositionToListElement(Matcher matcher, int groupNumber,
            List<T> list) {
        String positionString = matcher.group(groupNumber);
        int position = NumberString.parseInt(positionString);
        int index = position - 1;
        T element = list.get(index);
        return element;
    }

    /**
     * Findet im übergebenen Text den Index nach dem Ende des Platzhalters, dessen Anfangsindex
     * übergeben wird.
     *
     * @param text
     *            Text in dem gesucht werden soll.
     * @param placeholderStartIndex
     *            Startindex des Platzhalters, dessen Endindex gesucht werden soll.
     */
    public static int determinePlaceholderEndIndex(String text, int placeholderStartIndex) {
        int placeholderEndIndex = text.indexOf(">>", placeholderStartIndex);
        if (placeholderEndIndex == -1) {
            return -1;
        }
        else {
            return placeholderEndIndex + ">>".length();
        }
    }

    /** Sucht in einem Platzhalter der Art <<begriff:13>> die Position, im Beispiel 13. */
    public static int determinePlaceholderPosition(String placeholder) {
        int colonIndex = placeholder.indexOf(":");
        if (colonIndex == -1) {
            throw new RuntimeException("Keinen Doppelpunkt in einem Platzhalter der Art "
                    + "<<begriff:13>> gefunden.\n"
                    + "\t" + "placeholder : '" + placeholder + "'\n");
        }
        int closingIndex = placeholder.indexOf(">>");
        if (closingIndex == -1) {
            throw new RuntimeException("Kein >> in einem Platzhalter der Art "
                    + "<<begriff:13>> gefunden.\n"
                    + "\t" + "placeholder : '" + placeholder + "'\n");
        }
        if (closingIndex <= colonIndex) {
            throw new RuntimeException("In einem Platzhalter der Art <<begriff:13>> wurde >> vor "
                    + "dem Doppelpunkt gefunden.\n"
                    + "\t" + "placeholder : '" + placeholder + "'\n");
        }

        String positionString = placeholder.substring(colonIndex + 1, closingIndex);
        if (!NumberString.isDigitSequence(positionString)) {
            throw new RuntimeException("In einem Platzhalter der Art <<begriff:13>> wurde die "
                    + "Position falsch ermittelt.\n"
                    + "\t" + "placeholder   : '" + placeholder + "'\n"
                    + "\t" + "positionString: '" + positionString + "'\n");
        }
        return NumberString.parseInt(positionString);
    }

    /**
     * Sucht in einem Text zum End-Index eines Platzhalters der Form <<begriff:13>> im Text dessen
     * Anfangsposition im Text.
     *
     * @param text
     *            Text der den Platzhalter enthält.
     * @param placeholderEndIndex
     *            Index an dem der Platzhalter im Text endet.
     * @return Index an dem der Platzhalter im Text beginnt.
     */
    public static int searchStartIndexToEndIndexOfPlaceholder(String text,  int placeholderEndIndex) {
        String start = text.substring(0, placeholderEndIndex);
        if (!start.endsWith(">>")) {
            throw new RuntimeException("Der Text endet an der angegebenen Position nicht auf "
                    + "'>>'.\n"
                    + "\t" + "text    : '" + text + "'\n"
                    + "\t" + "endIndex: " + placeholderEndIndex + "\n");
        }

        return start.lastIndexOf("<<");
    }

    /**
     * Prüft ob der übergebene Text an der übergebenen Stelle mit einem Platzhalter der gewünschten
     * Art endet.
     */
    public static boolean endsTextWithPlaceholder(String text, int wantedIndex,
            String placeholderFrontPart) {
        String front = text.substring(0, wantedIndex);
        Pattern endsWithWantedPlaceholderPattern = Pattern.compile("<<" + placeholderFrontPart
                + ":\\d+>>$");
        Matcher matcher = endsWithWantedPlaceholderPattern.matcher(front);
        return matcher.find();
    }

    /**
     * Sucht in einem Platzhalter der Art <<begriff:13>> den vorderen Teil, im Beispiel "begriff".
     */
    public static String determinePlaceholderFrontPart(String placeholder) {
        int colonIndex = placeholder.indexOf(":");
        if (colonIndex == -1) {
            throw new RuntimeException("Keinen Doppelpunkt in einem Platzhalter der Art "
                    + "<<begriff:13>> gefunden.\n"
                    + "\t" + "placeholder : '" + placeholder + "'\n");
        }
        if (!placeholder.startsWith("<<")) {
            throw new RuntimeException("Der Platzhalter der Art <<begriff:13>> beginnt nicht mit "
                    + "'<<'.\n"
                    + "\t" + "placeholder : '" + placeholder + "'\n");
        }

        String frontPart = placeholder.substring("<<".length(), colonIndex);
        if (frontPart.isBlank()) {
            throw new RuntimeException("Der vordere Teil im Platzhalter der Art <<begriff:13>> ist "
                    + "leer.\n"
                    + "\t" + "placeholder : '" + placeholder + "'\n");
        }
        return frontPart;
    }

    /**
     * Zählt die Platzhalter (z.B. <<begriff:13>>) zum übergebenen vorderen Teil (z.B. "begriff").
     *
     * @param text
     *            Text in dem die Platzhalter gesucht werden.
     * @param placeholderReplaceFrontPart
     *            Der vordere Teil eines Platzhalters, z.B. "begriff" im, Platzhalter
     *            <<begriff:13>>.
     * @return Anzahl der Platzhalter mit dem übergebenen vorderen Teil im Text.
     */
    public static int countPlaceholders(String text, String placeholderReplaceFrontPart) {
        String placeholderRegex = "<<" + placeholderReplaceFrontPart + ":(\\d+)>>";
        Pattern placeholderPattern = Pattern.compile(placeholderRegex);
        return countPlaceholders(text, placeholderPattern);
    }

    /**
     * Zählt die Platzhalter (z.B. <<begriff:13>>) mit dem übergebenen Pattern.
     *
     * @param text
     *            Text in dem die Platzhalter gesucht werden.
     * @param placeholderPattern
     *            Pattern mit dem nach dem Platzhalter gesucht werden soll.
     * @return Anzahl der Platzhalter die dem Pattern entsprechen.
     */
    public static int countPlaceholders(String text, Pattern placeholderPattern) {
        int count = 0;

        Matcher matcher = placeholderPattern.matcher(text);
        while (matcher.find()) {
            ++count;
        }

        return count;
    }

    /**
     * Gibt die Position in den gewünschten Platzhaltern (z.B. bei <<begriff:13>> die 13) zurück.
     *
     * @param text
     *            Text in dem die Platzhalter gesucht werden.
     * @param placeholderPattern
     *            Pattern mit dem nach dem Platzhalter gesucht werden soll.
     *            Das Pattern muss in der ersten Gruppe die Position fangen und aus einem regulären
     *            Ausdruck der Form
     *                "<<begriff:(\\d+)>>"
     *            entstanden sein.
     * @return Eine Liste mit den Positionen aus den gesuchten Platzhaltern die vom Pattern
     *         gefunden werden.
     */
    public static List<Integer> getPositionsOfPlaceholders(String text,
            Pattern placeholderPattern) {
        List<Integer> positions = new ArrayList<>();

        Matcher matcher = placeholderPattern.matcher(text);
        while (matcher.find()) {
            int position = NumberString.parseIntIgnore(matcher.group(1), -1);
            positions.add(position);
        }

        return positions;
    }

}
