/*
    SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com>

    SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef BTTORRENTINTERFACE_H
#define BTTORRENTINTERFACE_H

#include <QPointer>
#include <QSharedPointer>
#include <QUrl>

#include <interfaces/trackerslist.h>
#include <ktorrent_export.h>
#include <torrent/torrentfilestream.h>
#include <torrent/torrentstats.h>
#include <util/constants.h>

#include <memory>

#ifdef ERROR
#undef ERROR
#endif
namespace bt
{
class Job;
class BitSet;
class SHA1Hash;
class WaitJob;
class PeerID;
class MonitorInterface;
class TorrentFileInterface;
class PeerSource;
class SHA1Hash;
class WebSeedInterface;
class JobQueue;
class ChunkSelectorInterface;

/*!
 * \enum TorrentStartResponse
 *
 * The response when attempting to start a torrent. The value will describe the reason if the torrent failed to start.
 *
 * TODO move to the KTorrent app as these are not used in libktorrent.
 *
 * \var START_OK
 * The torrent started okay.
 *
 * \var USER_CANCELED
 * This value is currently unused.
 *
 * \var NOT_ENOUGH_DISKSPACE
 * There is not enough disk space to complete this torrent.
 *
 * \var MAX_SHARE_RATIO_REACHED
 * The torrent is completed and has reached the maximum share ratio, therefore it will not continue seeding.
 *
 * \var BUSY_WITH_JOB
 * The torrent is currently busy running a job (e.g. moving files or checking data) but will start later.
 *
 * \var QM_LIMITS_REACHED
 * This value is currently unused.
 */
enum TorrentStartResponse {
    START_OK,
    USER_CANCELED,
    NOT_ENOUGH_DISKSPACE,
    MAX_SHARE_RATIO_REACHED,
    BUSY_WITH_JOB,
    QM_LIMITS_REACHED, // Max seeds or downloads reached
};

/*!
 * \enum AutoStopReason
 *
 * The reason why a torrent has stopped itself.
 *
 * \var MAX_RATIO_REACHED
 * The torrent has reached the maximum configured share ratio.
 *
 * \var MAX_SEED_TIME_REACHED
 * The torrent has reached the maximum configured seed duration.
 */
enum AutoStopReason {
    MAX_RATIO_REACHED,
    MAX_SEED_TIME_REACHED,
};

/*!
 * \headerfile interfaces/torrentinterface.h
 * \brief A DHT node.
 */
struct DHTNode {
    QString ip;
    bt::Uint16 port;
};

/*!
 * \enum TorrentFeature
 *
 * The list of extensions that a torrent can support.
 *
 * \var DHT_FEATURE
 * The DHT extension.
 *
 * \var UT_PEX_FEATURE
 * The uTorrent Peer Exchange extension.
 */
enum TorrentFeature {
    DHT_FEATURE,
    UT_PEX_FEATURE,
};

/*!
 * \headerfile interfaces/torrentinterface.h
 * \author Joris Guisson
 * \brief Interface for an object which controls one torrent.
 *
 * This class is the interface for an object which controls the
 * up- and download of one torrent.
 */
class KTORRENT_EXPORT TorrentInterface : public QObject
{
    Q_OBJECT
public:
    TorrentInterface();
    ~TorrentInterface() override;

    //! Set the URL which the torrent was loaded from
    void setLoadUrl(const QUrl &u)
    {
        url = u;
    }

    //! Get the URL which the torrent was loaded from
    [[nodiscard]] QUrl loadUrl() const
    {
        return url;
    }

    /*!
     * Update the object, should be called periodically.
     */
    virtual void update() = 0;

    /*!
     * Pause the torrent.
     */
    virtual void pause() = 0;

    /*!
     * Unpause the torrent.
     */
    virtual void unpause() = 0;

    /*!
     * Start the download of the torrent.
     */
    virtual void start() = 0;

    /*!
     * Stop the download, closes all connections.
     * \param wjob WaitJob, used when KT is shutting down,
     * so that we can wait for all stopped events to reach the tracker
     */
    virtual void stop(bt::WaitJob *wjob = nullptr) = 0;

    /*!
     * Update the tracker, this should normally handled internally.
     * We leave it public so that the user can do a manual announce.
     */
    virtual void updateTracker() = 0;

    /*!
     * Scrape all or one tracker (private torrents)
     */
    virtual void scrapeTracker() = 0;

    //! Get the torrent's statistics
    [[nodiscard]] const TorrentStats &getStats() const
    {
        return stats;
    }

    /*!
     * Checks if torrent is multimedial and chunks needed for preview are downloaded
     * This only works for single file torrents
     * \return true if it is
     **/
    [[nodiscard]] virtual bool readyForPreview() const = 0;

    //! See if this is a single file torrent and a multimedia files
    [[nodiscard]] virtual bool isMultimedia() const = 0;

