package de.duehl.html.download;

/*
 * Copyright 2017 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 de.duehl.basics.debug.Assure;
import de.duehl.basics.logging.Logger;
import de.duehl.basics.text.NumberString;
import de.duehl.html.download.data.RedirectHandling;
import de.duehl.html.download.logic.InternalDownloader;
import de.duehl.html.download.data.DownloadInfo;
import de.duehl.html.download.data.DownloadParameters;
import de.duehl.html.download.proxy.Proxy;
import de.duehl.html.download.proxy.RotatingProxies;

/**
 * Diese Klasse lädt Seiten aus dem WWW herunter. Hier wird sich nur um die Parameter gekümmert,
 * die eigentliche Arbeit findet dann im InternalDownloader statt.
 *
 * @version 1.01     2017-06-06
 * @author Christian Dühl
 */

public class Downloader {

    /** Die Parameter für den Download. */
    private final DownloadParameters parameters;

    /**
     * Konstruktor.
     *
     * @param website
     *            Die Webseite, die hier heruntergeladen werden soll.
     */
    public Downloader(String website) {
        Assure.notEmpty("Die URL 'website'", website);
        parameters = new DownloadParameters(website);
    }

    /** Übergibt einen Logger, mit dem dann die Arbeit hier geloggt wird. */
    public Downloader setLogger(Logger logger) {
        parameters.setLogger(logger);
        log("Verwende Logger"); //  oh wie erstaunlich!
        return this;
    }

    /** Setzt einen zu verwendenden Proxy für die Abfrage. */
    public Downloader useProxy(Proxy proxy) {
        parameters.setProxy(proxy);
        parameters.setUseProxy(true);
        parameters.setUseRotatingProxies(false);
        log("Verwende Proxy: " + proxy);
        return this;
    }

    /**
     * Übergibt eine Liste mit Proxies, die bei "Connection Refused" der Reihe nach durchprobiert
     * werden.
     *
     * @param proxies
     *            Liste mit den Proxies, durch die rotiert werden soll.
     */
    public Downloader useRotatingProxies(List<Proxy> proxies) {
        parameters.setRotatingProxies(new RotatingProxies(proxies));
        parameters.setUseProxy(true);
        parameters.setUseRotatingProxies(true);
        log("Verwende rotierende Proxies: " + proxies);
        return this;
    }

    /** Legt fest, dass eine laxe Strategie zum Umgang mit Redirects verwendet wird. */
    public Downloader useLaxRedirectHandling() {
        parameters.setRedirectHandling(RedirectHandling.LAX);
        log("Verwende laxe Strategie zum Umgang mit Redirects.");
        return this;
    }

    /** Legt fest, dass die Standard-Strategie zum Umgang mit Redirects verwendet wird. */
    public Downloader useStandardRedirectHandling() {
        parameters.setRedirectHandling(RedirectHandling.STANDARD);
        log("Verwende Standard-Strategie zum Umgang mit Redirects.");
        return this;
    }

    /** Legt fest, dass Redirects nicht gefolgt werden soll. */
    public Downloader disableRedirectHandling() {
        parameters.setRedirectHandling(RedirectHandling.NONE);
        log("Folge keinen Redirects.");
        return this;
    }

    /**
     * Legt fest, wie viele Versuche durchgeführt werden, um die Webseite herunterzuladen, und wie
     * lange zwischen diesen Versuchen geschlafen werden soll.
     *
     * @param maximumNumberOfTries
     *            Maximale zulässige Anzahl der Versuche, eine Seite herunter zu laden. Falls ein
     *            rotierender Proxy verwendet wird, ist dies die Anzahl der Versuche, die Seite mit
     *            jedem Proxy herunter zu laden.
     * @param secondsToSleep
     *            Schlafenszeit zwischen den Versuchen.
     */
    public Downloader multipleDownloadTries(int maximumNumberOfTries, long secondsToSleep) {
        Assure.isPositive(maximumNumberOfTries);
        Assure.isPositive(secondsToSleep);
        parameters.setMaximumNumberOfTries(maximumNumberOfTries);
        parameters.setSecondsToSleep(secondsToSleep);
        log("Verwende mehrere Versuche, die Webseite herunterzuladen. Anzahl Versuche : "
                + NumberString.taupu(maximumNumberOfTries) + ", Wartezeit zwischen Versuchen: "
                + NumberString.taupu(secondsToSleep) + " Sekunden.");
        return this;
    }

    /**
     * Beobachtet den Download und beendet ihn erfolglos nach der angegebenen Zeit, falls bis dahin
     * nichts heruntergeladen wurde.
     *
     * @param secondsBeforeKill
     *            Wenn secondsBeforeKill oder mehr Sekunden vergangen sind, wird der Task beendet.
     * @param millisecondsBetweenWatching
     *            Alle millisecondsBetweenWatching Millisekunden wird der Thread, in dem die
     *            Aufgabe läuft, auf Zeitüberschreitung überprüft.
     */
    public Downloader cancelAfter(int secondsBeforeKill, long millisecondsBetweenWatching) {
        parameters.setUseTimedThreadRunner(true);
        parameters.setSecondsBeforeKill(secondsBeforeKill);
        parameters.setMillisecondsBetweenWatching(millisecondsBetweenWatching);
        log("Der einzelne Download wird abgebrochen, wenn er zu lange braucht. "
                + "Sekunden bis zum Abbruch : " + NumberString.taupu(secondsBeforeKill)
                + ", Wartezeit zwischen den Überprüfungen: "
                + NumberString.taupu(millisecondsBetweenWatching) + " Millisekunden.");
        return this;
    }

    /**
     * Führt den eigentlichen Download durch.
     *
     * Bei rotierenden Proxies können das mehrere Versuche sein.
     *
     * Der Text wird im Encoding UTF-8 erzeugt.
     */
    public DownloadInfo download() {
        InternalDownloader downloader = new InternalDownloader(parameters);
        return downloader.download();
    }

    /** Loggt die übergebene Nachricht. */
    private void log(String message) {
        if (parameters.weHaveALogger()) {
            Logger logger = parameters.getLogger();
            logger.log(message, 1);
        }
    }

}

