package de.duehl.basics.text.html;

/*
 * Copyright 2018 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.awt.Desktop;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.debug.Assure;
import de.duehl.basics.text.Text;

/**
 * Diese Klasse stellt Hilfsmethoden für URLs zur Verfügung.
 *
 * @version 1.01     2018-03-07
 * @author Christian Dühl
 */

public class UrlHelper {

    private static final String HTTP = "http";

    /**
     * Ergänzt am Anfang einer URL "http://", falls in der gesamten URL kein "http" vorhanden ist.
     *
     * @param url
     *            Zu bearbeitende URL.
     * @return Ggf. ergänzte Url.
     */
    public static String addHttpToUrlIfMissing(String url) {
        if (!url.contains(HTTP)) {
            return HTTP + "://" + url;
        }
        else {
            return url;
        }
    }

    /**
     * Setzt einen Zusatz so an eine Url, dass genau ein / dazwischen steht.
     *
     * @param url
     *            Die Url (mit oder ohne / hinten dran).
     * @param part2
     *            An die Url anzuhängender Teil (mit oder ohne / davor).
     * @return Zusammengesetzte Url.
     */
    public static String concatUrlAndAdditional(String url, String additional) {
        boolean endsWithSlash = url.endsWith("/") || url.endsWith("\\");
        boolean startsWithSlash = additional.startsWith("/") || additional.startsWith("\\");

        if (endsWithSlash && startsWithSlash) {
            String tmp = url.substring(0, url.length() - 1);
            return tmp + additional;
        }
        else if (endsWithSlash || startsWithSlash) {
            return url + additional;
        }
        else {
            return url + "/" + additional;
        }
    }