    /*!
     * Get the torX directory of this torrent. Temporary stuff like the index
     * file get stored there.
     */
    [[nodiscard]] virtual QString getTorDir() const = 0;

    //! Get the data directory of this torrent
    [[nodiscard]] virtual QString getDataDir() const = 0;

    /*!
     * Get the download running time of this torrent in seconds
     * \return Uint32 - time in seconds
     */
    [[nodiscard]] virtual Uint32 getRunningTimeDL() const = 0;

    /*!
     * Get the upload running time of this torrent in seconds
     * \return Uint32 - time in seconds
     */
    [[nodiscard]] virtual Uint32 getRunningTimeUL() const = 0;

    /*!
     * Change to a new torX dir. If this fails
     * we will fall back on the old directory.
     * \param new_dir The new directory
     * \return true upon success
     */
    virtual bool changeTorDir(const QString &new_dir) = 0;

    /*!
     * \enum ChangeOutputOption
     *
     * Options used with bt::TorrentInterface::changeOutputDir to specify how to move the output directory.
     *
     * \var NO_OPTIONS
     * Set the output directory without moving any files.
     *
     * \var MOVE_FILES
     * Move all downloaded or partially downloaded files to the new output directory. If none of the files have been downloaded then you can omit this option.
     *
     * \var FULL_PATH
     * The given path refers to the path of the torrent itself, rather than the parent directory. This option should be used for multi-file torrents to indicate
     * that the output directory is the top-level directory of the torrent. For single-file torrents this option should be omitted and the new output directory
     * is the parent directory of the file.
     */
    enum ChangeOutputOption {
        NO_OPTIONS = 0,
        MOVE_FILES = 1,
        FULL_PATH = 2,
    };
    Q_DECLARE_FLAGS(ChangeOutputOptions, ChangeOutputOption)

    /*!
     * Change to a new data dir. If this fails
     * we will fall back on the old directory.
     * \param new_dir The new directory
     * \param flags A combination of ChangeOutputOption
     * \return true upon success
     */
    virtual bool changeOutputDir(const QString &new_dir, ChangeOutputOptions flags = bt::TorrentInterface::NO_OPTIONS) = 0;

    /*!
     * Roll back the previous changeDataDir call.
     * Does nothing if there was no previous changeDataDir call.
     */
    virtual void rollback() = 0;

    /*!
     * Get a BitSet of the status of all Chunks
     */
    [[nodiscard]] virtual const bt::BitSet &downloadedChunksBitSet() const = 0;

    /*!
     * Get a BitSet of the availability of all Chunks
     */
    [[nodiscard]] virtual const bt::BitSet &availableChunksBitSet() const = 0;

    /*!
     * Get a BitSet of the excluded Chunks
     */
    [[nodiscard]] virtual const bt::BitSet &excludedChunksBitSet() const = 0;

    /*!
     * Get a bitset of only seed chunks
     */
    [[nodiscard]] virtual const bt::BitSet &onlySeedChunksBitSet() const = 0;

    //! Set the monitor
    virtual void setMonitor(MonitorInterface *tmo) = 0;

    //! Get the number of files in a multifile torrent (0 if we do not have a multifile torrent)
    [[nodiscard]] virtual Uint32 getNumFiles() const = 0;

    /*!
     * Get the index'th file of a multifile torrent
     * \param index The index
     * \return The TorrentFileInterface (isNull() will be true in case of error)
     */
    virtual TorrentFileInterface &getTorrentFile(Uint32 index) = 0;

    /*!
     * Const version of the previous one.
     * \param index The index of the file
     * \return The TorrentFileInterface (isNull() will be true in case of error)
     */
    [[nodiscard]] virtual const TorrentFileInterface &getTorrentFile(Uint32 index) const = 0;

    /*!
     * Move a torrent file to a new location.
     * \param files Map of files and their new location
     * \return true upon success
     */
    virtual bool moveTorrentFiles(const QMap<TorrentFileInterface *, QString> &files) = 0;

    /*!
        Create a TorrentFileStream object. If the torrent is destroyed this object must also be
        destroyed.
        \param index Index of the file (in case of a single file torrent, this does not matter)
        \param streaming_mode Set to true if this needs to be streamed
            (note that only one streaming_mode per torrent is allowed)
        \param parent Parent of the TorrentFileStream
        \return A TorrentFileStream or nullptr if index is not valid
    */
    virtual TorrentFileStream::Ptr createTorrentFileStream(bt::Uint32 index, bool streaming_mode, QObject *parent) = 0;

    //! Get a pointer to TrackersList object
    virtual TrackersList *getTrackersList() = 0;

    //! Get a pointer to TrackersList object
    [[nodiscard]] virtual const TrackersList *getTrackersList() const = 0;

