/**
 * @copyright 2020-2020 Uniontech Technology Co., Ltd.
 *
 * @file thread.cpp
 *
 * @brief 坏道检测修复线程类
 *
 * @date 2021-01-04 13:58
 *
 * Author: liweigang  <liweigang@uniontech.com>
 *
 * Maintainer: liweigang  <liweigang@uniontech.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "thread.h"
#include "commondef.h"
#include "blockspecial.h"
#include "procpartitionsinfo.h"
#include "fsinfo.h"
#include "mountinfo.h"
#include "partedcore.h"

#include <QDebug>
#include <QProcess>
#include <QTime>
#include <QThread>
#include <unistd.h>

namespace DiskManager {

WorkThread::WorkThread(QObject *parent)
{
    Q_UNUSED(parent);
    m_stopFlag = 0;
    m_blockStart = 0;
    m_blockEnd = 0;
    m_checkConut = 0;
    m_checkSize = 0;
}

void WorkThread::setStopFlag(int flag)
{
    m_stopFlag = flag;
}

void WorkThread::setCountInfo(const QString &devicePath, int blockStart, int blockEnd, int checkConut, int checkSize)
{
    m_devicePath = devicePath;
    m_blockStart = blockStart;
    m_blockEnd = blockEnd;
    m_checkConut = checkConut;
    m_checkSize = checkSize;
}

void WorkThread::setTimeInfo(const QString &devicePath, int blockStart, int blockEnd, QString checkTime, int checkSize)
{
    m_devicePath = devicePath;
    m_blockStart = blockStart;
    m_blockEnd = blockEnd;
    m_checkTime = checkTime;
    m_checkSize = checkSize;
}

void WorkThread::runCount()
{
//    qDebug() << QThread::currentThreadId() << endl;
    Sector i = m_blockStart;
    Sector j = m_blockStart+1;
    QProcess proc;
    while(j <= m_blockEnd+1 && m_stopFlag != 2)
    {
        QString cmd = QString("badblocks -sv -c %1 -b %2 %3 %4 %5").arg(m_checkConut).arg(m_checkSize).arg(m_devicePath).arg(j).arg(i);

        QDateTime ctime = QDateTime::currentDateTime();
        proc.start(cmd);
        proc.waitForFinished(-1);
        QDateTime ctime1 = QDateTime::currentDateTime();

        cmd = proc.readAllStandardError();

        if (cmd.indexOf("0/0/0") != -1) {

            QString cylinderNumber = QString("%1").arg(i);
            QString cylinderTimeConsuming = QString("%1").arg(ctime.msecsTo(ctime1));
            QString cylinderStatus = "good";
            QString cylinderErrorInfo = "";

            emit checkBadBlocksInfo(cylinderNumber, cylinderTimeConsuming, cylinderStatus, cylinderErrorInfo);
        } else {

            QString cylinderNumber = QString("%1").arg(i);
            QString cylinderTimeConsuming = QString("%1").arg(ctime.msecsTo(ctime1));
            QString cylinderStatus = "bad";
            QString cylinderErrorInfo = "IO Read Error";

            emit checkBadBlocksInfo(cylinderNumber, cylinderTimeConsuming, cylinderStatus, cylinderErrorInfo);
        }

        i++;
        j++;
    }

    if (m_stopFlag != 2) {
        emit checkBadBlocksFinished(); //检测完成正常退出,发送给页面的正常结束信号
    }
}

void WorkThread::runTime()
{

    Sector i = m_blockStart;
    Sector j = m_blockStart+1;
    QProcess proc;
    while(j <= m_blockEnd+1 && m_stopFlag != 2)
    {
        QString cmd = QString("badblocks -sv -b %1 %2 %3 %4").arg(m_checkSize).arg(m_devicePath).arg(j).arg(i);

        QDateTime ctime = QDateTime::currentDateTime();
        proc.start(cmd);
        proc.waitForFinished(-1);
        QDateTime ctime1 = QDateTime::currentDateTime();

        cmd = proc.readAllStandardError();

        if(ctime.msecsTo(ctime1) > m_checkTime.toInt()) {
            QString cylinderNumber = QString("%1").arg(i);
            QString cylinderTimeConsuming = QString("%1").arg(ctime.msecsTo(ctime1));
            QString cylinderStatus = "bad";
            QString cylinderErrorInfo = "IO Device Timeout";

            emit checkBadBlocksInfo(cylinderNumber, cylinderTimeConsuming, cylinderStatus, cylinderErrorInfo);
        } else {
            if (cmd.indexOf("0/0/0") != -1) {
                QString cylinderNumber = QString("%1").arg(i);
                QString cylinderTimeConsuming = QString("%1").arg(ctime.msecsTo(ctime1));
                QString cylinderStatus = "good";
                QString cylinderErrorInfo = "";

                emit checkBadBlocksInfo(cylinderNumber, cylinderTimeConsuming, cylinderStatus, cylinderErrorInfo);
            } else {
                QString cylinderNumber = QString("%1").arg(i);
                QString cylinderTimeConsuming = QString("%1").arg(ctime.msecsTo(ctime1));
                QString cylinderStatus = "bad";
                QString cylinderErrorInfo = "IO Read Error";

                emit checkBadBlocksInfo(cylinderNumber, cylinderTimeConsuming, cylinderStatus, cylinderErrorInfo);
            }
        }
        i++;
        j++;
    }

    if (m_stopFlag != 2) {
        emit checkBadBlocksFinished();
    }
}

FixThread::FixThread(QObject *parent)
{
    Q_UNUSED(parent);
    m_stopFlag = 0;
    m_checkSize = 0;
}

void FixThread::setStopFlag(int flag)
{
    m_stopFlag = flag;
}

void FixThread::runFix()
{
//    qDebug() << m_list << endl;
    int i = 0;
    QProcess proc;
    while(i < m_list.size() && m_stopFlag != 2) {
        Sector j = m_list.at(i).toInt();
        Sector k = m_list.at(i).toInt() + 1;
        QString cmd = QString("badblocks -sv -b %1 -w %2 %3 %4").arg(m_checkSize).arg(m_devicePath).arg(k).arg(j);

        QDateTime ctime = QDateTime::currentDateTime();
        proc.start(cmd);
        proc.waitForFinished(-1);
        QDateTime ctime1 = QDateTime::currentDateTime();

        cmd = proc.readAllStandardError();

        if (cmd.indexOf("0/0/0") != -1) {

            QString cylinderNumber = QString("%1").arg(j);
            QString cylinderStatus = "good";
            QString cylinderTimeConsuming = QString("%1").arg(ctime.msecsTo(ctime1));

            emit fixBadBlocksInfo(cylinderNumber, cylinderStatus, cylinderTimeConsuming);
        } else {

            QString cylinderNumber = QString("%1").arg(j);
            QString cylinderStatus = "bad";
            QString cylinderTimeConsuming = QString("%1").arg(ctime.msecsTo(ctime1));

            emit fixBadBlocksInfo(cylinderNumber, cylinderStatus, cylinderTimeConsuming);
        }
        i++;
    }

    if (m_stopFlag != 2) {
        emit fixBadBlocksFinished();
    }
}

void FixThread::setFixBadBlocksInfo(const QString &devicePath, QStringList list, int checkSize)
{
    m_devicePath = devicePath;
    m_list = list;
    m_checkSize = checkSize;
}

ProbeThread::ProbeThread(QObject *parent)
{
    Q_UNUSED(parent);
    m_type = 0;
}

void ProbeThread::setSignal(int type, bool arg1, QString arg2)
{
    m_type = type;
    m_arg1 = arg1;
    m_arg2 = arg2;
}

void ProbeThread::sendsignals()
{
    qDebug() << __FILE__ << ":" << __FUNCTION__ << " m_type:" << m_type;
    switch (m_type) {
    case DISK_SIGNAL_TYPE_UMNT:
        emit unmountPartition(m_arg2);
        break;
    case DISK_SIGNAL_TYPE_DEL:
        emit deletePartitionMessage(m_arg2);
        break;
    case DISK_SIGNAL_TYPE_SHOW:
        emit showPartitionInfo(m_arg2);
        break;
    case DISK_SIGNAL_TYPE_CREATE_TABLE:
        emit createTableMessage(m_arg1);
        break;
    case DISK_SIGNAL_USBUPDATE:
        emit usbUpdated();
        break;
    case DISK_SIGNAL_TYPE_CLEAR:
        emit clearPartitionMessage(m_arg2);
        break;
    default:
        break;
    }
    m_type = 0;
}

void ProbeThread::probeDeviceInfo()
{
    qDebug() << __FILE__ << ":" << __FUNCTION__ << "Someone call me in thread!";

    if (DISK_SIGNAL_TYPE_AUTOMNT == m_type) {
        //Only usb add need to sleep 5 seconds
        qDebug() << __FUNCTION__ << "From auto Mount, So i will sleep 5 seconds! type:" << m_type;
        //相传有一块移动硬盘，需要先 sleep 一下才能正确读取。
        sleep (5);
    }

    QString rootFsName;
    m_inforesult.clear();
    m_deviceMap.clear();
    QVector<QString> devicePaths;
    devicePaths.clear();
    BlockSpecial::clearCache();
    ProcPartitionsInfo::loadCache();
    FsInfo::loadCache();
    MountInfo::loadCache(rootFsName);
    ped_device_probe_all();
    PedDevice *lpDevice = ped_device_get_next(nullptr);
    while (lpDevice) {
        /* TO TRANSLATORS: looks like   Confirming /dev/sda */
        qDebug() << QString("Confirming %1").arg(lpDevice->path);

        //only add this device if we can read the first sector (which means it's a real device)

        if (PartedCore::useableDevice(lpDevice))
            devicePaths.push_back(lpDevice->path);