    /**
     * Ermittelt, on ein Link in einer Liste von Links bereits vorhanden ist. Dafür wird die etwas
     * komplexere Definition von Gleichheit aus der Methode areLinksEqual() verwendet.
     *
     * @param url
     *            Zu prüfender Link.
     * @param urls
     *            Liste, die den Link eventuell schon enthält.
     * @return true genau dann, wenn der Link schon in der Liste ist.
     */
    public static boolean isLinkInLLinklist(String url, List<String> urls) {
        for (String linkFromList : urls) {
            if (UrlHelper.areLinksEqual(url, linkFromList)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Prüft, ob zwei Links "gleich" sind. Da in echten Links die Groß- oder Kleinschreibweise
     * keine Rolle spielt, in den Teilen hinter Fragezeichenparametern aber sehr wohl, werden im
     * Fall, das Fragezeichen vorhanden sind, die Teile vor den Fragezeichen in Kleinbuchstaben
     * gewandelt, bevor die Links miteinander vergleichen werden.
     *
     * @param url1
     *            Erster zu vergleichender Link.
     * @param url2
     *            Zweiter zu vergleichender Link.
     * @return true genau dann, wenn die Links "gleich" sind.
     */
    public static boolean areLinksEqual(String url1, String url2) {
        if (!url1.contains("?") || !url2.contains("?")) {
            String lowerUrl1 = url1.toLowerCase();
            String lowerUrl2 = url2.toLowerCase();
            return lowerUrl1.equals(lowerUrl2);
        }
        else {
            String lowerUrl1 = safelyLowerUrl(url1);
            String lowerUrl2 = safelyLowerUrl(url2);
            return lowerUrl1.equals(lowerUrl2);
        }
    }

    /** Wandelt eine Url so weit in Kleinbuchstaben um, wie es sicher ist. */
    static String safelyLowerUrl(String url) {
        if (url.contains("?")) {
            int index = url.indexOf("?");
            String front = url.substring(0, index);
            String rear = url.substring(index);
            return front.toLowerCase() + rear;
        }
        else {
            return url.toLowerCase();
        }
    }

    /** Normalisiert eine Url um Dinge wie "../" und "./" . */
    public static String normalizeUrl(String url) {
        String normalizedUrl = url;
        String lastStepUrl = "";

        while (!normalizedUrl.equals(lastStepUrl)) {
            lastStepUrl = normalizedUrl;
            normalizedUrl = normalizedUrl.replace("/../", "/");
            normalizedUrl = normalizedUrl.replace("/./", "/");
            //normalizedUrl = normalizedUrl.replace("./", "/");
        }

        return normalizedUrl;
    }

    /** Prüft, ob die angebenene Url mit einer Html-Seite endet. */
    public static boolean urlEndsWithHtml(String url) {
        return url.toLowerCase().endsWith(".html")
                || url.toLowerCase().endsWith(".htm");
    }

    /** Prüft, ob die angebenene Url mit einer Html-Seite endet. */
    public static boolean urlEndsWithHtmlOrPhp(String url) {
        return urlEndsWithHtml(url)
                || url.toLowerCase().endsWith(".php");
    }

    /**
     * Prüft, ob die angebenene Url eine andere Url enthält, unabhängig von der
     * Groß-/Kleinschreibung.
     */
    public static boolean urlContainsOtherUrl(String url, String containedUrl) {
        String lowerUrl = url.toLowerCase();
        String lowerContainedUrl = containedUrl.toLowerCase();
        return lowerUrl.contains(lowerContainedUrl);
    }

    /** Versucht die Basis zu einer Url zu ermitteln. */
    public static String determineBaseUrl(String url) {
        String lowerUrl = url.toLowerCase();
        String php = ".php";
        List<String> endings = CollectionsHelper.buildListFrom(".html", ".htm", php);
        int smallestFoundIndex = -1;
        for (String ending : endings) {
            int index = lowerUrl.indexOf(ending);
            if (index >= 0) {
                int urlLength = lowerUrl.length();
                int phpLength = php.length();
                if (ending.equals(php)
                        && urlLength > index + phpLength
                        && "?".equals(lowerUrl.substring(index + phpLength, index + phpLength + 1))) {
                    // in diesem Fall schneiden wir hier nicht hinter "bla.php?" ab!
                }
                else {
                    if (smallestFoundIndex == -1
                            || smallestFoundIndex > index) {
                        smallestFoundIndex = index;
                    }
                }
            }
        }
        if (smallestFoundIndex >= 0) {
            return cutToBaseUrl(url, smallestFoundIndex);
        }
        else {
            return url;
        }
    }

    private static String cutToBaseUrl(String url, int htmlIndex) {
        String base = url.substring(0, htmlIndex);
        int lastSlashIndex = base.lastIndexOf("/");
        if (lastSlashIndex == -1) {
            return url;
        }
        else {
            base = base.substring(0, lastSlashIndex + 1); // inclusive Slash
            return base;
        }
    }

    /** Startet den Link im Browser. */
    public static void openLinkInBrowser(String link) {
        URI uri;
        try {
            uri = new URI(link);
            Desktop.getDesktop().browse(uri);
        }
        catch (URISyntaxException e) {
            //e.printStackTrace();
        }
        catch (IOException e) {
            //e.printStackTrace();
        }
    }

    /** Entfernt vorn nacheinander "http://", "https://" und "www.". */
    public static String removeUrlStart(String website) {
        String url = website;

        if (url.startsWith("http://")) {
            url = url.substring("http://".length());
        }
        if (url.startsWith("https://")) {
            url = url.substring("https://".length());
        }
        if (url.startsWith("www.")) {
            url = url.substring("www.".length());
        }

        return url;
    }

    /**
     * Erstellt aus einer url einen String, den man zum Benennen einer Datei oder eines Verzeichnis
     * benutzen kann.
     */
    public static String url2dirname(String website) {
        String url = website;

        url = removeUrlStart(url);
        url = Text.removeTrailingSlash(url);
        url = url.replaceAll("[^-0-9A-Za-z .]", "_");
        url = url.replaceAll("__+", "_");

        return url;
    }

    /**
     * Prüft ob zwei Urls, wobei die eine die Url einer Seite ist, in der ein Frame in einem
     * Frameset vorkommt, dessen Url in der Hinsicht gleich genug sind, dass man vermutet, dass die
     * Url wirklich noch dicht genug an der Ausgangs-Url liegt, um nicht auf etwas völlig anderes zu
     * verweisen.
     *
     * symmetrisch, reflexiv, nicht notwendiger Weise transitiv, vielleicht aber doch.
     */
    public static boolean areUrlsEqualEnoughForFrames(String url1, String url2) {
        String cleanUrl1 = removeUrlStart(url1);
        String cleanUrl2 = removeUrlStart(url2);

        if (cleanUrl1.startsWith(cleanUrl2) || cleanUrl2.startsWith(cleanUrl1)) {
            return true;
        }

        List<String> equalParts = determineEqualParts(cleanUrl1, cleanUrl2);
        if (equalParts.isEmpty()) {
            return false;
        }

        CollectionsHelper.sortStringListByLengthDescanding(equalParts);
        String longestEqualPart = equalParts.get(0);

        int length1 = cleanUrl1.length();
        int length2 = cleanUrl2.length();
        int minLength = Math.min(length1, length2);

        int longestEqualLength = longestEqualPart.length();
        return longestEqualLength > 2                    // mindestens drei Zeichen lang und
                && longestEqualLength >= minLength / 2;  // mindestens die Hälfte der kürzeren,
                                                         // bereinigten Url
    }

    private static List<String> determineEqualParts(String url1, String url2) {
        List<String> equalParts = new ArrayList<>();

        for (int beginIndex = 0; beginIndex < url1.length() - 1; ++beginIndex) {
            for (int endIndex = url1.length(); endIndex > beginIndex; --endIndex) {
                String part = url1.substring(beginIndex, endIndex);
                Assure.notEmpty(part);
                if (url2.contains(part)) {
                    equalParts.add(part);
                    break;
                }
            }
        }

        return equalParts;
    }

    /** Erzeugt eine normalisierte und vorn um "http://", "https://" und "www." bereinigte Url. */
    public static String createCompareUrl(String url) {
        String compareUrl = removeUrlStart(url);
        compareUrl = normalizeUrl(compareUrl);
        return compareUrl;
    }

}