    //! Get the torrent queue number. Zero if not in queue
    [[nodiscard]] virtual int getPriority() const = 0;

    //! Set the torrent queue number.
    virtual void setPriority(int p) = 0;

    //! Set the max share ratio
    virtual void setMaxShareRatio(float ratio) = 0;

    //! Get the max share ratio
    [[nodiscard]] virtual float getMaxShareRatio() const = 0;

    //! Set the max seed time in hours (0 is no limit)
    virtual void setMaxSeedTime(float hours) = 0;

    //! Get the max seed time
    [[nodiscard]] virtual float getMaxSeedTime() const = 0;

    //! Get the comments
    [[nodiscard]] virtual QString getComments() const = 0;

    //! Update the status
    virtual void updateStatus() = 0;

    //! Is manual announce allowed?
    virtual bool announceAllowed() = 0;

    /*!
     * Returns estimated time left for finishing download. Returned value is in seconds.
     * Uses TimeEstimator class to calculate this value.
     */
    virtual int getETA() = 0;

    /*!
     * Verify the correctness of all data. If from and to are not a valid range, the
     * entire torrent will be checked.
     * \param auto_import Is this an automatic import
     * \param from Chunk to start from
     * \param to Chunk to end with
     */
    virtual Job *startDataCheck(bool auto_import, bt::Uint32 from, bt::Uint32 to) = 0;

    /*!
     * Is storage mounted for this torrent
     * \param missing Filled with missing mount points (if any)
     * \return true if there are any missing, false otherwise
     **/
    virtual bool isStorageMounted(QStringList &missing) = 0;

    /*!
     * Test all files and see if they are not missing.
     * If so put them in a list
     */
    virtual bool hasMissingFiles(QStringList &sl) = 0;

    /*!
     * Recreate missing files.
     */
    virtual void recreateMissingFiles() = 0;

    /*!
     * Mark missing files as do not download.
     */
    virtual void dndMissingFiles() = 0;

    //! Get the number of initial DHT nodes
    [[nodiscard]] virtual Uint32 getNumDHTNodes() const = 0;

    //! Get a DHT node
    [[nodiscard]] virtual const DHTNode &getDHTNode(Uint32 i) const = 0;

    /*! Delete the data files of the torrent,
     * they will be lost permanently
     */
    virtual void deleteDataFiles() = 0;

    //! Checks if a seeding torrent has reached its maximum share ratio
    virtual bool overMaxRatio() = 0;

    //! Checks if a seeding torrent has reached it's max seed timery / will be ret
    virtual bool overMaxSeedTime() = 0;

    //! Handle an error
    virtual void handleError(const QString &err) = 0;

    //! Get the info_hash.
    [[nodiscard]] virtual const bt::SHA1Hash &getInfoHash() const = 0;

    /*!
     * Add a new PeerSource
     * \param ps
     */
    virtual void addPeerSource(PeerSource *ps) = 0;

    /*!
     * Remove a nPeerSource
     * \param ps
     */
    virtual void removePeerSource(PeerSource *ps) = 0;

    //! Is a feature enabled
    virtual bool isFeatureEnabled(TorrentFeature tf) = 0;

    //! Disable or enable a feature
    virtual void setFeatureEnabled(TorrentFeature tf, bool on) = 0;

    //! Get our PeerID
    [[nodiscard]] virtual const bt::PeerID &getOwnPeerID() const = 0;

    //! Set the traffic limits for this torrent
    virtual void setTrafficLimits(Uint32 up, Uint32 down) = 0;

    //! Get the traffic limits
    virtual void getTrafficLimits(Uint32 &up, Uint32 &down) = 0;

    //! Set the assured speeds
    virtual void setAssuredSpeeds(Uint32 up, Uint32 down) = 0;

    //! Get the assured speeds
    virtual void getAssuredSpeeds(Uint32 &up, Uint32 &down) = 0;

    //! Check if there is enough diskspace available for this torrent
    virtual bool checkDiskSpace(bool emit_sig = true) = 0;

    //! Get the number of webseeds
    [[nodiscard]] virtual Uint32 getNumWebSeeds() const = 0;

    //! Get a webseed (returns 0 if index is invalid)
    [[nodiscard]] virtual const WebSeedInterface *getWebSeed(Uint32 i) const = 0;

    //! Get a webseed (returns 0 if index is invalid)
    virtual WebSeedInterface *getWebSeed(Uint32 i) = 0;

    //! Add a webseed (return false, if there is already a webseed with the same url)
    virtual bool addWebSeed(const QUrl &url) = 0;

    //! Remove a webseed (only user created ones can be removed)
    virtual bool removeWebSeed(const QUrl &url) = 0;

    //! Mark all existing files as downloaded (
    virtual void markExistingFilesAsDownloaded() = 0;

