#pragma once

#include <QString>
#include <QList>
#include <QJsonObject>
#include <QJsonArray>
#include <QUuid>
#include <QMetaType>
#include <QDBusArgument>

const int MAIN_WINDOW_WIDTH = 860;
const int MAIN_WINDOW_HEIGHT = 640; // 实际在界面量出来的大概是600

static double KB = 1000;
static double MB = 1000 * KB;
static double GB = 1000 * MB;

static double KiB = 1024;
static double MiB = 1024 * KiB;
static double GiB = 1024 * MiB;

const QString BackupModuleName = "BackupModule";
const QString RestoreModuleName = "RestoreModule";
const QString AdvancedModuleName = "AdvancedModule";

static const QString BACKUP_USER_INFO = "/var/lib/deepin-boot-kit/config/backupUserInfo.json";
static const QString UOS_RECOVERY_INI = "etc/uos-recovery/uos-recovery.ini";
static const QString BACKUP_GROUP = "backup";
static const QString RESTORE_GROUP = "restore";
static const QString GHOST_GROUP = "ghost";
static const QString GHOST_POLICY = "ghost_policy";
static const QString BACKUP_DEVICE_UUID_KEY = "device_uuid";
static const QString BACKUP_HISTORY_DEVICE_UUID_KEY = "history_device_uuid";
static const QString BACKUP_SYNC_TYPE_KEY = "type";
static const QString BACKUP_USER_DATA_SYNC_TYPE_KEY = "user_data_type";

static const QString RESTORE_AUTO_RESTORE_KEY = "auto_restore";
static const QString RESTORE_DO_RESTORE_KEY = "do_restore";
static const QString RESTORE_RECOVERY_TYPE_KEY = "recovery_type";
static const QString RESTORE_BACKUP_DEV_UUID_KEY = "backup_device_uuid";
static const QString RESTORE_BACKUP_POINT_KEY = "backup_point";
static const QString RESTORE_ROLLBACK_STATUS = "rollback_status";
static const QString RESTORE_ROLLBACK_VERSION = "rollback_version";
static const QString RESTORE_ROLLBACK_BEFORE_VERSION = "rollback_before_version";
static const QString V20RESTORE_PATH = "v20_restore_path";
static const QString V20RESTORE_LIST = "v20_restore_list";
static const QString V20RESTORE_TAR = "v20_restore_tar";

static const QString LAYOUT_GROUP = "layout";
static const QString UI_LAYOUT_BACKUP = "ui_layout_backup";
static const QString UI_LAYOUT_RESTORE = "ui_layout_restore";
static const QString UI_LAYOUT_ADVANCE = "ui_layout_advance";

static const int OS_VERSION_MAJOR_V23 = 23;
static const int OS_VERSION_MINOR_V23 = 23;
static const int OS_VERSION_MAJOR_V25 = 25;
static const int OS_VERSION_MINOR_V25 = 2500;

namespace RestoreConst {
    const int RESTORE_UNKNOWN = -1;
    const int RESTORE_SUCCESS = 0;
    const int RESTORE_FAILED = 1;
}

namespace ImmutableErrCode {
    const QString OTHER_INSTANCE_RUNNING = "000001";
}

enum ErrorCode {
    InvalidClient = -2,
    UnKnow = -1,                       //未知错误
    OK = 0,
    PartitionNotSupportRecovery, //分区方式不支持备份还原
    InsufficientDiskSpace,       //磁盘空间不足
    InvalidDirectory,            //无效的目录
    NoAuthority,                 //没有权限
    NoSpecifySystem,              //未指定需要备份的系统
    NullPointer,
    PartitionNotMount,           //分区未挂载
    PartitionNotExist,            //分区不存在
    TmpPartitionNotSupport,  // 系统备份不支持备份到/tmp分区
    ParamError,                    //参数错误
    RootUuidIsEmpty,               //根分区uuid为空
    DestUuidIsEmpty,               // 存储目的分区uuid为空
    DbusError,
    InvalidVersion,   // deepin-boot-kit get version failed
    NoInitBackup,
    OSTreeRunning,
    NoWriteable
};

enum OperateStatus {
    Running = 0,
    Success,
    Failed,
    Abort
};

enum FileType {
    InvalidFileType = -1,
    Dir = 0,
    File
};

class FileItem;
struct FileItem {
    QString fileName;
    FileType fileType;
    quint64 size = 0;
    QList<FileItem> children;

    FileItem() : fileName(""), fileType(FileType::InvalidFileType)
    {
        children.clear();
    }

    QJsonObject marshal() {
        QJsonObject jsonObject;
        jsonObject.insert("fileName", fileName);
        jsonObject.insert("fileType", fileType);
        QJsonArray array;
        for (auto &item : children) {
            QJsonObject childObject;
            childObject.insert("fileName", fileName);
            childObject.insert("fileType", fileType);
            array.append(childObject);
        }
        jsonObject.insert("children", array);
        return jsonObject;
    }

