#include "processburnudfdata.h"
#include "src/frame/logger.h"
#include "src/po/translation.h"
#include "src/data/datacdrom.h"
#include "src/data/datadirectory.h"
#include "src/view/modaldialog.h"
#include "src/view/execdialog.h"
#include "src/model/diroptutils.h"
#include "src/frame/signals.h"

#include <QFileInfo>
#include <QDir>
#include <QCoreApplication>
#include <QStandardPaths>
#include <fstream>

ProcessBurnUDFData::ProcessBurnUDFData(void *root, void *cdrom, QObject *parent) :
    ProcessBasic(parent),
    m_isCreateISO(false),
    m_oFlag(false),
    m_iProgress(0),
    m_bBurnSuccess(false),
    m_bBurnLogError(false),
    m_isVerficationData(false),
    m_icountBurn(0),
    m_oProgressBarTimer_DVD(new QTimer(this))
{
    auditburner=new QGSettings("org.ukui.kylinburner");
    auditlogpath="/var/log/kylin-burner/os_audit.log";
    m_isSpecialMachine=auditburner->get("burneraudit-isrunning").toBool();

    m_oCDROM = cdrom;
    m_cloneCDROM = ((DataCDROM *)cdrom)->clone();
    m_oRoot = root;
    m_root = static_cast<DataDirectory *>(m_oRoot);
    m_ototal = m_root->total(); //新旧文件总数量
    m_destination = m_root->destination();
    m_oDeviceBlock = m_cloneCDROM->device();
    m_isVerficationData = m_cloneCDROM->settingVerficationData();
    m_fSpeed = 0;

    if (m_isVerficationData) {
        //拼接路径
        m_tempPath = m_cloneCDROM->settingTempPath();

        if (m_tempPath.endsWith("/")) {
            m_tempPath = m_tempPath + m_cloneCDROM->settingVolumnID();
        } else{
            m_tempPath = m_tempPath + '/' + m_cloneCDROM->settingVolumnID();
        }
    }

    connect(g_user_signal, &GlobalUserSignal::removeDisc, this, &ProcessBurnUDFData::slotsRemoveDisc);
    connect(m_oProgressBarTimer_DVD, SIGNAL(timeout()), this, SLOT(slotsProcessBarDVD()));

    if (m_cloneCDROM->mediumStatus() == DataMedium::MEDIUM_EMPTY)
    {
        LOGGER->registerName(this, _("PROCESS_UDF_DATA"));
        title(_("Burning UDF Format Data"));
        m_oProcessName = _("Burn  UDF Format Data");
        m_bAppendBurn = false;
    }
    else
    {
        LOGGER->error(_("Media Not Empty,Not Burn UDF Format"));
        m_oProcessName = _("UDF Append Burn");
        m_bAppendBurn = true;
    }

    MODAL->show();
    emit setRange(0, 100);
    emit setValue(0);

    g_user_signal->setDeviceBusyStatus(m_oDeviceBlock,true);
}

bool ProcessBurnUDFData::prepare()
{
    m_oProcess->setProgram("/bin/bash");
    m_iProgress = 0;

    QString sysid;
    ifstream fin;
    fin.open("/etc/kylin-build");
    if (fin.is_open())
    {
        std::string s;
        std::getline(fin, s);
        sysid = QString::fromStdString(s);
    }
    if (sysid.isEmpty()) sysid = _("KylinOS");

    LOGGER->info(QString("光驱为：%1").arg(m_cloneCDROM->display()));

    QFileInfo f;
    f.setFile("/bin/growisofs");
    if (!f.exists() || !f.isExecutable())
    {
        LOGGER->error(_("There is no %1 program or %1 cannot execute."
                "Please check the cdrecord and growisofs is "
                "exists or not, and give the right permissions"
                ".%2 exiting...")
              .arg(f.absoluteFilePath())
              .arg("KylinBurner"));
        return false;
    }

    connect(this, SIGNAL(cancel(bool)), MODAL, SLOT(cancelEnable(bool)));

    m_isCreateISO = m_cloneCDROM->settingCreateISO();
    if (m_isCreateISO)
    {
        f.setFile("/bin/mkisofs");
        if (!f.exists() || !f.isExecutable())
        {
            LOGGER->error(_("There is no %1 program or %1 cannot execute."
                    "Please check the mkisofs is exists or not, and give "
                    "the right permissions.%2 exiting...")
                  .arg(f.absoluteFilePath())
                  .arg("KylinBurner"));
            return false;
        }
        m_isoOutput = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
        m_isoOutput.append("/").append(m_cloneCDROM->settingVolumnID()).append(".iso");
        f.setFile(m_isoOutput);
        if (f.exists())
        {

            if (!EXEC->question(_("Create ISO") + " - " + _("Already Exists"),
                                _("There is already have the iso file named %1,"
                                  "replace it or not?").arg(f.fileName()))) {
                return false;
            }
            if (f.isFile()) QFile::remove(f.absoluteFilePath());
            else { QDir dir(f.absoluteFilePath()); dir.removeRecursively(); }
        }
    }

    PROGRESS;
    MODAL->show();

    m_oCreateISO = QString("mkisofs -o %1 -r -J -udf ").arg(m_isoOutput);
    m_oMkisofs = QString("mkisofs -o %1 -r -J -udf ");
    m_oMkisofs.append(QString("-A \"%1\" ").arg(_("KylinBurner")));
    m_oCreateISO.append(QString("-A \"%1\" ").arg(_("KylinBurner")));
    m_oGrowisofs = QString("growisofs -r  -J -udf -v ");
    m_oGrowisofs.append(QString("-A \"%1\" ").arg(_("KylinBurner")));

    m_oArguments << "-c";

    setValue(++m_iProgress, m_oIcon, _("Prepare to burn UDF format data to DVD."));

    if (!sysid.isEmpty())
    {
        m_oMkisofs.append(QString("--sysid \"%1\" ").arg(sysid));
        m_oGrowisofs.append(QString("--sysid \"%1\" ").arg(sysid));
    }
    return m_oKeep;
}

