#include "app.h"
#include "filesystem.h"
#include "lotromanager.h"

#include <QFileInfo>
#include <QApplication>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QString>
#include <QStringList>
#include <QTimer>
#include <stdio.h>

DownloadManager::DownloadManager(QObject *parent)
    : QObject(parent), downloadedCount(0), totalCount(0)
{
    busy = false;
}

void DownloadManager::append(const QStringList &urlList){
    foreach (QString url, urlList)
        append(QUrl::fromEncoded(url.toUtf8()));

    if (downloadQueue.isEmpty())
        QTimer::singleShot(0, this, SIGNAL(finished()));
}

void DownloadManager::append(const QUrl &url){
    qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Добавлен файл для скачивания: ", url.fileName().toUtf8().data());

    if (downloadQueue.count(url) == 0) {
        downloadQueue.enqueue(url);
        ++totalCount;
    } else {
        qDebug("%s:%i: %s %s%s", __FILE__, __LINE__, "Не добавляю закачку "
               , url.fileName().toUtf8().data()
               , ", так как она уже находится в очереди закачек");
    }
    qDebug("%s:%i: %s%i", __FILE__, __LINE__, "Загрузок в списке: ", totalCount);

    //if (!busy)
    //    QTimer::singleShot(0, this, SLOT(startNextDownload()));
}

void DownloadManager::startDownloads() {
    if (!busy)
        QTimer::singleShot(0, this, SLOT(startNextDownload()));
}

void DownloadManager::startNextDownload() {
    current_speed = "";
    bytesReceivedBeforeSecond = 0;
    timeElapsedBeforeSecond = 0;
    elapsed_time = "высчитывается...";

    if (busy) {
        qWarning("%s:%i: %s", __FILE__, __LINE__, "Ошибка! Обнаружена попытка начала загрузки, когда не завершилась другая загрузка");
        return;
    }

    App *app = &App::getInstance();
    app->helper->setState("busy");
    busy = true;

    if (downloadQueue.isEmpty()) {
        app->helper->setState("free");// говорим что приложение освободилось
        qDebug("%s:%i: %s%d/%d", __FILE__, __LINE__, "Загрузка завершена. Загружено файлов: ", downloadedCount, totalCount);
        busy = false;
        emit finished();
        return;
    }

    QUrl url = downloadQueue.dequeue();
    QString filename = QFileInfo(url.path()).fileName();
    output.setFileName(QApplication::applicationDirPath() + "/data/" + filename);
    qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Начата загрузка файла: ", filename.toUtf8().data());

    // Проверяем целостность файла и игнорируем в случае если он цел
    QString hash = FileSystem::fileHash(QApplication::applicationDirPath() + "/data/" + filename, QCryptographicHash::Md5);
    QStringList pname = output.fileName().split("/");
    QStringList ptype = pname.last().split("_");
    qDebug() << ptype.first();
    QString keyname = ptype.first();
    if(keyname == "loadscreens") keyname = "screens";
    if(hash == app->config->getValue("Hashes", ptype.first()) && app->config->getValue("Editor", keyname) == "true"){
        qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Проверка хэша успешно завершена: ", filename.toUtf8().data());
        busy = false;
        QTimer::singleShot(0, this, SLOT(startNextDownload()));
        return;
    }

    qDebug("%s:%i: %s%s", __FILE__, __LINE__, "Хэш файла не совпал: ", filename.toUtf8().data());
    QStringList parsename = filename.split("_");
    QString name = parsename[0] + "Status";
    download_name = parsename[0];

    // Проверка, что файл не был отменён
    if(app->config->getValue("Editor", download_name) == "false") {
        emit changePatchStatus(download_name, "Отменён");
        busy = false;
        QTimer::singleShot(0, this, SLOT(startNextDownload()));
        return;
    }

    if (!output.open(QIODevice::WriteOnly)) {
        qWarning("%s:%i: %s%s", __FILE__, __LINE__, "Произошла остановка скачивания (не могу открыть файл) "
                 , filename.toUtf8().data());
        emit changePatchStatus(download_name, "Ошибка записи");
        busy = false;
        QTimer::singleShot(0, this, SLOT(startNextDownload()));
        return;
    }

    qInfo("%s:%i: %s%s", __FILE__, __LINE__, "Начинаем скачивание ", url.fileName().toUtf8().data());

    QNetworkRequest request(url);
    currentDownload = manager.get(request);
    connect(this, SIGNAL(cancelDownload()), currentDownload, SLOT(abort()));
    connect(currentDownload, SIGNAL(downloadProgress(qint64,qint64)), SLOT(downloadProgress(qint64,qint64)));
    connect(currentDownload, SIGNAL(finished()), SLOT(downloadFinished()));
    connect(currentDownload, SIGNAL(readyRead()), SLOT(downloadReadyRead()));

    // prepare the output

    downloadTime.start();
}