    void unmarshal(QJsonObject &jsonObject) {
        fileName = jsonObject.value("fileName").toString();
        fileType = static_cast<FileType>(jsonObject.value("fileType").toInt());
        QJsonArray array = jsonObject.value("children").toArray();
        for (auto a : array) {
            FileItem childItem;
            childItem.fileName = a.toObject().value("fileName").toString();
            childItem.fileType = static_cast<FileType>(a.toObject().value("fileType").toInt());
            children.append(childItem);
        }
    }

    bool operator==(const FileItem &other) const
    {
        return ((this->fileName == other.fileName) && (this->fileType == other.fileType));
    }
};

//备份还原策略类型
enum class RecoveryType {
    InvalidRecoveryType = -1,
    Rsync = 0,                   //使用rsync进行数据备份和恢复
    OSTree,                      //使用OSTree进行系统版本管理
    BtrfsSnapshot,               //使用btrfs文件系统快照进行数据备份和恢复
    LvmSnapshot,
    RsyncV20,
    ImmutableMode
};

//备份还原操作类型
enum class OperateType {
    Invalid = -1,
    SystemBackup = 0,            //系统备份
    SystemRestore,               //系统恢复
    UserDataBackup,              //用户数据备份
    UserDataRestore,             //用户数据恢复
    removeBackup,                //删除备份数据
    CheckFullSystemBackupSpace,  // 全量系统备份空间校验
    CheckIncSystemBackupSpace,   // 增量系统备份空间校验
    CheckGhostBackupSpace,       // Ghost备份空间校验
    CheckUserDataBackupSpace,    //数据备份空间校验
    CheckDimFileUseSpace,        //v20备份数据空间校验
    GhostBackup,                 //Ghost备份
    DimFileRestore,              //Dim文件转换
    AutoBackup,                  // OStree SubmissionType 为0时
    FactoryRestore,              // 恢复出厂设置
    ManualBackupV20,
    SystemBackupV20,
    ManualRestoreV20,
    SystemRestoreV20,
    InitRestoreV20,
    CancelBackupV20,
    CancelRestoreV20,
    CancelInitRestoreV20,
    RemoveCoverBackupV20,
    ImmutableSystemBackup,
    ImmutableSystemRestore
};

// OStree 原子更新 commit 类型
enum CommitType {
    InvalidCommit = -1,
    SystemCommit = 0,      // 系统提交
    UserCommit = 1,        // 用户提交
    InstallerCommit = 2    // 安装器提交
};

// 架构类型
enum PlatformType {
    X86_64 = 0,
    I386,
    I686,
    AMD64,
    X86,
    SW_64,
    MIPS64,
    LOONGARCH64,
    AARCH64,
    UNKNOW
};

//用户操作记录
struct OperateRecord {
    QString operateID;           //操作id（UUID）
    QString username;            //操作者用户名
    quint64 startTime;           //操作开始时间
    quint64 endTime;             //操作结束时间
    OperateType operateType;     //操作类型
    RecoveryType recoveryType;   //策略类型
};
typedef QList<OperateRecord> OperateRecordList;

//分区信息
struct Partition {
    QString name;
    QString fsType;
    QString label;
    QString uuid;
    QString deviceType;
    QString mountPoint;
    quint64 fsSize = 0;
    quint64 size = 0;
    quint64 used = 0;
    quint64 free = 0;
    bool fsTypeSupported; // 是否支持系统备份
    bool fsTypeSupportedDataBackup; // 是否支持数据备份
    bool externalDevice;

    QJsonObject marshal() {
        QJsonObject jsonObject;
        jsonObject.insert("name", name);
        jsonObject.insert("fsType", fsType);
        jsonObject.insert("label", label);
        jsonObject.insert("uuid", uuid);
        jsonObject.insert("deviceType", deviceType);
        jsonObject.insert("mountPoint", mountPoint);
        jsonObject.insert("fsSize", QString::number(fsSize));
        jsonObject.insert("size", QString::number(size));
        jsonObject.insert("used", QString::number(used));
        jsonObject.insert("free", QString::number(free));
        jsonObject.insert("fsTypeSupported", fsTypeSupported);
        jsonObject.insert("fsTypeSupportedDataBackup", fsTypeSupportedDataBackup);
        jsonObject.insert("externalDevice", externalDevice);
        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject) {
        name = jsonObject.value("name").toString();
        fsType = jsonObject.value("fsType").toString();
        label = jsonObject.value("label").toString();
        uuid = jsonObject.value("uuid").toString();
        deviceType = jsonObject.value("deviceType").toString();
        mountPoint = jsonObject.value("mountPoint").toString();
        fsSize = jsonObject.value("fsSize").toString().toULongLong();
        size = jsonObject.value("size").toString().toULongLong();
        used = jsonObject.value("used").toString().toULongLong();
        free = jsonObject.value("free").toString().toULongLong();
        fsTypeSupported = jsonObject.value("fsTypeSupported").toBool();
        fsTypeSupportedDataBackup = jsonObject.value("fsTypeSupportedDataBackup").toBool();
        externalDevice = jsonObject.value("externalDevice").toBool();
    }

};