bool ProcessBurnUDFData::config()
{
    m_oMkisofs.append(QString("-p \"%1\" ").arg(m_cloneCDROM->settingPreparer()));
    m_oCreateISO.append(QString("-p \"%1\" ").arg(m_cloneCDROM->settingPreparer()));
    m_oGrowisofs.append(QString("-p \"%1\" ").arg(m_cloneCDROM->settingPreparer()));

    m_oMkisofs.append(QString("-P \"%1\" ").arg(m_cloneCDROM->settingPublisher()));
    m_oCreateISO.append(QString("-P \"%1\" ").arg(m_cloneCDROM->settingPublisher()));
    m_oGrowisofs.append(QString("-P \"%1\" ").arg(m_cloneCDROM->settingPublisher()));

    m_oMkisofs.append(QString("-copyright \"%1\" ").arg(m_cloneCDROM->settingCopyright()));
    m_oCreateISO.append(QString("-copyright \"%1\" ").arg(m_cloneCDROM->settingCopyright()));
    m_oGrowisofs.append(QString("-copyright \"%1\" ").arg(m_cloneCDROM->settingCopyright()));

    if (m_cloneCDROM->settingSpeed()){
        m_oGrowisofs.append(QString("-speed=%1 ").arg(m_cloneCDROM->settingSpeed()));
    }

    setValue(++m_iProgress, m_oIcon, _("Prepare to start burn UFD format data."));
    return m_oKeep;
}

bool ProcessBurnUDFData::working()
{
    m_iCopied = 0;
    connect(m_root, &DataDirectory::copied, this, &ProcessBurnUDFData::slotsCopy);
    m_root->copy();
    if(!DataFile::isCopySuccess) {
        DataFile::isCopySuccess = true;
        LOGGER->info("(ProcessBurnData::working) 拷贝文件失败");
        return false;
    }

    if (m_isVerficationData) {
        DirOptUtils *diropt  = new DirOptUtils;
        diropt->getFileListUnderDir1(m_tempPath);
        diropt->testPrintMap();
        diropt->generateCheckFile();
        delete diropt;
    }

    if (m_bCancel)
    {
        return false;
    }

    if (m_isCreateISO)
    {
        m_oCreateISO.append(m_destination);
        m_oArguments << m_oCreateISO;
        m_oProcess->setArguments(m_oArguments);
        for (int i = 0; i < m_oArguments.size(); ++i)
        {
            qDebug() << QString("Arg.%1 - %2").arg(i + 1).arg(m_oArguments.at(i));
            LOGGER->info(QString("Arg.%1 - %2").arg(i + 1).arg(m_oArguments.at(i)));
        }
        qDebug() << "";

        m_cloneCDROM->updateCDROMBurnStart(true);
        m_oProcess->start();
        while (m_oProcess->state() != QProcess::NotRunning){
            QCoreApplication::processEvents();
        }
        if(!m_oKeep) return false;
        m_isCreateISO = false;
    }

    if (m_bCancel) {
        return false;
    }

    m_oArguments.clear();
    m_oArguments << "-c";

    m_oGrowisofs.append(QString("-Z %1 \"%2\"").arg(m_cloneCDROM->device()).arg(m_destination));
    m_oArguments << m_oGrowisofs;

    m_oProcess->setArguments(m_oArguments);
    m_oProgressBarTimer_DVD->start(1000);

    m_cmdArgumentStr = "Argument setted for " + m_oProcess->program() + "\n" + "Arg: ";
    for (int i = 0; i < m_oArguments.size(); ++i)
    {
        m_cmdArgumentStr += " "; m_cmdArgumentStr +=  m_oArguments.at(i);
    }
    if (m_cloneCDROM->mediumStatus() == DataMedium::MEDIUM_EMPTY) {
        LOGGER->info(QString("/*************** Empty Disc Burn Data, count = %1 ***************/").arg(m_icountBurn + 1));
    } else {
        LOGGER->info(QString("/*************** NonEmpty Disc Append Burn Data ***************/"));
    }
    LOGGER->info(m_cmdArgumentStr);
    m_oProcess->start();
    while (m_oProcess->state() != QProcess::NotRunning)
        QCoreApplication::processEvents();
    return m_oKeep;
}

