#include "DimFileTransTask.h"
#include "utils/Utils.h"
#include <QFile>
#include <QJsonObject>
#include <QJsonArray>
#include <QDir>
#include <QDebug>

DimFileTransTask::DimFileTransTask()
{
    m_readResult = new QTimer(this);
    connect(m_readResult, &QTimer::timeout, this, &DimFileTransTask::readResultSlot);
    connect(this, &QThread::finished, this, &DimFileTransTask::taskFinishedSlot);
}

DimFileTransTask::~DimFileTransTask()
{
}

bool DimFileTransTask::buildArgumentsForBackup()
{
    if (m_tranStep == ReadDimFile) {
        return beforeReadDimFile();
    } else {
        return beforeSquashfsImgFile();
    }
}

bool DimFileTransTask::buildArgumentsForRestore(const QString &fromDir)
{
    return true;
}

void DimFileTransTask::doResult()
{

}

bool DimFileTransTask::doTrans(const QString &dimFile, const QString &destPath, const QString &loopDevice)
{
    QJsonObject jsonObject;

    // 检查源目录是否存在
    QFile dimFileToTreat(dimFile);
    if (!dimFileToTreat.exists()) {
        QString err = "Other restore file : " + dimFile + " do not exists ";
        jsonObject.insert("errMsg", err);
        Q_EMIT error(jsonObject);
        return false;
    }

    m_destPath = destPath;
    m_dimFile = dimFile;
    QString dimName = dimFile.right(dimFile.length() - dimFile.lastIndexOf("/") - 1) + ".img";
    if (destPath.contains(" ")) {
        QString destPathTmp = destPath;
        destPathTmp.replace("\"", "");
        qInfo()<<"doTrans destPathTmp = "<<destPathTmp;
        m_imgFile = QString("\"%1\"").arg(destPathTmp + "/" + dimName);
        m_squashfsImgFile = QString("%1\"").arg(m_imgFile.left(m_imgFile.lastIndexOf(".")) + ".squashfs");
        m_loopDevMountPath = QString("%1\"").arg(m_imgFile.left(m_imgFile.lastIndexOf(".")) + "loop");
    } else {
        m_imgFile = destPath + "/" + dimName;
        m_squashfsImgFile = m_imgFile.left(m_imgFile.lastIndexOf(".")) + ".squashfs";
        m_loopDevMountPath = m_imgFile.left(m_imgFile.lastIndexOf(".")) + "loop";
    }
    m_loopDevice = loopDevice;
    qInfo()<<"doTrans dimFile = "<<dimFile<<", destPath = "<<destPath<<", m_imgFile = "<<m_imgFile;
    qInfo()<<"doTrans m_squashfsImgFile = "<<m_squashfsImgFile <<", m_loopDevMountPath = "<<m_loopDevMountPath;

    m_currentProgressValue = 1;
    m_tranStep = ReadDimFile;

    // 使用deepin-clone将dim文件内容解压到与loop设备的绑定的img文件中
    m_cmd = "sudo";
    m_args.clear();
    m_args.append("deepin-clone");
    m_args.append("--tui");
    m_args.append(m_dimFile);
    m_args.append(m_loopDevice);
    m_readResult->start(1000);
    start();

    return true;
}

void DimFileTransTask::readResultSlot()
{
    if (m_process == nullptr) {
        qInfo() << "process is : not";
        return;
    }

    // 解析进度信息
    QString line = m_process->readAll();
    if (m_tranStep == ReadDimFile) {
        QString findStr = "total progress:";
        int processPos = line.indexOf(findStr);
        if (processPos != -1) {
            int linePos = line.indexOf("----", processPos);
            line = line.left(linePos);
            line = line.right(line.length() - processPos - findStr.length());
            line.replace(" ", "");
            qInfo() << "Other restore cmd out : " << line;
            m_currentProgressValue = static_cast<int>(line.toDouble()) / 2;
        }
    } else {
        int processPos = line.indexOf("]");
        if (processPos != -1) {
            line.replace("\t", "");
            line.replace("\n", "");
            line = line.right(line.length() - processPos);
            line = line.right(line.length() - line.lastIndexOf(" "));
            QString processValue = line.left(line.indexOf("\%"));
            m_currentProgressValue = 50 + (processValue.toInt() / 2);
        }
    }

    QJsonObject valueObj;
    valueObj["progress"] = m_currentProgressValue;
    Q_EMIT progressChanged(valueObj);
}

