/***************************************************************************
 *   Copyright (C) 2008 by Joris Guisson and Ivan Vasic                    *
 *   joris.guisson@gmail.com                                               *
 *   ivasic@gmail.com                                                      *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
 ***************************************************************************/
#ifndef BTWEBSEED_H
#define BTWEBSEED_H

#include <QTimer>
#include <QUrl>
#include <diskio/piecedata.h>
#include <interfaces/chunkdownloadinterface.h>
#include <interfaces/webseedinterface.h>
#include <ktorrent_export.h>
#include <peer/connectionlimit.h>
#include <util/constants.h>

namespace bt
{
class Torrent;
class HttpConnection;
class ChunkManager;
class Chunk;
class WebSeedChunkDownload;

/**
    @author Joris Guisson
    Class which handles downloading from a webseed
*/
class KTORRENT_EXPORT WebSeed : public QObject, public WebSeedInterface
{
    Q_OBJECT
public:
    WebSeed(const QUrl &url, bool user, const Torrent &tor, ChunkManager &cman);
    ~WebSeed() override;

    /// Is this webseed busy ?
    bool busy() const;

    /// Check if a chunk lies in the current range we are downloading
    bool inCurrentRange(Uint32 chunk) const
    {
        return chunk >= first_chunk && chunk <= last_chunk;
    }

    /**
     * Download a range of chunks
     * @param first The first chunk
     * @param last The last chunk
     */
    void download(Uint32 first, Uint32 last);

    /**
     * A range has been excluded, if we are fully
     * downloading in this range, reset.
     * @param from Start of range
     * @param to End of range
     */
    void onExcluded(Uint32 from, Uint32 to);

    /**
     * Check if the connection has received some data and handle it.
     * @return The number of bytes downloaded
     */
    Uint32 update();

    /**
     * A chunk has been downloaded.
     * @param chunk The chunk
     */
    void chunkDownloaded(Uint32 chunk);

    /**
     * Cancel the current download and kill the connection
     */
    void cancel();

    /// Get the current download rate
    Uint32 getDownloadRate() const override;

    /**
     * Set the group ID's of the http connection (for speed limits)
     * @param up Upload group id
     * @param down Download group id
     */
    void setGroupIDs(Uint32 up, Uint32 down);

    /**
     * Set the proxy to use for all WebSeeds
     * @param host Hostname or IP address of the proxy
     * @param port Port number of the proxy
     */
    static void setProxy(const QString &host, bt::Uint16 port);

    /**
     * Whether or not to enable or disable the use of a proxy.
     * When the proxy is disabled, we will use the KDE proxy settings.
     * @param on On or not
     */
    static void setProxyEnabled(bool on);

    /// Get the current webseed download
    WebSeedChunkDownload *currentChunkDownload()
    {
        return current;
    }

    void setEnabled(bool on) override;

    /// Disable the webseed
    void disable(const QString &reason);

    /// Get the number of failed attempts
    Uint32 failedAttempts() const
    {
        return num_failures;
    }

public Q_SLOTS:
    /**
     * Reset the webseed (kills the connection)
     */
    void reset();

Q_SIGNALS:
    /**
     * Emitted when a chunk is downloaded
     * @param c The chunk
     */
    void chunkReady(Chunk *c);

    /**
     * Emitted when a range has been fully downloaded
     */
    void finished();

    /**
     * A ChunkDownload was started
     * @param cd The ChunkDownloadInterface
     * @param chunk The chunk which is being started
     */
    void chunkDownloadStarted(WebSeedChunkDownload *cd, Uint32 chunk);

    /**
     * A ChunkDownload was finished
     * @param cd The ChunkDownloadInterface
     * @param chunk The chunk which is being stopped
     */
    void chunkDownloadFinished(WebSeedChunkDownload *cd, Uint32 chunk);

private Q_SLOTS:
    void redirected(const QUrl &to_url);

private:
    struct Range {
        Uint32 file;
        Uint64 off;
        Uint64 len;
    };

    class AutoDisabled
    {
    }; // Exception

    void fillRangeList(Uint32 chunk);
    void handleData(const QByteArray &data);
    void chunkStarted(Uint32 chunk);
    void chunkStopped();
    void connectToServer();
    void continueCurChunk();
    void readData();
    void retryLater();

private:
    const Torrent &tor;
    ChunkManager &cman;
    HttpConnection *conn;
    QList<QByteArray> chunks;
    Uint32 first_chunk;
    Uint32 last_chunk;
    Uint32 cur_chunk;
    Uint32 bytes_of_cur_chunk;
    Uint32 num_failures;
    Uint32 downloaded;
    WebSeedChunkDownload *current;
    Uint32 up_gid, down_gid;
    QList<Range> range_queue;
    QUrl redirected_url;
    PieceData::Ptr cur_piece;
    QTimer retry_timer;
    ConnectionLimit::Token::Ptr token;

    static QString proxy_host;
    static Uint16 proxy_port;
    static bool proxy_enabled;
};

class WebSeedChunkDownload : public ChunkDownloadInterface
{
public:
    WebSeedChunkDownload(WebSeed *ws, const QString &url, Uint32 index, Uint32 total);
    ~WebSeedChunkDownload() override;

    void getStats(Stats &s) override;

    WebSeed *ws;
    QString url;
    Uint32 chunk;
    Uint32 total_pieces;
    Uint32 pieces_downloaded;
};

}

#endif