//        qDebug() << lpDevice->path;
        lpDevice = ped_device_get_next(lpDevice);
    }
//    qDebug() << __FUNCTION__ << "devicepaths size=" << devicepaths.size();
    std::sort(devicePaths.begin(), devicePaths.end());
//    qDebug() << __FUNCTION__ << "**8";
    for (int t = 0; t < devicePaths.size(); t++) {
        /*TO TRANSLATORS: looks like Searching /dev/sda partitions */
        Device tempDevice;
        PartedCore pcl;
        pcl.setDeviceFromDisk(tempDevice, devicePaths[t]);
        DeviceStorage device;
        tempDevice.m_mediaType = device.getDiskInfoMediaType(devicePaths[t]);
        device.getDiskInfoModel(devicePaths[t], tempDevice.m_model);
        device.getDiskInfoInterface(devicePaths[t],tempDevice.m_interface, tempDevice.m_model);
        m_deviceMap.insert(devicePaths.at(t), tempDevice);
    }
//    qDebug() << __FUNCTION__ << "**9";
    //这里的代码有可能会恢复，与文管对移动设备的处理相关
//    getPartitionHiddenFlag();
    for (auto it = m_deviceMap.begin(); it != m_deviceMap.end(); it++) {
        DeviceInfo devinfo = it.value().getDeviceInfo();
        for (int i = 0; i < it.value().m_partitions.size(); i++) {
            const Partition &pat = *(it.value().m_partitions.at(i)); //拷贝构造速度提升 const 引用
            PartitionInfo partinfo = pat.getPartitionInfo();
              //这里的代码有可能会恢复，与文管对移动设备的处理相关
//            if(m_hiddenPartition.indexOf(partinfo.m_uuid) != -1 && !partinfo.m_uuid.isEmpty()) {
//                partinfo.m_flag = 1;
//            } else {
//                partinfo.m_flag = 0;
//            }

            partinfo = pat.getPartitionInfo();
            if (rootFsName == pat.getPath()) {
                partinfo.m_flag = 4;
                qDebug() << __FUNCTION__ << "Set systemfs Flags3 !! " << pat.m_devicePath << " " << pat.m_name << " " << pat.m_uuid;
            }

            if (pat.m_type == PartitionType::TYPE_EXTENDED) {
                devinfo.m_partition.push_back(partinfo);
                for (int k = 0; k < pat.m_logicals.size(); k++) {
                    const Partition &plogic = *(pat.m_logicals.at(k));
                    partinfo = plogic.getPartitionInfo();
                    if (rootFsName == plogic.m_name) {
                        partinfo.m_flag = 4;
                        qDebug() << __FUNCTION__ << "Set systemfs Flags4 !! " << plogic.m_devicePath << " " << plogic.m_name << " " << plogic.m_uuid;
                    }
                    qDebug() << __FUNCTION__ << plogic.m_devicePath << " " << plogic.m_name << " " << plogic.m_uuid;
                    devinfo.m_partition.push_back(partinfo);
                }
            } else {
                devinfo.m_partition.push_back(partinfo);
            }
        }
        m_inforesult.insert(devinfo.m_path, devinfo);
    }
//    qDebug() << __FUNCTION__ << m_inforesult.count();
//    qDebug() << __FUNCTION__ << "**10";
    emit updateDeviceInfo(/*m_deviceMap,*/ m_inforesult);

    sendsignals();
    qDebug() << __FILE__ << ":" << __FUNCTION__ << "Someone call me in thread，working done!";
    qDebug() << __FILE__ << "Now I am working on thread:" << QThread::currentThreadId();
}

QMap<QString, Device> ProbeThread::getDeviceMap()
{
    return m_deviceMap;
}

}