void DimFileTransTask::taskFinishedSlot()
{
    if (m_tranStep == ReadDimFile) {
        m_tranStep = SquashfsImg;

        int cpuCores = Utils::getCPUCores();
        if (cpuCores > 1) {
            cpuCores--;
        }

        qInfo()<<"DimFileTransTask::taskFinishedSlot, m_loopDevMountPath = "<<m_loopDevMountPath;
        qInfo()<<"DimFileTransTask::taskFinishedSlot, m_squashfsImgFile = "<<m_squashfsImgFile;
        QString destPath = m_destPath;
        if (m_destPath.contains(" ")) {
            destPath.replace("\"", "");
        }
        QString loopPath = m_loopDevMountPath;
        QString squashfsImgFile = m_squashfsImgFile;
        if (loopPath.contains(" ")) {
            loopPath.replace("\"", "");
        }

        if (squashfsImgFile.contains(" ")) {
            squashfsImgFile.replace("\"", "");
        }
        qInfo()<<"destPath = "<<destPath<<", loopPath = "<<loopPath<<", squashfsImgFile = "<<squashfsImgFile;

        // 处理执行块设备上文件系统压缩squashfs的操作
        m_cmd = QString("mksquashfs");
        m_args.clear();
        m_args.append(loopPath);
        m_args.append(squashfsImgFile);
        m_args.append("-processors");
        m_args.append(QString("%1").arg(cpuCores));
        m_args.append("-mem");
        m_args.append("512M");
        start();
    } else {
        m_readResult->stop();
        m_currentProgressValue = 100;

        afterSquashfsImgFile();

        QJsonObject jsonObject;
        jsonObject["progress"] = m_currentProgressValue;

        int errCode = m_process->exitCode();
        if (m_process->exitStatus() != QProcess::NormalExit || errCode != 0) {
            QString err = QString("Other restore deepin-clone error cmd : deepin-clone --tui %1 %2 exitCode : %3 err : %4")
                    .arg(m_dimFile)
                    .arg(m_loopDevice)
                    .arg(errCode)
                    .arg(QString(m_process->readAllStandardError()));

            jsonObject.insert("errMsg", err);
            Q_EMIT error(jsonObject);
        } else {
            jsonObject.insert("imgFilPath", m_imgFile);
            Q_EMIT success(jsonObject);
        }
    }
}

void DimFileTransTask::readStandardOutput()
{
    AsyncTask::readStandardOutput();
}

void DimFileTransTask::readAllStandardError()
{
    AsyncTask::readAllStandardError();
}

bool DimFileTransTask::beforeReadDimFile()
{
    QJsonObject jsonObject;
    qint64 testFileSize = 0;
    QString err = "";
    // 使用deepin-clone命令获取需要的实际空间大小
    QJsonObject dimFileJsonInfo;
    if (!Utils::getDimFileJsonInfo(m_dimFile, dimFileJsonInfo, err)) {
        qWarning() << "beforeReadDimFile error getDimFileJsonInfo failed ! err : " << err;
        return false;
    }
    testFileSize = dimFileJsonInfo.value("totalSize").toVariant().toLongLong();
    qInfo() << "filesize is : "<< Utils::byte2DisplaySize(testFileSize);

    // 此处使用fallocate代替dd，因为dd速度太慢
    QString cmd = QString("fallocate -l %1 %2").arg(testFileSize).arg(m_imgFile);
    QString cmdLog = "";
    err = "";
    QString strFileSize = QString("%1").arg(testFileSize);
    QStringList args;
    args<<"-l"<<strFileSize<<m_imgFile;
    if (!Process::spawnCmd("fallocate", args, cmdLog, err)) {
        qWarning() << "beforeReadDimFile error ! cmd : " + cmd + " err : " + err;
        return false;
    }

    // 绑定img文件，并激活loop设备
    cmd = QString("losetup %1 %2").arg(m_loopDevice).arg(m_imgFile);
    cmdLog = "";
    err = "";
    if (!Process::spawnCmd("losetup", {m_loopDevice, m_imgFile}, cmdLog, err)) {
        // 判断一下loop设备是否已挂载起来，未挂载起来则返回false
        QString out;
        Process::spawnCmd("lsblk", args,out, err);
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
        QStringList outList = out.split("\n", Qt::SkipEmptyParts);
#else
        QStringList outList = out.split("\n", QString::SkipEmptyParts);
#endif
        bool existLoopDev = false;
        for (QString &line : outList) {
            if (m_loopDevice == line) {
                existLoopDev = true;
                break;
            }
        }
        if (!existLoopDev) {
            return false;
        }
    }

    // 格式化loop设备
    QJsonArray childrenPartList = dimFileJsonInfo.value("childrenPartList").toArray();
    if (childrenPartList.size() == 1) {
        if (!Utils::getFormatPartitionCmd("", childrenPartList.first().toObject().value("fsTypeName").toString(), m_loopDevice, cmd, err)) {
            qWarning() << "beforeReadDimFile error ! getFormatPartitionCmd err : " << err;
            return false;
        }
    } else {
        cmd = QString("mkfs.ext4 -F %1").arg(m_loopDevice);
    }

    cmdLog = "";
    err = "";
    if (!Process::spawnCmd("mkfs.ext4", {"-F", m_loopDevice}, cmdLog, err)) {
        qWarning() << "beforeReadDimFile error ! cmd : " + cmd + " err : " + err;
        return false;
    }

    return true;
}