//备份信息
struct BackupInfo {
    QString operateID;           //操作id（UUID）
    QString username;            //操作者用户名
    quint64 startTime = 0;       //操作开始时间
    QString remark;              //备注信息
    int operateType = static_cast<int> (OperateType::Invalid);     // OperateType 操作类型
    int recoveryType = static_cast<int> (RecoveryType::InvalidRecoveryType);   // RecoveryType 策略类型
    QString backupPath;          //备份路径
    quint64 size = 0;            //大小
    QString rootUUID;
    QString backupDevUUID;       //存储备份文件的分区
    QString backupDevice;
    bool backupDeviceRemovable = false;
    int status = static_cast<int> (OperateStatus::Failed);        // OperateStatus 状态
    quint64 submissionTime = 0;  // OStree: 十位时间戳，精度为秒
    QString systemVersion;       // OStree: 当前系统版本号
    QString submissionVersion;   // OStree: 提交版本号
    int submissionType = static_cast<int> (CommitType::InvalidCommit);   // OStree: CommitType 本次提交类型
    QString backupVersion;
    QString versionDisplay;
    QString snapshotName; // immutable system
    int systemEnvType = -1; // 1: immutable system

    QJsonObject marshal() const {
        QJsonObject jsonObject;
        jsonObject.insert("operateID", operateID);
        jsonObject.insert("username", username);
        jsonObject.insert("startTime", QString("%1").arg(startTime));
        jsonObject.insert("remark", remark);
        jsonObject.insert("operateType", static_cast<int> (operateType));
        jsonObject.insert("recoveryType", recoveryType);
        jsonObject.insert("backupPath", backupPath);
        jsonObject.insert("size", QString("%1").arg(size));
        jsonObject.insert("rootUUID", rootUUID);
        jsonObject.insert("backupDevUUID", backupDevUUID);
        jsonObject.insert("backupDevice", backupDevice);
        jsonObject.insert("backupDeviceRemovable", backupDeviceRemovable);
        jsonObject.insert("status", status);
        jsonObject.insert("SubmissionTime", QString("%1").arg(submissionTime));
        jsonObject.insert("SystemVersion", systemVersion);
        jsonObject.insert("SubmissionVersion", submissionVersion);
        jsonObject.insert("SubmissionType", submissionType);
        jsonObject.insert("backupVersion", backupVersion);
        jsonObject.insert("versionDisplay", versionDisplay);
        jsonObject.insert("snapshotName", snapshotName);
        jsonObject.insert("systemEnvType", systemEnvType);
        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject) {
        operateID = jsonObject.value("operateID").toString();
        username = jsonObject.value("username").toString();
        startTime = jsonObject.value("startTime").toString().toULongLong();
        remark = jsonObject.value("remark").toString();
        //operateType = static_cast<OperateType>(jsonObject.value("operateType").toInt());
        operateType = jsonObject.value("operateType").toInt();
        //recoveryType = static_cast<RecoveryType>(jsonObject.value("recoveryType").toInt());
        recoveryType = jsonObject.value("recoveryType").toInt();
        backupPath = jsonObject.value("backupPath").toString();
        size = jsonObject.value("size").toString().toULongLong();
        rootUUID = jsonObject.value("rootUUID").toString();
        backupDevUUID = jsonObject.value("backupDevUUID").toString();
        backupDevice = jsonObject.value("backupDevice").toString();
        backupDeviceRemovable = jsonObject.value("backupDeviceRemovable").toBool();
        //status = static_cast<OperateStatus>(jsonObject.value("status").toInt());
        status = jsonObject.value("status").toInt();

        // OStree 才需要下面的字段
        submissionTime = jsonObject.value("SubmissionTime").toString().toULongLong();

        if (jsonObject.contains("SystemVersion")) {
            systemVersion = jsonObject.value("SystemVersion").toString();
        }

        if (jsonObject.contains("SubmissionVersion")) {
            submissionVersion = jsonObject.value("SubmissionVersion").toString();
        }

        submissionType = jsonObject.value("SubmissionType").toInt(-1);

        if (jsonObject.contains("backupVersion")) {
            backupVersion = jsonObject.value("backupVersion").toString();
        }

        if (jsonObject.contains("versionDisplay")) {
            versionDisplay = jsonObject.value("versionDisplay").toString();
        }

        if (jsonObject.contains("snapshotName")) {
            snapshotName = jsonObject.value("snapshotName").toString();
        }

        systemEnvType = jsonObject.value("systemEnvType").toInt(-1);
    }

