#ifndef LEGACYPATCH_H
#define LEGACYPATCH_H

#include <QObject>
#include <QQueue>
#include <QTimer>

#include "models/downloader.h"
#include "models/filesystem.h"
#include "models/lotrodatmanager.h"
#include "models/settings.h"

class Patch : public QObject
{
    Q_OBJECT

public:
    enum Operation {
        E_CHECKFORUPDATES,
        E_DOWNLOAD,
        E_INSTALL,
        E_ACTIVATE
    };

    struct OperationProgress {

        OperationProgress() = default;
        OperationProgress(Downloader::Status status);
        OperationProgress(LotroDatManager::Status status);

        OperationProgress operator+(const Patch::OperationProgress& other) const;

        double getDownloadPercent() const;
        double getInstallPercent() const;

    public:
        quint64 download_finished_bytes = 0;
        quint64 download_total_bytes = 0;
        quint64 download_speed = 0;
        quint64 download_elapsed_time = 0;

        quint64 install_finished_parts = 0;
        quint64 install_total_parts = 0;
    };

public:
    explicit Patch(QString patch_name, LotroDatManager* mgr, QObject* parent);

    QString getPatchName() const;

public slots:
    void enqueue(const Patch::Operation& operation);

    void enqueue(const QList<Patch::Operation>& operations);

    void processQueue();

private slots: // WARNING: THESE CAN BE NON-BLOCKING OPERATIONS! Use them in chain only via operationFinished signal handlers.
    virtual void checkForUpdates() = 0; // Checks for updates. Updates patches info in Settings.
    virtual void download() = 0;        // Downloads patch contents, prepared for installation.
    virtual void install() = 0;         // Installs patch into game resources (needs to be activated in order to be available in-game)
    virtual void activate() = 0;        // Activates (or deactivates) patch components based on user preferences (patch needs to be installed)

    void onOperationStarted(Operation operation, Patch* patch);
    void onOperationFinished(Operation operation, Patch* patch);

signals: // each signal brings with it pointer to class-emitter, operation name is equal to function name
    void operationStarted(Operation operation, Patch* patch);
    void progressChanged(OperationProgress progress, Patch* patch);
    void operationFinished(Operation operation, Patch* patch);
    void errorOccured(Operation operation, Patch* patch, QString msg);

protected:
    bool needDownloadDatabase(QString db_name); // Checks if database needs to be downloaded (by checksum comparing)

    static bool isDatabaseDownloadEnabled(QString db_name); // Checks if database is checked for installation by user (via Settings)

    void execOperation(Operation operation); // Starts operation execution by its "name"

protected:
    bool running_operation_ = false;
    LotroDatManager* lotro_mgr_;
    QString patch_name_;
    QSet<Downloader*> downloaders_;
    QQueue<Operation> operations_queue_;
    bool is_being_patched_by_lotro_dat_manager_ = false;

    quint32 elapsed_patches_to_install_ = 0;
};

QDebug operator<<(QDebug dbg, const Patch::Operation& operation);

QDebug operator<<(QDebug dbg, const Patch::OperationProgress& operation_progress);

QDebug operator<<(QDebug dbg, const Patch& patch);

#endif // LEGACYPATCH_H
