//
// Created by uos on 2022/2/16.
//

#ifndef UOS_RECOVERY_DEVICE_H
#define UOS_RECOVERY_DEVICE_H

#include <QString>
#include <QSharedPointer>
#include <QList>
#include <QMap>
#include <QJsonObject>
#include <QJsonArray>

struct DeviceInfo {
    QString deviceName = "";
    QString name = "";
    QString kname = "";
    QString pkname = "";
    QString path = "";
    QString mappedName = "";
    QString uuid = "";
    QString label = "";
    QString partuuid = "";
    QString partLabel = "";
    QString type = ""; // disk, part, crypt, loop, rom, lvm
    QString fsType = ""; // iso9660, ext4, btrfs, ...
    quint64 fsSizeBytes = 0;
    quint64 sizeBytes = 0;
    quint64 usedBytes = 0;
    quint64 availableBytes = 0;
    quint64 logSectorSize = 0;
    quint64 phySectorSize = 0;
    QString usedPercent = "";
    QString mountPoint = "";
    QString mode = "";
    QString majMin = ""; // major:minor device number
    int major = -1;
    int minor = -1;
    int rota = -1;
    bool readOnly = false;
    bool removable = false;
    QString vendor = "";
    QString serial = "";
    QString revision = "";
    QString deviceByUUID = "";
    QString deviceByLabel = "";
    QString deviceByPartUUID = "";
    QString deviceByPartLabel = "";
    QString symlink = "";
    QString diskName = ""; // get from smartctl
    QSharedPointer<DeviceInfo> parent;
    QList<QSharedPointer<DeviceInfo>> children;
};
typedef QSharedPointer<DeviceInfo> DeviceInfoPtr;
typedef QList<DeviceInfoPtr> DeviceInfoList;
int deviceInfoCompareMore (const DeviceInfoPtr left, const DeviceInfoPtr right);
int deviceInfoTotalSizeCompare(const DeviceInfoPtr &left, const DeviceInfoPtr &right);

enum class  EPartitionType {
    PartitionTypeInvalid = -1,
    PartitionTypeLogical = 0,
    PartitionTypeExtension,
    PartitionTypePrimary
};

// uos-recovery: 6.0.15, update: 6.0.15+u021
// oem uos-recovery: 6.0.15.1, update: 6.0.15.1+u021
struct AppVersion {
    int major = -1;
    int minor = -1;
    int patch = -1;
    int build = -1;
    int update = -1;

    bool operator<(const AppVersion &version);
    AppVersion& operator=(const AppVersion &version);
    QJsonObject marshal();
    void unmarshal(const QJsonObject &jsonObject);
    static AppVersion getAppVersion();
};

struct PartitionInfo {
    QString deviceName = "";
    QString kName = "";
    QString pkName = "";
    QString uuid = "";
    QString label = "";
    QString type = "";
    QString fsType = "";
    QString mountPoint = "";
    quint64 totalSizeBytes = 0;
    quint64 usedSizeBytes = 0;
    int logSectorSize = 0;
    int phySectorSize = 0;
    int rota = -1;
    bool isEncrypt = false;
    bool isLvm = false;

    QJsonObject marshal();
    void unmarshal(const QJsonObject &jsonObject);
};

struct SysDirSizeInfo {
    quint64 sysRoot;
    quint64 sysRootb;
    quint64 efi;
    quint64 rootDir;
    quint64 home;
    quint64 opt;
    quint64 var;
    quint64 tmp;
    quint64 boot;
    quint64 srv;
    quint64 usrLocal;
    quint64 persistent;

    QJsonObject marshal();
    void unmarshal(const QJsonObject &jsonObject);
};

struct GhostDiskInfo {
    QString deviceName = "";
    QString vendor = "";
    QString mode = "";
    quint64 totalSizeBytes = 0; // disk total size
    quint64 allocTotalSizeBytes = 0;  // manual partition, allocTotalSizeBytes maybe less than totalSizeBytes
    quint64 usedSizeBytes = 0;
    quint64 lastPartitionTotalSizeBytes = 0;
    quint64 recoveryPartitionUsedSizeBytes = 0;
    quint64 curSysRootUsedSizeBytes = 0;
    quint64 anotherSysRootUsedSizeBytes = 0;
    quint64 efiUsedSizeBytes = 0;
    int mediaType = 0; // 0: HDD; 1: SDD;
    bool isEncrypt = false;
    bool isLvm = false;
    bool isVirtualDisk = false;
    bool isRemovable = false;
    bool isCurRoot = false;
    bool isRootb = false;
    bool isEfi = false;
    QJsonArray partitionList;

    QJsonObject marshal();
    void unmarshal(const QJsonObject &jsonObject);

    bool operator<(const GhostDiskInfo &diskInfo);
    QList<PartitionInfo> getPartitionList();
};

struct SystemUpgradeInfo {
    QString rootUuid = "";
    QString rootPath = "";
    QString mountPoint = "";
    bool isSystemUpgrade = false;