    bool operator < (const BackupInfo& d) const {
        if (startTime <= 0) {
            return true;
        }
        return startTime <= d.startTime;
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const BackupInfo &info);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, BackupInfo &info);
};
Q_DECLARE_METATYPE(BackupInfo)

typedef QList<BackupInfo> BackupInfoList;

enum class ActionType {
    Null,
    ManualBackup,
    SystemBackup,
    ManualRestore,
    SystemRestore,
    InitialRestore,
    CancelBackup,
    CancelRestore
};

enum class ErrorType {
    NoError = 0,
    SpaceError,     //磁盘空间不足
    FsError,        //文件系统不支持
    PathError,      //不是有效路径
    LocationError,  //路径位置不允许
    MD5Error,       //备份文件错误
    DeviceReadOnly, // 只读设备，不允许备份
    BackupPathError, // 备份文件路径位置错误
    FileError,
    NotFoundRecoveryPartition,
    DeleteFileError,
    UNKOWN          //其他未知错误
};

// v20 backup files
struct BackupFileInfo {
    QString backupUUID;
    QString backupPath;
    QString backupTime;
    QString createTime;
    QString backupFullPath;

    QJsonObject marshal()
    {
        QJsonObject jsonObject;
        jsonObject.insert("backupUUID", backupUUID);
        jsonObject.insert("backupPath", backupPath);
        jsonObject.insert("backupTime", backupTime);
        jsonObject.insert("createTime", createTime);
        jsonObject.insert("backupFullPath", backupFullPath);
        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject) {
        if (jsonObject.contains("backupUUID")) {
            backupUUID = jsonObject.value("backupUUID").toString();
        }

        if (jsonObject.contains("backupPath")) {
            backupPath = jsonObject.value("backupPath").toString();
        }

        if (jsonObject.contains("backupTime")) {
            backupTime = jsonObject.value("backupTime").toString();
        }

        if (jsonObject.contains("createTime")) {
            createTime = jsonObject.value("createTime").toString();
        }

        if (jsonObject.contains("backupFullPath")) {
            backupFullPath = jsonObject.value("backupFullPath").toString();
        }
    }
};

void registerALLMetaType();

struct BaseData {
    QString operateID;
    QString userName;
    int operateType; // OperateType

    BaseData() : operateID(QUuid::createUuid().toString()), userName(""), operateType(-1)
    {}

    BaseData(const QString &opID, const QString &name, const int &type) : operateID(opID),
                                                                                 userName(name), operateType(type)
    {}

    QJsonObject marshal() const
    {
        QJsonObject jsonObject;
        jsonObject.insert("operateID", operateID);
        jsonObject.insert("userName", ""); // 不显示
        jsonObject.insert("operateType", operateType);

        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject)
    {
        if (jsonObject.contains("operateID")) {
            operateID = jsonObject.value("operateID").toString();
        }

        if (jsonObject.contains("userName")) {
            userName = jsonObject.value("userName").toString();
        }

        if (jsonObject.contains("operateType")) {
            operateType = jsonObject.value("operateType").toInt(static_cast<int>(OperateType::Invalid));
        }
    }
};

struct V20BackupReq : public BaseData {
    QString backupPath;
    QString language;
    int actionType; // v20  ActionType

    V20BackupReq() : BaseData(), backupPath(""), language(""), actionType(static_cast<int> (ActionType::Null))
    {}

    V20BackupReq(const QString &opID, const QString &name, const int &type,
                 const QString &path, const QString &lang, const int &acType) :
            BaseData(opID, name, type), backupPath(path), language(lang), actionType(acType)
    {}

    QJsonObject marshal() const
    {
        QJsonObject jsonObject = BaseData::marshal();
        jsonObject.insert("backupPath", backupPath);
        jsonObject.insert("language", language);
        jsonObject.insert("actionType", actionType);

        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject)
    {
        BaseData::unmarshal(jsonObject);
        if (jsonObject.contains("backupPath")) {
            backupPath = jsonObject.value("backupPath").toString();
        }

        if (jsonObject.contains("language")) {
            language = jsonObject.value("language").toString();
        }

        if (jsonObject.contains("actionType")) {
            actionType = jsonObject.value("actionType").toInt(static_cast<int>(ActionType::Null));
        }
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const V20BackupReq &req);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, V20BackupReq &req);
};
Q_DECLARE_METATYPE(V20BackupReq)

struct V20RestoreReq : public BaseData {
    QString backupDir;
    QString language;
    bool formatData;
    int actionType; // v20  ActionType

