#include "patchlist.h"

#include <QDebug>

PatchList::PatchList(LotroDatManager *mgr, QObject *parent) : QObject(parent)
{
    lotro_mgr_ = mgr;

    texts_patch_ = new TextsPatch(mgr);
    graphics_patch_ = new GraphicsPatch(mgr);
    sounds_patch_ = new SoundsPatch(mgr);
    videos_patch_ = new VideosPatch(mgr);

    texts_patch_thread_ = new QThread(this);
    graphics_patch_thread_ = new QThread(this);
    sounds_patch_thread_ = new QThread(this);
    videos_patch_thread_ = new QThread(this);

    connect(texts_patch_thread_, &QThread::finished, texts_patch_, &TextsPatch::deleteLater);
    connect(graphics_patch_thread_, &QThread::finished, graphics_patch_, &GraphicsPatch::deleteLater);
    connect(sounds_patch_thread_, &QThread::finished, sounds_patch_, &SoundsPatch::deleteLater);
    connect(videos_patch_thread_, &QThread::finished, videos_patch_, &VideosPatch::deleteLater);

    texts_patch_->moveToThread(texts_patch_thread_);
    graphics_patch_->moveToThread(graphics_patch_thread_);
    sounds_patch_->moveToThread(sounds_patch_thread_);
    videos_patch_->moveToThread(videos_patch_thread_);

    connect(texts_patch_, &TextsPatch::operationStarted, this, &PatchList::onPatchOperationStarted, Qt::QueuedConnection);
    connect(graphics_patch_, &GraphicsPatch::operationStarted, this, &PatchList::onPatchOperationStarted, Qt::QueuedConnection);
    connect(sounds_patch_, &SoundsPatch::operationStarted, this, &PatchList::onPatchOperationStarted, Qt::QueuedConnection);
    connect(videos_patch_, &VideosPatch::operationStarted, this, &PatchList::onPatchOperationStarted, Qt::QueuedConnection);

    connect(texts_patch_, &TextsPatch::operationFinished, this, &PatchList::onPatchOperationFinished, Qt::QueuedConnection);
    connect(graphics_patch_, &GraphicsPatch::operationFinished, this, &PatchList::onPatchOperationFinished, Qt::QueuedConnection);
    connect(sounds_patch_, &SoundsPatch::operationFinished, this, &PatchList::onPatchOperationFinished, Qt::QueuedConnection);
    connect(videos_patch_, &VideosPatch::operationFinished, this, &PatchList::onPatchOperationFinished, Qt::QueuedConnection);

    connect(texts_patch_, &TextsPatch::downloadStatusChanged, this, &PatchList::onPatchDownloadStatusChanged, Qt::QueuedConnection);
    connect(graphics_patch_, &GraphicsPatch::downloadStatusChanged, this, &PatchList::onPatchDownloadStatusChanged, Qt::QueuedConnection);
    connect(sounds_patch_, &SoundsPatch::downloadStatusChanged, this, &PatchList::onPatchDownloadStatusChanged, Qt::QueuedConnection);
    connect(videos_patch_, &VideosPatch::downloadStatusChanged, this, &PatchList::onPatchDownloadStatusChanged, Qt::QueuedConnection);

    connect(texts_patch_, &TextsPatch::installStatusChanged, this, &PatchList::onPatchInstallStatusChanged, Qt::QueuedConnection);
    connect(graphics_patch_, &GraphicsPatch::installStatusChanged, this, &PatchList::onPatchInstallStatusChanged, Qt::QueuedConnection);
    connect(sounds_patch_, &SoundsPatch::installStatusChanged, this, &PatchList::onPatchInstallStatusChanged, Qt::QueuedConnection);
    connect(videos_patch_, &VideosPatch::installStatusChanged, this, &PatchList::onPatchInstallStatusChanged, Qt::QueuedConnection);

    connect(lotro_mgr_, &LotroDatManager::operationFinished, this, &PatchList::onLotroManagerOperationFinished, Qt::QueuedConnection);

    texts_patch_thread_->start();
    graphics_patch_thread_->start();
    sounds_patch_thread_->start();
    videos_patch_thread_->start();

    patch_update_timer.setInterval(10 * 60 * 1000); // 10 minutes
    connect(&patch_update_timer, &QTimer::timeout, this, &PatchList::update);
}