bool ProcessBurnUDFData::worked()
{
    if (m_bCancel) return false;
    if (QProcess::NormalExit != m_oProcess->exitStatus()) return false;
    return m_oKeep;
}

void ProcessBurnUDFData::emptyDiscBurn()
{
    LOGGER->info(QString("/*************** Empty DVD+RDL Disc Burn Data, count = %1 ***************/").arg(m_icountBurn + 1));
    LOGGER->info(m_cmdArgumentStr);
    m_bBurnLogError = false;
    m_oProcess->start();
    while (m_oProcess->state() != QProcess::NotRunning){
        QCoreApplication::processEvents();
    }
}

void ProcessBurnUDFData::slotsStart()
{
    emit cancel(false);
}

void ProcessBurnUDFData::slotsOutput()
{

    QString output = m_oProcess->readAllStandardOutput();
    LOGGER->info(_("ProcessBurnUDFData %1").arg(output));
    if (m_bCancel)
        m_oProcess->kill();
}

void ProcessBurnUDFData::slotsError()
{
    QString output;

    output = m_oProcess->readAllStandardError();
    LOGGER->info(QString("ProcessBurnUDFData %1").arg(output));

    QList<QString> errorInfoList;
    errorInfoList << "Input/output error" << "OPC failed." << "FATAL:" << ":-[" << "target is busy"
                  << "Cannot open busy device";;
    bool isErrorInfo = false;

    foreach (QString index, errorInfoList) {
        if(output.contains(index)){
            m_bBurnLogError = true;
            isErrorInfo = true;
            break;
        }
    }

    if (output.contains("Input/output error")) {
        m_lastErrorMsg = QString(_("Input/output error. Disc may be damaged, please clean or replace a disc and try again."));
    }

    if (output.contains(":-(")) {
        LOGGER->info(QString("[%1] [%2] 发现错误： [%3]").arg(__func__).arg(__LINE__).arg(output));
        if (m_oFlag && output.contains(":-( unable to reload tray")){
            m_bBurnLogError = false;
            LOGGER->info(QString("[%1] [%2] 出现 :-( unable to reload tray, 不认为是错误输出 设置m_bBurnLogError = [%3]").arg(__func__).arg(__LINE__).arg(m_bBurnLogError));
        } else {
            m_bBurnLogError = true;
        }
    }

    if (output.contains("Current Write Speed")){
        QStringList subStrList = output.split(" ");
        subStrList = subStrList.last().split("x");
        m_fSpeed = subStrList.first().toDouble();
    }

    QList<QString> processInfoList;
    processInfoList << "flushing cache" << "closing track" << "closing session";

    foreach (QString index, processInfoList) {
        if(output.contains(index)){
           if(m_oProgressBarTimer_DVD->isActive()) {
               m_oProgressBarTimer_DVD->stop();
           }
            emit setValue(99, m_oIcon, _(index));
        }
    }

    if(output.contains("reloading tray")){
        m_oFlag = true;
        LOGGER->info(QString("[%1] [%2]  run return value: reloading tray 设置m_oFlag = [%3]").arg(__func__).arg(__LINE__).arg(m_oFlag));
    }
}