    //! Set the user modified file or toplevel directory name
    virtual void setUserModifiedFileName(const QString &n)
    {
        user_modified_name = n;
    }

    //! Gets the user modified file or toplevel directory name
    [[nodiscard]] QString getUserModifiedFileName() const
    {
        return user_modified_name.isEmpty() ? stats.torrent_name : user_modified_name;
    }

    //! Set the displayed name
    virtual void setDisplayName(const QString &n) = 0;

    //! Gets the displayed name
    [[nodiscard]] QString getDisplayName() const
    {
        return display_name.isEmpty() ? stats.torrent_name : display_name;
    }

    //! Set whether the QM can start a torrent.
    virtual void setAllowedToStart(bool on) = 0;

    /*!
     * Can the torrent be started by the QM.
     * \return True if it can, false otherwise
     */
    [[nodiscard]] bool isAllowedToStart() const
    {
        return stats.qm_can_start;
    }

    /*!
     * Set whether the torrent is queued or not
     */
    virtual void setQueued(bool queued) = 0;

    //! Get the JobQueue of the torrent
    [[nodiscard]] virtual const JobQueue *getJobQueue() const = 0;

    //! Set the ChunkSelector to use (nullptr resets to the default ChunkSelector)
    virtual void setChunkSelector(std::unique_ptr<ChunkSelectorInterface> csel) = 0;

    //! After some network down time, the network is back up
    virtual void networkUp() = 0;

    /*!
     * Get the move upon completion directory.
     * \param dir the directory an empty url disables this feature
     */
    virtual void setMoveWhenCompletedDir(const QString &dir) = 0;

    /*!
     * Get the move upon completion directory.
     */
    [[nodiscard]] virtual QString getMoveWhenCompletedDir() const = 0;

    /*!
     * Enable or disable superseeding mode, does nothing if the torrent is not finished.
     */
    virtual void setSuperSeeding(bool on) = 0;

    using Ptr = QSharedPointer<TorrentInterface>;
    using WPtr = QPointer<TorrentInterface>;

Q_SIGNALS:
    /*!
     * Emitted when we have finished downloading.
     * \param me The object who emitted the signal
     */
    void finished(bt::TorrentInterface *me);

    /*!
     * Emitted when a Torrent download is stopped by error
     * \param me The object who emitted the signal
     * \param msg Error message
     */
    void stoppedByError(bt::TorrentInterface *me, QString msg);

    /*!
     * Emitted when maximum share ratio for this torrent is changed
     * \param me The object which emitted the signal.
     */
    void maxRatioChanged(bt::TorrentInterface *me);

    /*!
     * Emitted then torrent is stopped from seeding by KTorrent.
     * Happens when torrent has reached maximum share ratio and maybe we'll add something more...
     * \param me The object which emitted the signal.
     * \param reason The reason why it was aut stopped
     */
    void seedingAutoStopped(bt::TorrentInterface *me, bt::AutoStopReason reason);

    /*!
     * Emitted just before the torrent is started, this should be used to do some
     * checks on the files in the cache.
     * \param me The torrent which emitted the signal
     * \param ret The return value
     */
    void aboutToBeStarted(bt::TorrentInterface *me, bool &ret);

    /*!
     * Emitted when missing files have been marked as dnd.
     * The intention of this signal is to update the GUI.
     * \param me The torrent which emitted the signal
     */
    void missingFilesMarkedDND(bt::TorrentInterface *me);

    /*!
     * A corrupted chunk has been found during upload.
     * \param me The torrent which emitted the signal
     */
    void corruptedDataFound(bt::TorrentInterface *me);

    /*!
     * Disk is running out of space.
     * \param me The torrent which emitted the signal
     * \param toStop should this torrent be stopped or not
     */
    void diskSpaceLow(bt::TorrentInterface *me, bool toStop);

    /*!
     * Torrent has been stopped
     * \param me The torrent which emitted the signal
     */
    void torrentStopped(bt::TorrentInterface *me);

    /*!
     * Signal emitted when the torrent needs a data check
     * \param me The torrent
     * */
    void needDataCheck(bt::TorrentInterface *me);

    /*!
     * Emitted whenever the status of the torrent changes.
     * \param me the torrent
     */
    void statusChanged(bt::TorrentInterface *me);

    /*!
     * Emitted when a chunk is downloaded.
     * \param me The torrent
     * \param chunk The chunk
     */
    void chunkDownloaded(bt::TorrentInterface *me, bt::Uint32 chunk);

    /*!
     * Emitted when the torrent thinks the QM should update the queue
     */
    void updateQueue();

    /*!
     * Emitted when all running jobs are done.
     * \param me the torrent
     */
    void runningJobsDone(bt::TorrentInterface *me);

protected:
    TorrentStats stats;
    QString user_modified_name;
    QString display_name;
    QUrl url;
};

}

#endif