    V20RestoreReq() : BaseData(), backupDir(""), language(""), formatData(false)
    {}

    V20RestoreReq(const QString &opID, const QString &name, const int &type,
                   const QString &dir, const QString &lang, bool isFormat, const int &acType)
            : BaseData(opID, name, type)
            , backupDir(dir)
            , language(lang)
            , formatData(isFormat)
            , actionType(acType)
    {}

    QJsonObject marshal() const
    {
        QJsonObject jsonObject = BaseData::marshal();
        jsonObject.insert("backupDir", backupDir);
        jsonObject.insert("language", language);
        jsonObject.insert("formatData", formatData);
        jsonObject.insert("actionType", actionType);

        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject)
    {
        BaseData::unmarshal(jsonObject);
        if (jsonObject.contains("backupDir")) {
            backupDir = jsonObject.value("backupDir").toString();
        }

        if (jsonObject.contains("language")) {
            language = jsonObject.value("language").toString();
        }

        if (jsonObject.contains("formatData")) {
            formatData = jsonObject.value("formatData").toBool();
        }

        if (jsonObject.contains("actionType")) {
            actionType = jsonObject.value("actionType").toInt(static_cast<int>(ActionType::Null));
        }
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const V20RestoreReq &req);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, V20RestoreReq &req);
};
Q_DECLARE_METATYPE(V20RestoreReq)

struct V20Response : public BaseData {
    int retCode;
    int progress;
    QString errMsg;
    bool autoReboot;

    V20Response() : BaseData(), retCode(-1), progress(0), errMsg(""), autoReboot(false)
    {}

    V20Response(const QString &opID, const QString &name, const int &type, int code, int progress,
             const QString &err, bool reboot)
            : BaseData(opID, name, type)
            , retCode(code)
            , progress(progress)
            , errMsg(err)
            , autoReboot(reboot)
    {}

    QJsonObject marshal() const
    {
        QJsonObject jsonObject = BaseData::marshal();
        jsonObject.insert("retCode", retCode);
        jsonObject.insert("progress", progress);
        jsonObject.insert("errMsg", errMsg);
        jsonObject.insert("autoReboot", autoReboot);

        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject)
    {
        BaseData::unmarshal(jsonObject);
        if (jsonObject.contains("retCode")) {
            retCode = jsonObject.value("retCode").toInt(-1);
        }

        if (jsonObject.contains("progress")) {
            progress = jsonObject.value("progress").toInt(0);
        }

        if (jsonObject.contains("errMsg")) {
            errMsg = jsonObject.value("errMsg").toString();
        }

        if (jsonObject.contains("autoReboot")) {
            autoReboot = jsonObject.value("autoReboot").toBool();
        }
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const V20Response &rsp);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, V20Response &rsp);
};
Q_DECLARE_METATYPE(V20Response)


struct ImmutableBackupReq : public BaseData {
    QString snapshotName;
    QString description;

    ImmutableBackupReq() : BaseData(), snapshotName(""), description("")
    {}

    ImmutableBackupReq(const QString &opID, const QString &name, const int &type,
                 const QString &snapName, const QString &des) :
            BaseData(opID, name, type), snapshotName(snapName), description(des)
    {}

    QJsonObject marshal() const
    {
        QJsonObject jsonObject = BaseData::marshal();
        jsonObject.insert("snapshotName", snapshotName);
        jsonObject.insert("description", description);

        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject)
    {
        BaseData::unmarshal(jsonObject);
        if (jsonObject.contains("snapshotName")) {
            snapshotName = jsonObject.value("snapshotName").toString();
        }

        if (jsonObject.contains("description")) {
            description = jsonObject.value("description").toString();
        }
    }

  //  static void registerMetaType();
  //  friend QDBusArgument &operator<<(QDBusArgument &arg, const ImmutableBackupReq &req);
   // friend const QDBusArgument &operator>>(const QDBusArgument &arg, ImmutableBackupReq &req);
};
//Q_DECLARE_METATYPE(ImmutableBackupReq)

struct SystemBackupRequest : public BaseData {
    QString snapshotName;
    QString rootUUID;
    QString destUUID;
    QString remark;

    SystemBackupRequest() : BaseData(), snapshotName(""), rootUUID(""), destUUID(""), remark("")
    {}

    SystemBackupRequest(const QString &opID, const QString &name, const int &type,
                       const QString &snapName, const QString &rootUuid, const QString &destUuid, const QString &des) :
            BaseData(opID, name, type), snapshotName(snapName), rootUUID(rootUuid), destUUID(destUuid), remark(des)
    {}

    QJsonObject marshal() const {
        QJsonObject jsonObject = BaseData::marshal();
        jsonObject.insert("snapshotName", snapshotName);
        jsonObject.insert("rootUUID", rootUUID);
        jsonObject.insert("destUUID", destUUID);
        jsonObject.insert("remark", remark);
        jsonObject.insert("operateID", operateID);
        return jsonObject;
    }

