package de.duehl.basics.datetime.date;

/*
 * Copyright 2022 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.autodetect.AutoDetectionHelper;
import de.duehl.basics.datetime.date.data.IndexAndDate;

import static de.duehl.basics.datetime.date.ImmutualDate.*;

/**
 * Diese Klasse sucht alle Vorkommen von Datumswerten in einem Text und ersetzt diese dort.
 *
 * @version 1.01     2022-11-23
 * @author Christian Dühl
 */

public class DateFinder {

    /**
     * Ein regulärer Ausdruck für ein Datum der Art "DD.MM.YYYY" oder auch "D.MM.YYYY", welcher das
     * Datum einfängt.
     */
    private static final String MATCHING_DATE_REGEX = "(" + ImmutualDate.DATE_REGEX + ")";

    /**
     * Ein Pattern für ein Datum der Art "DD.MM.YYYY" oder auch "D.MM.YYYY", welcher das
     * Datum einfängt.
     */
    private static final Pattern DATE_PATTERN = Pattern.compile(MATCHING_DATE_REGEX);

    /** Ein Pattern für ein Datum in der Form "DD.MM. / DD.MM.YYYY". */
    private static final Pattern COMPLICATED_DATE_PATTERN = Pattern.compile(COMPLICATED_DATE_REGEX);

    /**
     * Ein regulärer Ausdruck für ein Datum der Art "DD. Januar YYYY" oder auch "D. Januar YYYY",
     * welcher das Datum einfängt.
     */
    private static final String MATCHING_DATE_WITH_GERMAN_MONTH_NAMES_REGEX =
            "(" + ImmutualDate.DATE_WITH_GERMAN_MONTH_NAMES_REGEX + ")";

    /**
     * Ein Pattern für ein Datum der Art "DD. Januar YYYY" oder auch "D. Januar YYYY",
     * welcher das Datum einfängt.
     */
    private static final Pattern DATE_WITH_GERMAN_MONTH_NAMES_PATTERN = Pattern.compile(
            MATCHING_DATE_WITH_GERMAN_MONTH_NAMES_REGEX);

    /** Der vordere Teil des Platzhalters in <<date:1>>. */
    public static final String DATE_REPLACE_FRONT_PART = "date";

    /**
     * Ein regulärer Ausdruck zum Auffinden eines Platzhalters für ein Datum in der Form
     * <<date:1>>.
     */
    public static final String DATE_REPLACE_REGEX = "<<" + DATE_REPLACE_FRONT_PART + ":(\\d+)>>";

    /** Der Text, in dem Datumswerte gesucht und durch Platzhalter ersetzt werden sollen. */
    private String text;

    /**
     * Die Liste der gefundenen Datumswerte mit Index.
     *
     * Achtung, diese Indices passen durch die Ersetzungen aber weder zwingend zum Eingangs noch
     * zum zurückgelieferten Text!
     */
    private List<IndexAndDate> foundDates;

    /**
     * Die Liste der Datumswerte in der Reihenfolge der Ersetzungen. Die Position im Platzhalter
     * <<date:1>> ist der Index dieser Liste plus Eins.
     */
    private List<String> dates;

    /**
     * Konstruktor.
     *
     * @param text
     *            Der Text, in dem Datumswerte gesucht und durch Platzhalter ersetzt werden sollen.
     */
    public DateFinder(String text) {
        this.text = text;
    }

    /** Sucht im Text alle Datumswerte. */
    public void find() {
        init();
        findComplicatedDates();
        findNormalDates();
        findDatesWithGermanMonthNames();
    }

    private void init() {
        foundDates = new ArrayList<>();
        dates = new ArrayList<>();
    }

    private void findComplicatedDates() {
        Matcher complicatedDateMatcher = COMPLICATED_DATE_PATTERN.matcher(text);
        while (complicatedDateMatcher.find()) {
            int index = complicatedDateMatcher.start();
            String originalFoundDate = complicatedDateMatcher.group();
            String dateString1 = complicatedDateMatcher.group(1);
            String dateString2 = complicatedDateMatcher.group(2);
            String dateString = dateString1 + dateString2;
            ImmutualDate date = new ImmutualDate(dateString);
            dates.add(date.toString());
            int position = dates.size();
            replaceText(index, originalFoundDate, position);
            foundDates.add(new IndexAndDate(index, date));

            complicatedDateMatcher = COMPLICATED_DATE_PATTERN.matcher(text);
            // Sonst wird im unveränderten Text gesucht!
        }
    }

    private void findNormalDates() {
        Matcher dateMatcher = DATE_PATTERN.matcher(text);
        while (dateMatcher.find()) {
            int index = dateMatcher.start();
            String dateString = dateMatcher.group();
            ImmutualDate date = new ImmutualDate(dateString);
            dates.add(date.toString());
            int position = dates.size();
            replaceText(index, dateString, position);
            foundDates.add(new IndexAndDate(index, date));

            dateMatcher = DATE_PATTERN.matcher(text);
            /*
             * Sonst wird im unveränderten Text gesucht! Das fiel hier wegen der gleichen Längen
             * von 12.34.5678 und <<date:1>> nicht auf, wenn man maximal neun datumswerte findet.
             */
        }
    }

    private void findDatesWithGermanMonthNames() {
        Matcher dateWithGermanMonthNamesMatcher = DATE_WITH_GERMAN_MONTH_NAMES_PATTERN.matcher(text);
        while (dateWithGermanMonthNamesMatcher.find()) {
            int index = dateWithGermanMonthNamesMatcher.start();
            String dateString = dateWithGermanMonthNamesMatcher.group();
            ImmutualDate date = new ImmutualDate(dateString);
            dates.add(date.toString());
            int position = dates.size();
            replaceText(index, dateString, position);
            foundDates.add(new IndexAndDate(index, date));

            dateWithGermanMonthNamesMatcher = DATE_WITH_GERMAN_MONTH_NAMES_PATTERN.matcher(text);
            // Sonst wird im unveränderten Text gesucht!
        }
    }

    private void replaceText(int index, String originalFoundDate, int position) {
        String replacement = generateDateReplacement(position);
        int end = index + originalFoundDate.length();
        String front = text.substring(0, index);
        String rear = text.substring(end);
        text = front + replacement + rear;
    }

    /** Getter für den Text, in dem Datumswerte gesucht und durch Platzhalter ersetzt wurden. */
    public String getText() {
        return text;
    }

    /**
     * Getter für die Liste der gefundenen Datumswerte mit Index.
     *
     * Achtung, diese Indices passen durch die Ersetzungen aber weder zwingend zum Eingangs noch
     * zum zurückgelieferten Text!
     */
    List<IndexAndDate> getFoundDates() {
        return foundDates;
    }

    /**
     * Getter für die Liste der Datumswerte in der Reihenfolge der Ersetzungen. Die Position im
     * Platzhalter <<date:1>> ist der Index dieser Liste plus Eins.
     */
    public List<String> getDates() {
        return dates;
    }

    /**
     * Erzeugt zur übergebenen Position (Index der Liste + 1) einen Platzhalter der Form
     * <<date:1>>.
     */
    public static final String generateDateReplacement(int position) {
        return AutoDetectionHelper.createEntity(DATE_REPLACE_FRONT_PART, position);
    }

}
