package de.duehl.basics.text.data;

/*
 * Copyright 2023 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.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

import de.duehl.basics.text.Text;

/**
 * Diese Klasse stellt einen Suchtext und die Stelle, an der er gefunden wurde, dar.
 *
 * @version 1.01     2023-02-01
 * @author Christian Dühl
 */

public class FoundSearch implements Comparable<FoundSearch> {

    /** Objekt zum Anzeigen, dass kein Treffer vorliegt. */
    public static final FoundSearch NOT_FOUND = new FoundSearch(-1, "SUCHBEGRIFF LIEGT NICHT VOR!");

    /** Der Index an dem der Suchbegriff in einem Text gefunden wurde. */
    private final int index;

    /** Der gefundene Suchbegriff. */
    private final String search;

    /**
     * Konstruktor.
     *
     * @param index
     *            Index an dem der Suchbegriff in einem Text gefunden wurde.
     * @param search
     *            Gefundener Suchbegriff.
     */
    public FoundSearch(int index, String search) {
        this.index = index;
        this.search = search;
    }

    /** Getter für den Index, an dem der Suchbegriff in einem Text gefunden wurde. */
    public int getIndex() {
        return index;
    }

    /** Getter für den gefundenen Suchbegriff. */
    public String getSearch() {
        return search;
    }

    /** Gibt den Endindex zurück (index + Länge des Suchbegriffs). */
    public int getEnd() {
        return index + search.length();
    }

    /** Gibt an, ob die Suche erfolgreich war. */
    public boolean wasSuccessfull() {
        return !this.equals(NOT_FOUND);
    }

    /** Gibt an, ob diese Fundstelle nach der anderen Fundstelle liegt. */
    public boolean isBehind(FoundSearch that) {
        return this.index > that.index;
    }

    /**
     * Gibt an, ob diese Fundstelle innerhalb der anderen Fundstelle liegt.
     *
     * Achtung, hierbei wird nicht geschaut, ob der eine Suchbegriff im anderen enthalten ist!
     */
    public boolean isIn(FoundSearch that) {
        return that.index <= this.index && that.getEnd() >= this.getEnd();
    }

    @Override
    public String toString() {
        return "FoundSearch [index=" + index + ", search=" + search + "]";
    }

    @Override
    public int compareTo(FoundSearch that) {
        return this.index - that.index;
    }

    @Override
    public int hashCode() {
        return Objects.hash(index, search);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        FoundSearch other = (FoundSearch) obj;
        return index == other.index && Objects.equals(search, other.search);
    }

    /**
     * Sortiert die übergebene Liste von Fundstellen nach dem Index an dem der Suchbegriff in einem
     * Text gefunden wurde.
     */
    public static void sortFoundSearchListByIndex(List<FoundSearch> foundSearches) {
        Collections.sort(foundSearches, new Comparator<FoundSearch>() {
            @Override
            public int compare(FoundSearch foundSearch1, FoundSearch foundSearch2) {
                int index1 = foundSearch1.getIndex();
                int index2 = foundSearch2.getIndex();
                return index1 - index2;
            }
        });
    }

    /**
     * Sortiert die übergebene Liste von Fundstellen rückwärts nach dem Endindex und dem Startindex
     * an dem der Suchbegriff in einem Text gefunden wurde.
     *
     * Das weiter rechts liegende Ende gewinnt. Bei gleichem Ende, gewinnt der weiter links
     * liegende Anfang.
     */
    public static void sortFoundSearchListReverseByEndAndIndex(List<FoundSearch> foundSearches) {
        Collections.sort(foundSearches, new Comparator<FoundSearch>() {
            @Override
            public int compare(FoundSearch foundSearch1, FoundSearch foundSearch2) {
                int end1 = foundSearch1.getEnd();
                int end2 = foundSearch2.getEnd();
                if (end1 != end2) {
                    return end2 - end1;
                }
                else {
                    int index1 = foundSearch1.getIndex();
                    int index2 = foundSearch2.getIndex();
                    return index1 - index2;
                }
            }
        });
    }

    /**
     * Sucht alle Vorkommen der Suchbegriffe im übergebenen Text.
     *
     * @param text
     *            Der zu durchsuchende Text.
     * @param searches
     *            Die im Text zu suchenden Suchbegriffe.
     * @return Liste der gefundenen Fundstellen. Achtung, falls sich die Suchbegriffe ineinander
     *         enthalten, muss man diese Liste danach noch bereinigen (vgl. CompanyNameFinder). Die
     *         Liste ist nicht sortiert. Dies lässt sich mit sortFoundSearchListByIndex()
     *         nachholen.
     */
    public static List<FoundSearch> findAll(String text, List<String> searches) {
        List<FoundSearch> foundSearches = new ArrayList<>();

        for (String search : searches) {
            List<Integer> indices = Text.findAllPositions(search, text);
            for (int index : indices) {
                FoundSearch foundSearch = new FoundSearch(index, search);
                foundSearches.add(foundSearch);
            }
        }

        return foundSearches;
    }

}