    void unmarshal(QJsonObject &jsonObject) {
        BaseData::unmarshal(jsonObject);
        snapshotName = jsonObject.value("snapshotName").toString();
        rootUUID = jsonObject.value("rootUUID").toString();
        destUUID = jsonObject.value("destUUID").toString();
        remark = jsonObject.value("remark").toString();
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const SystemBackupRequest &req);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, SystemBackupRequest &req);
};
Q_DECLARE_METATYPE(SystemBackupRequest)

struct SystemRestoreRequest {
    QString username;
    QString operateID;
    BackupInfo backupInfo;

    QJsonObject marshal() const {
        QJsonObject jsonObject;
        jsonObject.insert("username", username);
        jsonObject.insert("operateID", operateID);
        QJsonObject backupObject = backupInfo.marshal();
        jsonObject.insert("backupInfo", backupObject);
        return jsonObject;
    }

    void unmarshal(QJsonObject &jsonObject) {
        username = jsonObject.value("username").toString();
        operateID = jsonObject.value("operateID").toString();
        QJsonObject backupObject = jsonObject.value("backupInfo").toObject();
        backupInfo.unmarshal(backupObject);
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const SystemRestoreRequest &req);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, SystemRestoreRequest &req);
};
Q_DECLARE_METATYPE(SystemRestoreRequest)

struct UserDataBackupRequest {
    QString username;
    QString rootUUID;
    QString destUUID;
    QString remark;
    QString operateID;
    QStringList exclude;
    QStringList filesFrom;
    QString strTotalSizeBytes;

    QJsonObject marshal() const {
        QJsonObject jsonObject;
        jsonObject.insert("username", username);
        jsonObject.insert("rootUUID", rootUUID);
        jsonObject.insert("destUUID", destUUID);
        jsonObject.insert("remark", remark);
        jsonObject.insert("operateID", operateID);
        QJsonArray array;
        for (auto &i : exclude) {
            array.append(i);
        }
        jsonObject.insert("exclude", array);

        QJsonArray filesFromArray;
        for (auto &i : filesFrom) {
            filesFromArray.append(i);
        }
        jsonObject.insert("filesFrom", filesFromArray);
        jsonObject.insert("strTotalSizeBytes", strTotalSizeBytes);
        return jsonObject;
    }

    void unmarshal(QJsonObject &jsonObject) {
        username = jsonObject.value("username").toString();
        rootUUID = jsonObject.value("rootUUID").toString();
        destUUID = jsonObject.value("destUUID").toString();
        remark = jsonObject.value("remark").toString();
        operateID = jsonObject.value("operateID").toString();
        QJsonArray array = jsonObject.value("exclude").toArray();
        for (auto a : array) {
            exclude.append(a.toString());
        }

        QJsonArray filesFromArray = jsonObject.value("filesFrom").toArray();
        for (auto a : filesFromArray) {
            filesFrom.append(a.toString());
        }
        strTotalSizeBytes = jsonObject.value("strTotalSizeBytes").toString();
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const UserDataBackupRequest &req);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, UserDataBackupRequest &req);
};
Q_DECLARE_METATYPE(UserDataBackupRequest)

struct UserDataRestoreRequest {
    QString username;
    QString operateID;
    BackupInfo backupInfo;
    QJsonObject marshal() const {
        QJsonObject jsonObject;
        jsonObject.insert("username", username);
        jsonObject.insert("operateID", operateID);
        QJsonObject backupObject = backupInfo.marshal();
        jsonObject.insert("backupInfo", backupObject);
        return jsonObject;
    }

    void unmarshal(QJsonObject &jsonObject) {
        username = jsonObject.value("username").toString();
        operateID = jsonObject.value("operateID").toString();
        QJsonObject backupObject = jsonObject.value("backupInfo").toObject();
        backupInfo.unmarshal(backupObject);
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const UserDataRestoreRequest &req);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, UserDataRestoreRequest &req);
};
Q_DECLARE_METATYPE(UserDataRestoreRequest)

typedef UserDataRestoreRequest RemoveUserDataBackupRequest;

struct SystemCloneRequest {
    QString username;
    QString operateID;
    QString destPath;
    QString destUUID;
    QString relativePath;
    int operateType; // OperateType
    quint64 totalSize; // bytes

    QJsonObject marshal() const {
        QJsonObject jsonObject;
        jsonObject.insert("username", username);
        jsonObject.insert("operateID", operateID);
        jsonObject.insert("destPath", destPath);
        jsonObject.insert("destUUID", destUUID);
        jsonObject.insert("relativePath", relativePath);
        jsonObject.insert("operateType", operateType);
        jsonObject.insert("totalSize", QString("%1").arg(totalSize));
        return jsonObject;
    }