QList<Patch *> PatchList::getPatchList()
{
    return {texts_patch_, graphics_patch_, sounds_patch_, videos_patch_};
}

void PatchList::onPatchOperationStarted(QString operation_name, Patch *patch)
{
    patch_update_timer.stop();
    if (active_operations_num_ == 0) {
        emit patchOperationsStarted();
    }
    ++active_operations_num_;

    qDebug() << "Operation " << operation_name << " started of patchset " << patch->getPatchName();
}

void PatchList::onPatchOperationFinished(QString operation_name, Patch *patch, bool result)
{
    qDebug() << "Operation " << operation_name << " finished of patchset " << patch->getPatchName() << ", result = " << result;
    --active_operations_num_;

    if (result && operation_name == "checkForUpdates") {
        QMetaObject::invokeMethod(patch, &Patch::download, Qt::QueuedConnection);
        return;
    }

//    if (result && operation_name == "download") {
//        QMetaObject::invokeMethod(patch, &Patch::install, Qt::QueuedConnection);
//        return;
//    }

//    if (result && operation_name == "install") {
//        QMetaObject::invokeMethod(patch, &Patch::activate, Qt::QueuedConnection);
//        return;
//    }

    if (active_operations_num_ == 0) {
        if (auto_updates_enabled_) {
            patch_update_timer.start();
        }
        qDebug() << __FUNCTION__ << "All patch operations successfully finished!";
        emit patchOperationsFinished();
    }
}

void PatchList::onPatchDownloadStatusChanged(Patch *patch, Downloader::Status status)
{
    patches_download_status_[patch] = status;
    Downloader::Status total_status;

    foreach (Downloader::Status st, patches_download_status_) {
        total_status = total_status + st;
    }

    emit downloadTotalStatusChanged(total_status);
}

void PatchList::onPatchInstallStatusChanged(Patch *patch, Patch::InstallationStatus status)
{
    patches_installation_status_[patch] = status;
    Patch::InstallationStatus total_status;

    foreach (Patch::InstallationStatus st, patches_installation_status_) {
        total_status = total_status + st;
    }

    emit installTotalStatusChanged(total_status);
}

void PatchList::onLotroManagerOperationFinished(QString operation_name, QVector<QVariant>, bool result)
{
    if (operation_name == "initializeManager") {
        --active_operations_num_;

        if (active_operations_num_ == 0) {
            emit patchOperationsFinished();
        }

//        if (result == true) {
            update();
//        }
    }
}

void PatchList::startAutoUpdate()
{
    auto_updates_enabled_ = true;
    patch_update_timer.start();
}

void PatchList::initialize() {
    ++active_operations_num_;
    emit patchOperationsStarted();
    QMetaObject::invokeMethod(lotro_mgr_, &LotroDatManager::initializeManager, Qt::QueuedConnection);
}

void PatchList::update()
{
    if (active_operations_num_ != 0) {
        qDebug() << __FUNCTION__ << "Tried to start update, while other operations weren't finished yet!";
        return;
    }

    qDebug() << __FUNCTION__ << "Starting update!";
    QMetaObject::invokeMethod(texts_patch_, &TextsPatch::checkForUpdates, Qt::QueuedConnection);
    QMetaObject::invokeMethod(graphics_patch_, &GraphicsPatch::checkForUpdates, Qt::QueuedConnection);
    QMetaObject::invokeMethod(sounds_patch_, &SoundsPatch::checkForUpdates, Qt::QueuedConnection);
    QMetaObject::invokeMethod(videos_patch_, &VideosPatch::checkForUpdates, Qt::QueuedConnection);
}