bool DimFileTransTask::beforeSquashfsImgFile()
{
    QJsonObject dimFileJsonInfo;
    QString err = "";
    if (!Utils::getDimFileJsonInfo(m_dimFile, dimFileJsonInfo, err)) {
        qWarning() << "beforeSquashfsImgFile error getDimFileJsonInfo failed ! err : " << err;
        return false;
    }

    // 获取所有分区信息
    QJsonArray allPartJsonArrayInfo;
    err = "";
    QStringList args;
    args<<"-lpOJ";
    if (!Utils::getLsblkJsonReturn(args, allPartJsonArrayInfo, err)) {
        qWarning() << "beforeSquashfsImgFile error getLsblkJsonReturn failed ! err : " << err;
        return false;
    }
    QList<QJsonObject> allPartJsonObjInfo;
    for (int i = 0; i < allPartJsonArrayInfo.size(); i++) {
        allPartJsonObjInfo.append(allPartJsonArrayInfo.at(i).toObject());
    }

    QList<QStringList> mountStrList;
    QJsonArray childrenPartList = dimFileJsonInfo.value("childrenPartList").toArray();
    for (int i = 0; i < childrenPartList.size(); i++) {
        QJsonObject childrenPartObj = childrenPartList.at(i).toObject();

        // 根据路径获取分区对应的挂载点信息
        QString partPath = childrenPartObj.value("name").toString();
        auto partInfoIter = std::find_if(allPartJsonObjInfo.begin(), allPartJsonObjInfo.end(), [=](QJsonObject partInfo) {
            return (!partInfo.value("name").toString().compare(partPath));
        });

        if (partInfoIter == allPartJsonObjInfo.end()) {
            qWarning() << "beforeSquashfsImgFile error ! get mountpoint key value failed";
            return false;
        }

        // 过滤掉交换分区
        if ((*partInfoIter).value("fstype").toString().contains("swap")) {
            continue;
        }

        QString partMountPoint = (*partInfoIter).value("mountpoint").toString();
        if (partMountPoint.isEmpty()) {
            partMountPoint = "/tmpPoint" + partPath.right(partPath.length() - partPath.lastIndexOf("/") - 1);
        }

        // 根据uuid获取分区对应的loop设备路径
        QString partUuid = childrenPartObj.value("uuid").toString();
        partInfoIter = std::find_if(allPartJsonObjInfo.begin(), allPartJsonObjInfo.end(), [=](QJsonObject partInfo) {
            return ((!partInfo.value("uuid").toString().compare(partUuid))
            && partInfo.value("mountpoint").toString().isEmpty()
            && partInfo.value("name").toString().contains(m_loopDevice));
        });

        if (partInfoIter == allPartJsonObjInfo.end()) {
            qWarning() << "beforeSquashfsImgFile error ! get loop dev info  failed";
            return false;
        }

        QString loopDevItemMountPath = (!partMountPoint.compare("/")) ? m_loopDevMountPath : m_loopDevMountPath + partMountPoint;
        QStringList mountStrItem;
        mountStrItem.append(QString("%1").arg(loopDevItemMountPath.count("/")));
        mountStrItem.append((*partInfoIter).value("name").toString() + ";" + loopDevItemMountPath);
        mountStrList.append(mountStrItem);
    }

    // 按照路径浅度挂载目录
    std::sort(mountStrList.begin(), mountStrList.end(), [=](QStringList strItemPre, QStringList strItemAfter){
        return (strItemPre.first().toInt() < strItemAfter.first().toInt());
    });

    for (QStringList mountStrItem : mountStrList) {
        QString loopSrcPath = mountStrItem.last().left(mountStrItem.last().indexOf(";"));
        QString loopDestPath = mountStrItem.last().right(mountStrItem.last().length() - mountStrItem.last().indexOf(";") - 1);
        QString cmd = QString("mkdir -p %1").arg(loopDestPath);
        QString cmdLog = "";
        err = "";
        if (!Process::spawnCmd("mkdir", {"-p", loopDestPath}, cmdLog, err)) {
            qWarning() << "beforeSquashfsImgFile error ! cmd : " + cmd + " err : " + err;
            return false;
        }

        // 挂载loop分区
        cmd = QString("mount %1 %2").arg(loopSrcPath).arg(loopDestPath);
        cmdLog = "";
        err = "";
        if (!Process::spawnCmd("mount", {loopSrcPath, loopDestPath}, cmdLog, err)) {
            qWarning() << "beforeSquashfsImgFile error ! cmd : " + cmd + " err : " + err;
            return false;
        }
    }

    return true;
}