    QJsonObject marshal();
    void unmarshal(const QJsonObject &jsonObject);
};

struct FstabItem {
    explicit FstabItem();
    explicit FstabItem(const QString &devUUID, const QString &mnt, const QString &devType, const QString &option,
                       const QString &devDump, const QString &devPass);
    FstabItem& operator=(const FstabItem &other);

    QString device = "";
    QString mountPoint = "";
    QString type = "";
    QString options = "";
    QString dump = "";
    QString pass = "";

    QJsonObject marshal();
    void unmarshal(const QJsonObject &jsonObject);
};

struct GhostInfo {
    QString platform= "";
    QString hardwareType = "";
    QString sysProductName = "";
    QString huaweiTypeAlias = "";
    QString cpuModel = "";
    QString displayDriver = "";
    QJsonObject systemUpgradeInfo;
    QJsonObject ghostVersion;
    QJsonArray diskInfo;
    QJsonArray fstab;
    QJsonObject dirSizeObj;
    quint64 diskSize = 0;
    bool isEFI = false;
    bool isFullInstall = false;
    QString initOptApps; // eg: /opt/apps/org.deepin.scanner;/opt/deepin-defender-vdbup

    QJsonObject marshal();
    void unmarshal(const QJsonObject &jsonObject);

    SystemUpgradeInfo getSystemUpgradeInfo();
    AppVersion getGhostVersion();
    QList<GhostDiskInfo> getGhostDiskInfo() const;
    QList<FstabItem> getFstabItemList();
};

struct SPartitionInfo
{
    SPartitionInfo() : id(""),
                       filesystem(""),
                       mountPoint(""),
                       label(""),
                       device(""),
                       path(""),
                       partitionType(EPartitionType::PartitionTypeInvalid),
                       startPoint(-1),
                       endPoint(-1),
                       size(-1),
                       sectorSize(-1),
                       phySectorSize(-1),
                       deviceSize(-1),
                       index(-1),
                       usage(false),
                       isStartPoint(false),
                       isEFIPartition(false),
                       isMergeVG(false)
    {}

    QString id;             // 前端生成唯一标识，用于前后端界面操作时判断的标识符
    QString filesystem;     // 文件系统
    QString mountPoint;     // 挂载点
    QString label;          // 分区盘标
    QString device;         // 分区所在的设备
    QString path;           // 分区的路径
    EPartitionType partitionType;    // 分区的类型
    qint64 startPoint;      // 起始位置
    qint64 endPoint;        // 终止位置
    qint64 size;            // 分区大小, 单位M
    qint64 sectorSize;      // 逻辑扇区大小
    qint64 phySectorSize;   // 物理扇区大小
    qint64 deviceSize;      // 分区所对应的设备的size
    int index;              // 分区索引号，用于删除分区时的参数
    bool usage;             // 是否是一个已经分区的空间
    bool isStartPoint;      // 开始位置还是结束位置进行分区
    bool isEFIPartition;    // 是否是EFI分区
    bool isMergeVG;         // 标记该分区是否创建合成VG

    bool operator==(const SPartitionInfo &info) const {return this->id == info.id;}
    // 降序排序
    bool operator<(const SPartitionInfo &info) const {return this->startPoint < info.startPoint;}
};

struct SVGInfo
{
    QString id;
    QString name;
    QStringList pvIds;
    qint64 size = 0;        // VG大小，单位M

    bool operator==(const SVGInfo &info) const {return this->id == info.id;}
    bool operator<(const SVGInfo &info) const {return this->name < info.name;}
};

struct SLVMInfo
{
    QString id;             // 前端生成分区唯一标识，用于前后端界面操作分区时判断的标识符
    QString filesystem;
    QString mountPoint;
    QString label;
    qint64 size = 0;        // LVM大小，单位M
    QString vgName;
    bool usage;             // 是否是一个已经分区的空间

    bool operator==(const SLVMInfo &info) const {return this->id == info.id;}
};