void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) {    
    App* app = &App::getInstance();

    double speed = (bytesReceived - bytesReceivedBeforeSecond) * 1000.0 / (downloadTime.elapsed() - timeElapsedBeforeSecond);
    double percent =  double(std::ceil(((double)bytesReceived/ bytesTotal) * 100 * 10)) / 10.0;

    QString unit;
    if (speed < 1024) {
        unit = "bytes/sec";
    } else if (speed < 1024*1024) {
        speed /= 1024;
        unit = "kB/s";
    } else {
        speed /= 1024*1024;
        unit = "MB/s";
    }
    QString speedtext = QString::fromLatin1("%1 %2").arg(speed, 3, 'f', 1).arg(unit);
    QString percenttext = QString::fromLatin1("%1").arg(percent, 3, 'f', 1);

    if (downloadTime.elapsed() - timeElapsedBeforeSecond >= 1000) {
        timeElapsedBeforeSecond = downloadTime.elapsed();
        bytesReceivedBeforeSecond = bytesReceived;
        current_speed = speedtext;
        elapsed_time = app->helper->countFormattedElapsedTime(bytesTotal - bytesReceived, bytesReceived * 1000.0 / downloadTime.elapsed());
    }

    if (current_speed == "")
        current_speed = speedtext;


    emit changePatchStatus(download_name, "Загрузка ... <br/>" + percenttext + "% (" + current_speed + ")");
    emit changeHint("Загрузка патча " + LotroManager::getInstance().patchTitleFromName(download_name),
                    "Завершено " + percenttext + "% (" + current_speed + "). Оставшееся время: " + elapsed_time);
}

void DownloadManager::downloadFinished() {
    busy = false;
    output.close();
    qDebug() << "Finished download: " << output.fileName().split('/').last().split('_')[0];

    downloaded_list.append(output.fileName().split('/').last().split('_')[0]);

    App *app = &App::getInstance();
    app->helper->setState("free");// говорим что приложение освободилось

    if (currentDownload->error()) {
        qWarning("%s:%i: %s%s", __FILE__, __LINE__, "Загрузка не удалась: ", currentDownload->errorString().toUtf8().data());
        emit changePatchStatus(download_name, "Не удалась");
    } else {
        emit changePatchStatus(download_name, "Готово");
        ++downloadedCount;
    }

    if (downloadQueue.isEmpty()) {
        qInfo("%s:%i: %s", __FILE__, __LINE__, "Все загрузки завершены. Загрузчик завершил свою работу.");
        emit allDownloadsFinished(downloaded_list);
    } else {
        QTimer::singleShot(0, this, SLOT(startNextDownload()));
    }

    currentDownload->deleteLater();
}

void DownloadManager::downloadReadyRead(){
    output.write(currentDownload->readAll());
}

void DownloadManager::abortDownload(QString name){
    App *app = &App::getInstance();
    if(download_name == name){
       if(app->state != "free" &&  currentDownload != NULL && currentDownload->isOpen()) {
            qDebug() << "Прерываем закачку " + download_name;
            qInfo("%s:%i: %s%s", __FILE__, __LINE__, "Пользователь прервал закачку файла ", download_name.toUtf8().data());
            emit changePatchStatus(name, "Отменён");
            currentDownload->abort();
       }
    }
    downloadQueue.removeAll(name);
}

int DownloadManager::getDownloadedCount() {
    return downloadedCount;
}

void DownloadManager::resetDownloadedCount() {
    totalCount = 0;
    downloadedCount = 0;
    downloaded_list.clear();
}