bool DimFileTransTask::afterSquashfsImgFile()
{
    // 获取所有分区信息
    QJsonArray allPartJsonArrayInfo;
    QString err = "";
    QStringList args;
    args<<m_loopDevice<<"-lpOJ";
    if (!Utils::getLsblkJsonReturn(args, allPartJsonArrayInfo, err)) {
        qWarning() << "afterSquashfsImgFile error ! getLsblkJsonReturn failed err : " + err;
        return false;
    }

    QString cmd = "";
    QString cmdLog = "";

    // 卸载loop的挂载目录
    QList<QStringList> umountStrList;
    for (int i = 0; i < allPartJsonArrayInfo.size(); i++) {
        QJsonObject loopPartInfo = allPartJsonArrayInfo.at(i).toObject();
        QString loopDevMountPoint = loopPartInfo.value("mountpoint").toString();
        if (!loopDevMountPoint.isEmpty()) {
            QStringList umountStrItem;
            umountStrItem.append(QString("%1").arg(loopDevMountPoint.count("/")));
            umountStrItem.append(loopDevMountPoint);
            umountStrList.append(umountStrItem);
        }
    }

    // 按照路径深度卸载目录
    std::sort(umountStrList.begin(), umountStrList.end(), [=](QStringList strItemPre, QStringList strItemAfter){
        return (strItemPre.first().toInt() > strItemAfter.first().toInt());
    });

    for (QStringList umountStrItem : umountStrList) {
        QString umountPoint = umountStrItem.last();
        if (umountPoint.contains(" ")) {
            umountPoint = QString("\"%1\"").arg(umountPoint);
        }
        cmd = QString("umount %1").arg(umountPoint);
        cmdLog = "";
        err = "";
        if (!Process::spawnCmd("umount", {umountPoint}, cmdLog, err)) {
            qWarning() << "afterSquashfsImgFile error ! cmd : " + cmd + " err : " + err;
        }
    }

    // 卸载loop设备
    cmd = QString("losetup -d %1").arg(m_loopDevice);
    cmdLog = "";
    err = "";
    if (!Process::spawnCmd("losetup",{"-d", m_loopDevice}, cmdLog, err)) {
        qWarning() << "afterSquashfsImgFile error ! cmd : " + cmd + " err : " + err;
    }

    // 清除loop设备的挂载目录
    cmd = QString("rm -rf %1").arg(m_loopDevMountPath);
    cmdLog = "";
    err = "";
    if (!Process::spawnCmd("rm",{"-rf", m_loopDevMountPath}, cmdLog, err)) {
        qWarning() << "afterSquashfsImgFile error ! cmd : " + cmd + " err : " + err;
    }
    m_loopDevMountPath = "";

    // 修改squashfs文件为img文件
    cmd = QString("rm %1").arg(m_imgFile);
    cmdLog = "";
    err = "";
    if (!Process::spawnCmd("rm",{"-rf", m_imgFile}, cmdLog, err)) {
        qWarning() << "afterSquashfsImgFile error ! cmd : " + cmd + " err : " + err;
    }

    cmd = QString("mv %1 %2").arg(m_squashfsImgFile).arg(m_imgFile);
    cmdLog = "";
    err = "";
    if (!Process::spawnCmd("mv",{m_squashfsImgFile, m_imgFile}, cmdLog, err)) {
        qWarning() << "afterSquashfsImgFile error ! cmd : " + cmd + " err : " + err;
    }

    return true;
}
