/* This file is part of the KDE project

   Copyright (C) 2008 Manolo Valdes <nolis71cu@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.
*/


#ifndef TRANSFERDATASOURCE_H
#define TRANSFERDATASOURCE_H

#include "kget_export.h"
#include "transfer.h"

#include <QObject>

#include <KIO/Job>

/**
 * This Class is an interface for inter-plugins data change.
 * allowing to use already implemented features from others plugins
 */
class KGET_EXPORT TransferDataSource : public QObject
{
    Q_OBJECT
    public:
        TransferDataSource(const QUrl &srcUrl, QObject *parent);
        ~TransferDataSource() override;

        /**
         * @enum Error
         * @brief Error type enum
         */
        enum Error
        {
            Unknown,
            WrongDownloadSize,
            NotResumeable
        };

        /**
         * Returns the capabilities this TransferDataSource supports
         */
        Transfer::Capabilities capabilities() const;

        virtual void start() = 0;
        virtual void stop() = 0;

        /**
         * Tries to find the filesize if this capability is supported,
         * if successful it emits foundFileSize(TransferDataSource*,KIO::filesize_t,QPair<int,int>)
         * and assigns all segments to itself
         * if not successful it will try to download the file nevertheless
         * @note if stop is called and no size is found yet then this is aborted, i.e. needs to be
         * called again if start is later called
         * @param segmentSize the segments should have
         */
        virtual void findFileSize(KIO::fileoffset_t segmentSize);

        /**
         * Adds multiple continuous segments that should be downloaded by this TransferDataSource
         * @param segmentSize first is always the general segmentSize, second the segmentSize
         * of the last segment in the range. If just one (the last) segment was assigned, then
         * first would not equal second, this is to ensure that first can be used to calculate the offset
         * TransferDataSources have to handle all that internally.
         * @param segmentRange first the beginning, second the end
         */
        virtual void addSegments(const QPair<KIO::fileoffset_t, KIO::fileoffset_t> &segmentSize, const QPair<int, int> &segmentRange) = 0;

        /**
         * Removes one connection, useful when setMaximumParallelDownloads was called with a lower number
         * @return the segments that are removed (unassigned) now
         *
         */
        virtual QPair<int, int> removeConnection();

        QUrl sourceUrl() const {return m_sourceUrl;}//TODO

        /**
         * returns the current speed of this data source
         * @return the speed
         */
        ulong currentSpeed() const {return m_speed;}

        /**
         * Set the size the server used for downloading should report
         * @param supposedSize the size the file should have
         */
        virtual void setSupposedSize(KIO::filesize_t supposedSize) {m_supposedSize = supposedSize;}

        /**
         * Returns the assignedSegments to this TransferDataSource
         * Each connection is represented by a QPair, where the first int is the beginning
         * segment and the last the ending segment
         * @note an empty list is returned by default, the elements can also be (-1, -1)
         */
        virtual QList<QPair<int, int> > assignedSegments() const;

        /**
         * Returns the number of unfinished Segments of the connection with the most
         * unfinished segments
         * Each TransferDataSource can have multiple connections and each connection
         * can have multiple segments assigned
         * @note default implementation returns 0
         */
        virtual int countUnfinishedSegments() const;

        /**
         * If a connection of this TransferDataSource is assigned multiple (continuous) segments, then
         * this method will split them (the unfinished ones) in half, it returns the beginning
         * and the end of the now unassigned segments; (-1, -1) if there are none
         * @note if only one segment is assigned to a connection split will also return (-1, -1)
         */
        virtual QPair<int, int> split();//TODO should split also take the current running segment into account?


        //the following methods are used for managing the number of parallel connections
        //subclasses have to keep track of the currentSegments
        /**
         * @return the number of parallel segments this DataSource is allowed to use,
         * default is 1
         */
        virtual int parallelSegments() const;