    void unmarshal(QJsonObject &jsonObject) {
        username = jsonObject.value("username").toString();
        operateID = jsonObject.value("operateID").toString();
        destPath = jsonObject.value("destPath").toString();
        destUUID = jsonObject.value("destUUID").toString();
        relativePath = jsonObject.value("relativePath").toString();
        if (jsonObject.contains("operateType")) {
            operateType = jsonObject.value("operateType").toInt(static_cast<int>(OperateType::Invalid));
        } else {
            operateType = static_cast<int> (OperateType::Invalid);
        }

        if (jsonObject.contains("totalSize")) {
            totalSize = jsonObject.value("totalSize").toString().toULongLong();
        } else {
            totalSize = 0;
        }
    }
    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const SystemCloneRequest &req);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, SystemCloneRequest &req);
};
Q_DECLARE_METATYPE(SystemCloneRequest)

static const int LOG_MASK_BEGIN_TIME = 0x01;
static const int LOG_MASK_END_TIME = 0x02;
static const int LOG_MASK_OPERATE_TYPE = 0x04;
static const int LOG_MASK_USER_NAME = 0x08;
static const int LOG_MASK_REMARK = 0x10;

struct OperateLogQuery {
    int mask = -1;
    quint64 beginTime = 0;
    quint64 endTime = 0;
    int operateType; // OperateType
    QString username;
    QString remark;
    bool orderByTimeDesc = true;

    QJsonObject marshal() const {
        QJsonObject jsonObject;
        jsonObject.insert("mask", mask);
        jsonObject.insert("beginTime", QString("%1").arg(beginTime));
        jsonObject.insert("endTime", QString("%1").arg(endTime));
        jsonObject.insert("operateType", operateType);
        jsonObject.insert("username", username);
        jsonObject.insert("remark", remark);
        jsonObject.insert("orderByTimeDesc", orderByTimeDesc);
        return jsonObject;
    }

    void unmarshal(QJsonObject &jsonObject) {
        mask = jsonObject.value("mask").toInt();
        beginTime = jsonObject.value("beginTime").toString().toULongLong();
        endTime = jsonObject.value("endTime").toString().toULongLong();
        operateType = jsonObject.value("operateType").toInt();
        username = jsonObject.value("username").toString();
        remark = jsonObject.value("remark").toString();
        orderByTimeDesc = jsonObject.value("orderByTimeDesc").toBool();
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const OperateLogQuery &req);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, OperateLogQuery &req);
};
Q_DECLARE_METATYPE(OperateLogQuery)

struct OperateLog {
    quint64 time = 0;
    OperateType operateType;
    QString username;
    OperateStatus status;
    QString remark;
    QString reserve;

    QJsonObject marshal() const {
        QJsonObject jsonObject;
        jsonObject.insert("time", QString("%1").arg(time));
        jsonObject.insert("operateType", static_cast<int> (operateType));
        jsonObject.insert("username", username);
        jsonObject.insert("status", status);
        jsonObject.insert("remark", remark);
        jsonObject.insert("reserve", reserve);
        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject) {
        time = jsonObject.value("time").toString().toULongLong();
        operateType = static_cast<OperateType>(jsonObject.value("operateType").toInt());
        username = jsonObject.value("username").toString();
        status = static_cast<OperateStatus>(jsonObject.value("status").toInt());
        remark = jsonObject.value("remark").toString();
        reserve = jsonObject.value("reserve").toString();
    }
};

typedef QList<OperateLog> OperateLogList;

struct GhostLog {
    quint64 time = 0;
    OperateType operateType;
    QString operateID;
    QString userName;
    OperateStatus status;
    QString deviceUUID;
    QString path;
    QString reserve;
    bool isRemoveable;

    QJsonObject marshal() const {
        QJsonObject jsonObject;
        jsonObject.insert("time", QString("%1").arg(time));
        jsonObject.insert("operateType", static_cast<int> (operateType));
        jsonObject.insert("operateID", operateID);
        jsonObject.insert("userName", userName);
        jsonObject.insert("status", status);
        jsonObject.insert("deviceUUID", deviceUUID);
        jsonObject.insert("path", path);
        jsonObject.insert("reserve", reserve);
        jsonObject.insert("isRemoveable", isRemoveable);
        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject) {
        time = jsonObject.value("time").toString().toULongLong();
        operateType = static_cast<OperateType>(jsonObject.value("operateType").toInt());
        operateID = jsonObject.value("operateID").toString();
        userName = jsonObject.value("userName").toString();
        status = static_cast<OperateStatus>(jsonObject.value("status").toInt());
        deviceUUID = jsonObject.value("deviceUUID").toString();
        path = jsonObject.value("path").toString();
        reserve = jsonObject.value("reserve").toString();
        isRemoveable = jsonObject.value("isRemoveable").toBool();
    }
};
typedef QList<GhostLog> GhostLogList;

