#ifndef INCLUDEILEDOWNLOADER_H
#define INCLUDEILEDOWNLOADER_H

#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QFile>
#include <QTime>
#include <QByteArray>

class Downloader : public QObject
{
    Q_OBJECT

public:
    struct Status {
        bool running = false;
        double percent = 0.0;

        quint64 total_bytes = 0;
        quint64 downloaded_bytes = 0;
        quint64 current_speed = 0;
        quint64 average_speed = 0;
        quint64 elapsed_time = 0;
    };

    friend Status operator+(const Status& a, const Status &b) {
        Status result;
        result.running = a.running || b.running;
        result.total_bytes = a.total_bytes + b.total_bytes;
        result.downloaded_bytes = a.downloaded_bytes + b.downloaded_bytes;
        result.percent = double(result.downloaded_bytes) * 100.0 / double(result.total_bytes);
        result.average_speed = a.average_speed + b.average_speed;
        result.current_speed = a.current_speed + b.current_speed;


        // Counting elapsed time
        quint64 delta_size = result.total_bytes - result.downloaded_bytes;
        quint64 avg_speed = qMax(quint64(1), result.average_speed);

        while (delta_size > 1024 && avg_speed > 1024) { // Making precision smaller to get smoother dynamics
            delta_size /= 1024;
            avg_speed /= 1024;
        }
        result.elapsed_time = delta_size / avg_speed;


        return result;
    }

    explicit Downloader(QObject *parent = 0);
private:
    Q_DISABLE_COPY(Downloader)

public:
    virtual ~Downloader();

    QUrl getUrl();
    void setUrl(const QUrl& _url);
    void waitForDownloaded();

    bool isStarted();
    double getPercent();
    quint64 getBytesTotal();
    quint64 getBytesDownloaded();
    quint64 getElapsedTime();
    quint64 getCurrentSpeed();
    quint64 getAverageSpeed();

    Status getDownloadStatus();

    static QString getSizeFormatted(quint64 bytes);
    static QString getSpeedFormatted(quint64 speed_bytes_per_sec);
    static QString getElapsedTimeFormatted(quint64 elapsed_time_secs);

signals:
    void downloadFinished(Downloader* this_downloader_ptr);
    void progressChanged(Downloader* this_downloader_ptr, Status status);

public slots:
    void start();
    void updateDownloadSpeedLimit(int bytes_per_sec);
    void stop();

private slots:
    void onDownloadProgressChanged(qint64 bytesReceived, qint64 bytesTotal);
    void onDownloadFinished(QNetworkReply* pReply);
    void onReadyRead();

public:
    QFile* targetFile {nullptr};
    QByteArray* targetBytearray {nullptr};

private:
    QTime last_tick_time_;
    quint64 download_speed_ = 0; // bytes per second
    quint64 average_speed_ = 0;
    quint64 update_ticks_counter_ = 0;

    quint64 bytes_total_ = 0;
    quint64 bytes_downloaded_ = 0;

    quint64 bytes_downloaded_before_tick_ = 0; // Нужны для подсчёта текущей скорости скачивания, а не

    bool busy_;
    QUrl url_;
    QNetworkReply* m_CurrentReply {nullptr};
    QNetworkAccessManager m_WebCtrl;

    unsigned download_speed_limit {0};
};


Q_DECLARE_METATYPE(Downloader::Status)

#endif // INCLUDEILEDOWNLOADER_H