//磁盘信息
class Device
{
public:
    explicit Device();
    explicit Device(const QString &devNameOrUUID);
    virtual ~Device();
    static DeviceInfoList getDeviceByLsblk();
    static QString getLsblkByOutput();
    static DeviceInfoList getDeviceInfoByLsblkOut(const QString &lsblkOut);
    static DeviceInfoList getAllDeviceByLsblk();
    static QStringList getSysDevicesByFstab(const QString &fstabFile);
    static bool exportPartitionInfoByLsblk(const QString &filepath, const QStringList &deviceList, QString &err);
    static void calculateDiskSpace();
    static bool getMountPointByUUID(const QString &uuid, QString &mountPointPath);
    static bool getUUIDByPartitionPath(const QString &path, QString &uuid);
    static bool getAllMediaMountPoint(QList<QString> &mountPointList);
    static bool isDeviceMountedReadOnly(const QString &path);
    static bool isFsTypeSupported(const QString &fsType); // 是否支持系统备份
    static bool isFsTypeSupportedDataBackup(const QString &fsType); // 是否支持数据备份
    static bool isFsTypeSupportedGhost(const QString &fsType);
    static bool findSuitablePartition(quint64 dimTotalBytes, QString &partitionMountPoint, quint64 thresholdBytes,
           bool removable = false);
    static bool getVGNames(QStringList &vgNameList);
    static bool getPvDisplay(QMap<QString, QString> &pvVgMaps);
    static bool getLvPathsByVgName(const QString &vgName, QStringList &lvPathList);
    static bool lvRemove(const QString &lvPath);
    static bool deleteLVM(const QStringList &devices, const QMap<QString, QString> &pvVgMaps);
    static bool vgRemove(const QString &vgName);
    static bool deleteVG(const QStringList &devices, const QMap<QString, QString> &pvVgMaps);
    static bool renameVG(const QStringList &sysVgNameList, QMap<QString, QString> &vgMap);
    static void getAllTypeDevice(DeviceInfoList &diskList, DeviceInfoList &cryptDevList,
           DeviceInfoList &noCryptDevList, DeviceInfoList &partitionList, DeviceInfoList &lvmList,
           DeviceInfoList &fileCryptDevList, DeviceInfoList &dmDevList);
    static bool isMultiFullDiskEncrypt(const QString &fstabFile);
    static bool isFullDiskInstall(const QString &fstabFile, QStringList &sysMountPointList);
    // fill disk parent and children
    static DeviceInfoList getOrganizedDiskList(DeviceInfoList &diskList, DeviceInfoList &cryptDevList,
           DeviceInfoList &noCryptDevList, DeviceInfoList &partitionList, DeviceInfoList& lvmList,
           DeviceInfoList &fileCryptDevList, DeviceInfoList &dmDevList);
    static void getGhostDiskInfo(const QString &fstabFile, QList<GhostDiskInfo> &diskInfoList);
    static void fillGhostDiskInfo(const DeviceInfoPtr &devInfoPtr, GhostDiskInfo &diskInfo);
    bool mount(const QString &mountPoint);
    bool umount();
    void getDeviceByName(const QString &name);
    void getDeviceByUUID(const QString &uuid);
    QString size();
    QString used();
    QString free();
    DeviceInfoPtr getDeviceInfo();
    static bool isLVM(const QStringList &sysUUIDList, bool &isEncrypted, bool &isFileMgrEncrypted, DeviceInfoList &fileCryptDevList);
    bool operator < (const QSharedPointer<Device> other) {
        if (m_pDevice->availableBytes < other->getDeviceInfo()->availableBytes) {
            return true;
        }
        return false;
    }
private:
    static void findChildByDmsetup(DeviceInfoList &deviceList);
    static DeviceInfoPtr findDevice(const QString &devAilas);
    static bool isLinuxFilesystem(const DeviceInfoPtr &dev);
    static bool isDisk(const DeviceInfoPtr &dev);
    static SPartitionInfo newPartitionByJObj(const QString &deviceName, const QJsonObject &partitionInfoObj);
    // 针对每个设备更新其下分区起止位置信息
    static bool updatePartStartEndPoint(const QString &deviceInfo, QList<SPartitionInfo> &partitionInfos);
    // 根据fstab将当前系统用到的设备信息选取出来
    static bool updateDeviceInfoMapByFstab(QMap<QString, QList<SPartitionInfo>> &devicesInfos);
    // 使用parted针对每个设备更新其下分区的Type
    static bool updatePartType(const QString &deviceInfo, QList<SPartitionInfo> &partitionInfos);
    //
    static void updateLvmInfos(QMap<QString, QList<SPartitionInfo> > devs, QMap<QString, SVGInfo> &vgNames, QMap<QString, SLVMInfo> &lvNames, QMap<SVGInfo, QList<SLVMInfo>> &lvmInfos);
    // 使用lsblk命令获取json格式的设备信息
    static bool getVGNamesInfo(QMap<QString, SVGInfo> &vgNames, QString &err);
    // 更新分区信息，判断该分区是否在vg中
    static void updatePartIsMergeVG(QMap<QString, SVGInfo> &vgNames, SPartitionInfo &partitionInfo);
    static void updateVGNames(const QJsonObject &partInfoObj, QMap<QString, SVGInfo> &vgNames, QMap<QString, SLVMInfo> &lvNames);
    static bool getLVMPESize(const QString &vgName, qint64 &peSize, QString &err);
    // 将磁盘分区信息保存到文件中
    static bool writeDeviceInfo(const QString &filepath, const QMap<QString, QList<SPartitionInfo>> &devicesInfos, QMap<SVGInfo, QList<SLVMInfo>> &lvmInfos);

private:
    DeviceInfoPtr m_pDevice;
    static DeviceInfoList m_allDevice;
};

typedef QSharedPointer<Device> DevicePtr;
typedef QList<DevicePtr> DeviceList;

int deviceCompare (const DevicePtr left, const DevicePtr right);

bool deviceInfoChildrenCompare(const DeviceInfoPtr left, const DeviceInfoPtr right);

#endif //UOS_RECOVERY_DEVICE_H