void ProcessBurnUDFData::slotsFinished(int code, QProcess::ExitStatus status)
{
    LOGGER->info(_("Exit code is %1, status : %2, m_oKeep : %3").arg(code).arg(status).arg(m_oKeep));
    if (!m_isCreateISO) setValue(99, m_oIcon, _("Burn Data"));

    //创建镜像
    if (m_isCreateISO)
    {
        if (QProcess::NormalExit == status && 0 == code) {
            LOGGER->info( QString("file:[%1] line:[%2] : slotsFinished() create Udf-ISO success.").arg(__FILE__).arg(__LINE__) );
            return;
        } else {
            LOGGER->error( QString("file:[%1] line:[%2] : slotsFinished() create Udf-ISO failure. [%3]").arg(__FILE__).arg(__LINE__).arg(m_oProcess->errorString()) );
            m_oCreateIsoLog.ouputCreateResult(m_cloneCDROM->display(), m_cloneCDROM->mDisplay(), true==m_oKeep);
            m_oKeep = false;
            return;
        }
    }

    // 刻录结果
    m_icountBurn++;
    LOGGER->info(_("m_icountBurn : %3").arg(m_icountBurn));
    if (QProcess::NormalExit == status && 0 == code) {
        if (m_bBurnLogError) {
            m_bBurnSuccess = false;
        } else {
            m_bBurnSuccess = true;
        }
    } else {
        m_bBurnSuccess = false;
        if (m_oFlag && !m_bBurnLogError && (1 == code)){
            LOGGER->info(QString("%1 %2 出现 :-( unable to reload tray, 不认为是错误输出 m_oFlag = %3, code = %4 m_bBurnLogError = %5")
                 .arg(__LINE__).arg(__func__).arg(m_oFlag).arg(code).arg(m_bBurnLogError));
            m_bBurnSuccess = true;
        }
    }

    if (m_bBurnSuccess) {
        LOGGER->info(QString("ProcessBurnDataDL::slotsFinished() : burn DVD+R DL data success. Line:%1").arg(__LINE__));
    } else {
        if(m_icountBurn < 3 && (m_cloneCDROM->mediumStatus() == DataMedium::MEDIUM_EMPTY) && (m_cloneCDROM->mediumType().contains("DVD+R DL"))) {
            // command `growisofs` maybe fail, such as DVD+R DL disk, we need burn three times again to make burn success.
            emptyDiscBurn();
            return;
        } else {
            // 刻录失败
            m_oKeep = false;
            LOGGER->info(QString("ProcessBurnDataDL::slotsFinished() : burn DVD+R DL data fail. Line:%1").arg(__LINE__));

        }
    }

    g_user_signal->setDeviceBusyStatus(m_oDeviceBlock,false);
    LOGGER->info("ProcessBurnDataDL::slotsFinished 弹出光盘");
    m_cloneCDROM->updateCDROMBurnStart(false);
    DataCDROM *cdrom = static_cast<DataCDROM *>(m_oCDROM);
    emit g_user_signal->noMedium(cdrom);

    if(m_oProgressBarTimer_DVD->isActive()) {
        m_oProgressBarTimer_DVD->stop();
    }

    if(m_isSpecialMachine==true)
    {
        auditburner->set("burner-os-auditlog-name",auditlogpath);
        m_oBurnUdfDataLog.outputResult(m_cloneCDROM->display(), m_cloneCDROM->mediumType(), m_oProcessName, true==m_oKeep);
    }
    else
    {
        auditburner->set("burner-os-auditlog-name","");
    }
}

void ProcessBurnUDFData::slotsCopy(QString src, QString des)
{
    int basic;

    if(m_isSpecialMachine==true){
        auditburner->set("burner-os-auditlog-name",auditlogpath);
        m_oBurnUdfDataLog.appendFile(src);                         //待刻录的源文件
    }else{
        auditburner->set("burner-os-auditlog-name","");
    }

    if (m_isCreateISO) {
        basic = 30 - m_iProgress;
    }else {
        basic = 50 - m_iProgress;
    }

    ++m_iCopied;

    qreal v = ((m_iCopied * 1.0) / (m_ototal * 1.0));
    v = v * basic;
    m_iProgress += v;

    emit setValue(m_iProgress, m_oIcon, _("Copied %1 to %2").arg(src).arg(des));
    LOGGER->debug(_("Copied %1 to %2").arg(src).arg(des));
}

void ProcessBurnUDFData::slotsRemoveDisc(QString dev)
{
    if(dev == m_cloneCDROM->device()) {
        if(m_oProcess->state() != QProcess::NotRunning) {
            qDebug() << __func__ << __LINE__ << dev << "被拔出，杀死刻录数据进程";
            LOGGER->info(QString("[%1] [%2] : %3 被拔出，杀死刻录数据进程").arg(__func__).arg(__LINE__).arg(dev));
            m_oProcess->kill();
        }
    }
}

void ProcessBurnUDFData::slotsProcessBarDVD()
{
    m_iProgress += 1;
    if (m_iProgress > 98) m_iProgress = 98;
    setValue(m_iProgress, m_oIcon, _("Burn Data"));
}

ProcessBurnUDFData::~ProcessBurnUDFData()
{
    if(m_oProgressBarTimer_DVD!=nullptr)
        delete m_oProgressBarTimer_DVD;
    m_oProgressBarTimer_DVD = nullptr;
    LOGGER->info("ProcessBurnUDFData::~ProcessBurnUDFData");
}