        /**
         * Sets the number of parallel segments this DataSource is allowed to use
         */
        virtual void setParallelSegments(int parallelSegments);

        /**
         * @return the number of parallel segments this DataSources currently uses
         */
        virtual int currentSegments() const;

        /**
         * Returns the mismatch of parallelSegments() and currentSegments()
         * @return the number of segments to add/remove e.g. -1 means one segment to remove
         */
        virtual int changeNeeded() const;

    Q_SIGNALS:
        /**
         * Emitted after findFileSize is called successfully
         * @param source that found the filesize
         * @param fileSize that was found
         * @param segmentRange that was calculated based on the segmentSize and that was assigned to
         * source automatically
         */
        void foundFileSize(TransferDataSource *source, KIO::filesize_t fileSize, const QPair<int, int> &segmentRange);

        /**
         * Emitted when the capabilities of the TransferDataSource change
         */
        void capabilitiesChanged();

        /**
         * Emitted when the TransferDataSource finished the download on its own, e.g. when findFileSize
         * is being called but no fileSize is found and instead the download finishes
         * @param source the source that emitted this signal
         * @param fileSize the fileSize of the finished file (calculated by the downloaded bytes)
         */
        void finishedDownload(TransferDataSource *source, KIO::filesize_t fileSize);

        /**
         * Returns data in the forms of chucks
         * @note if the receiver set worked to wrong the TransferDataSource should cache the data
         * @param offset the offset in the file
         * @param data the downloaded data
         * @param worked if the receiver could handle the data, if not, the sender should cache the data
         */
        void data(KIO::fileoffset_t offset, const QByteArray &data, bool &worked);

        /**
         * Returns data in the forms of URL List
         * @param data in form of QUrl list
         */
        void data(const QList<QUrl> &data);

        /**
         * Returns found checksums with their type
         * @param type the type of the checksum
         * @param checksum the checksum
         */
        void data(const QString type, const QString checksum);

        /**
         * emitted when there is no more data
         */
        void finished();

        /**
         * emitted when an assigned segment finishes
         * @param source the source that emitted this signal
         * @param segmentNumber the number of the segment, to identify it
         * @param connectionFinished true if all segments of this connection have been finished,
         * if one segment (instead of a group of segments) has been assigned this is always true
         */
        void finishedSegment(TransferDataSource *source, int segmentNumber, bool connectionFinished = true);

        /**
         * Alert that datasource is no able to send any data
         * @param source the datasource, sending the signal
         * @param error the error type
         */
        void broken(TransferDataSource *source, TransferDataSource::Error error);

        /**
         * emitted when an assigned segment is broken
         * @param source the source that emitted this signal
         * @param segmentRange the range of the segments e.g. (1,1,) or (0, 10)
         */
        void brokenSegments(TransferDataSource *source, QPair<int, int> segmentRange);

        /**
         * The speed of the download
         * @param speed speed of the download
         */
        void speed(ulong speed);

        /**
         * Emitted when a Datasource itself decides to not download a specific segmentRange,
         * e.g. when there are too many connections for this TransferDataSource
         */
        void freeSegments(TransferDataSource *source, QPair<int, int> segmentRange);

        void log(const QString &message, Transfer::LogLevel logLevel);
        
        /**
         * Emitted when the filename of a url changes, e.g. when a link redirects
         */
        void urlChanged(const QUrl &old, const QUrl &newUrl);

    protected:
        /**
         * Sets the capabilities and automatically emits capabilitiesChanged
         */
        void setCapabilities(Transfer::Capabilities capabilities);

    private Q_SLOTS:
        virtual void slotSpeed(ulong speed) {Q_UNUSED(speed)}

    protected:
        QUrl m_sourceUrl;
        ulong m_speed;
        KIO::filesize_t m_supposedSize;
        int m_parallelSegments;
        int m_currentSegments;

    private:
        Transfer::Capabilities m_capabilities;
};
#endif