struct GhostLogQuery {
    int mask = -1;
    quint64 beginTime = 0;
    quint64 endTime = 0;
    int operateType; // OperateType
    QString userName;
    bool orderByTimeDesc = true;

    QJsonObject marshal() const {
        QJsonObject jsonObject;
        jsonObject.insert("mask", mask);
        jsonObject.insert("beginTime", QString("%1").arg(beginTime));
        jsonObject.insert("endTime", QString("%1").arg(endTime));
        jsonObject.insert("operateType", operateType);
        jsonObject.insert("userName", userName);
        jsonObject.insert("orderByTimeDesc", orderByTimeDesc);
        return jsonObject;
    }

    void unmarshal(QJsonObject &jsonObject) {
        mask = jsonObject.value("mask").toInt();
        beginTime = jsonObject.value("beginTime").toString().toULongLong();
        endTime = jsonObject.value("endTime").toString().toULongLong();
        operateType = jsonObject.value("operateType").toInt();
        userName = jsonObject.value("userName").toString();
        orderByTimeDesc = jsonObject.value("orderByTimeDesc").toBool();
    }

    static void registerMetaType();
    friend QDBusArgument &operator<<(QDBusArgument &arg, const GhostLogQuery &req);
    friend const QDBusArgument &operator>>(const QDBusArgument &arg, GhostLogQuery &req);
};
Q_DECLARE_METATYPE(GhostLogQuery)

static QString getRecoveryType(RecoveryType recoveryType)
{
    static QMap<RecoveryType, QString> all = {
            {RecoveryType::Rsync, "rsync"},
            {RecoveryType::OSTree, "ostree"},
            {RecoveryType::BtrfsSnapshot, "btrfsSnapshot"},
            {RecoveryType::LvmSnapshot, "LvmSnapshot"},
            {RecoveryType::RsyncV20, "RsyncV20"}
    };

    if (all.contains(recoveryType)) {
        return all.value(recoveryType);
    }

    return QString("");
}

struct Response {
    int retCode;
    int progress;
    int remainSecond;
    int ostreeOperate;
    OperateType operateType; // OperateType
    QString errMsg;
    QString data;
    QString operateID;
    QString errCodeStr;

    Response() : retCode(-1), progress(0), remainSecond(0), ostreeOperate(-1), operateType(OperateType::Invalid)
        ,errMsg(""), data(""), operateID(""), errCodeStr("")
    {}

    Response(int code, int progress, int remainSec, int ostreeOp, OperateType opType,
             const QString &err, const QString &resultData, const QString &opID, const QString &errStr)
            : retCode(code)
            , progress(progress)
            , remainSecond(remainSec)
            , ostreeOperate(ostreeOp)
            , operateType(opType)
            , errMsg(err)
            , data(resultData)
            , operateID(opID)
            , errCodeStr(errStr)
    {}

    QJsonObject marshal() const
    {
        QJsonObject jsonObject;
        jsonObject.insert("retCode", retCode);
        jsonObject.insert("progress", progress);
        jsonObject.insert("remainSecond", remainSecond);
        jsonObject.insert("ostreeOperate", ostreeOperate);
        jsonObject.insert("operateType", static_cast<int> (operateType));
        jsonObject.insert("errMsg", errMsg);
        jsonObject.insert("data", data);
        jsonObject.insert("operateID", operateID);
        jsonObject.insert("errCodeStr", errCodeStr);

        return jsonObject;
    }

    void unmarshal(const QJsonObject &jsonObject)
    {
        if (jsonObject.contains("retCode")) {
            retCode = jsonObject.value("retCode").toInt(-1);
        }

        if (jsonObject.contains("progress")) {
            progress = jsonObject.value("progress").toInt(0);
        }

        if (jsonObject.contains("remainSecond")) {
            remainSecond = jsonObject.value("remainSecond").toInt(0);
        }

        if (jsonObject.contains("ostreeOperate")) {
            ostreeOperate = jsonObject.value("ostreeOperate").toInt(-1);
        }

        if (jsonObject.contains("operateType")) {
            operateType = static_cast<OperateType> (jsonObject.value("operateType").toInt(-1));
        }

        if (jsonObject.contains("errMsg")) {
            errMsg = jsonObject.value("errMsg").toString();
        }

        if (jsonObject.contains("data")) {
            data = jsonObject.value("data").toString();
        }

        if (jsonObject.contains("operateID")) {
            operateID = jsonObject.value("operateID").toString();
        }

        if (jsonObject.contains("errCodeStr")) {
            errCodeStr = jsonObject.value("errCodeStr").toString();
        }
    }
};
