#include "processburnimage.h"
#include "src/frame/configuration.h"
#include "src/frame/logger.h"
#include "src/po/translation.h"
#include "src/data/datacdrom.h"
#include "src/view/modaldialog.h"
#include "src/frame/signals.h"

#include <QFileInfo>
#include <QDebug>

ProcessBurnImage::ProcessBurnImage(QString image, void *cdrom, QObject *parent) :
    ProcessBasic(parent),
    m_oImage(image),
    m_oCDROM(cdrom),
    m_iProgress(0)
{
    m_oErrorAndIgnoreInfo.clear();
    auditburner = new QGSettings("org.ukui.kylinburner");
    auditlogpath = "/var/log/kylin-burner/os_audit.log";
    m_isSpecialMachine = auditburner->get("burneraudit-isrunning").toBool();
    m_oCloneCDROM = ((DataCDROM *)m_oCDROM)->clone();
    m_oDeviceBlock = m_oCloneCDROM->device();
    LOGGER->registerName(this, _("PROCESS_BI"));
    title(_("Burning Image"));
    m_oProcessName = _("Burn Image");
    setRange(0, 100);
    setValue(0);
    connect(g_user_signal, &GlobalUserSignal::removeDisc, this, &ProcessBurnImage::slotsRemoveDisc);
    g_user_signal->setDeviceBusyStatus(m_oDeviceBlock,true);
}

bool ProcessBurnImage::prepare()
{
    QFileInfo f;
    LOGGER->info(_("Step.%1: preparing...").arg("prepare"));
    setValue(0, m_oIcon, _("Step.%1: preparing...").arg("prepare"));

    PROGRESS;
    MODAL->show();
    connect(this, SIGNAL(cancel(bool)), MODAL, SLOT(cancelEnable(bool)));

    f.setFile(m_oImage);
    if (!f.exists())
    {
        LOGGER->error(_("Image file %1 already been removed.").arg(m_oImage));
        setValue(++m_iProgress, QIcon::fromTheme("dialog-error-symbolic"),
                 _("Image file %1 already been removed.").arg(m_oImage));
        return false;
    }
    LOGGER->info(_("Checked image already exists."));
    setValue(++m_iProgress, m_oIcon, _("Checked image already exists."));

    if (!verifyCDROM()) {
        return false;
    }
    m_oDeviceBlock = m_oCloneCDROM->device();

    if (m_oCloneCDROM->mediumType().startsWith("CD"))
    {
        f.setFile("/bin/cdrecord");
        if (!f.exists())
        {
            LOGGER->error(_("Cannot find program <%1>.").arg(f.fileName()));
            setValue(++m_iProgress, QIcon::fromTheme("dialog-error-symbolic"),
                     _("Cannot find program <%1>.").arg(f.fileName()));
            return false;
        }
        if (!f.isExecutable())
        {
            LOGGER->error(_("Program %1 is not executable file.").arg(f.fileName()));
            setValue(++m_iProgress, QIcon::fromTheme("dialog-error-symbolic"),
                     _("Program %1 is not executable file.").arg(f.fileName()));
            return false;
        }
        m_oProcess->setProgram(f.fileName());
        m_oArguments << "-v";
        m_oArguments << QString("dev=%1").arg(m_oCloneCDROM->device());
        m_oArguments << "driveropts=burnfree";
    }
    else if ( (m_oCloneCDROM->mediumType().contains("DVD+R DL"))
             || (m_oCloneCDROM->mediumType().contains("DVD+RW"))
             || (m_oCloneCDROM->mediumType().contains("DVD-RW"))
             || (m_oCloneCDROM->mediumType().compare("DVD+R") == 0 && m_oCloneCDROM->display().contains("ASUS SDRW-08U1MT")) )
    {
        LOGGER->info(QString("ProcessBurnImage::prepare() 光驱：%1， 光盘：%2, 使用xorriso命令刻录镜像").arg(m_oCloneCDROM->display()).arg(m_oCloneCDROM->mediumType()));
        //xorriso -as cdrecord -v dev=/dev/sr0 blank=as_needed -multi -eject /media/kylin/Kylin-Deskt/ISO/ubuntu-14.04.5-server-ppc64el.iso
        f.setFile("/bin/xorriso");
        if (!f.exists())
        {
            LOGGER->error(_("Cannot find program <%1>.").arg(f.fileName()));
            setValue(++m_iProgress, QIcon::fromTheme("dialog-error-symbolic"),
                     _("Cannot find program <%1>.").arg(f.fileName()));
            return false;
        }

        if (!f.isExecutable())
        {
            LOGGER->error(_("Program %1 is not executable file.").arg(f.fileName()));
            setValue(++m_iProgress, QIcon::fromTheme("dialog-error-symbolic"),
                     _("Program %1 is not executable file.").arg(f.fileName()));
            return false;
        }

        m_oProcess->setProgram("/usr/bin/xorriso");
        m_oArguments << "-as" << "cdrecord" << "-v" << QString("dev=%1").arg(m_oCloneCDROM->device()) << "blank=as_needed" << "-multi" << "-eject" << m_oImage;

        if(m_isSpecialMachine==true){
            auditburner->set("burner-os-auditlog-name",auditlogpath);
            m_oBurnImageLog.appendFile(m_oImage);
        }else{
            auditburner->set("burner-os-auditlog-name","");
        }
    }
    else if (m_oCloneCDROM->mediumType().startsWith("DVD"))
    {
        f.setFile("/bin/growisofs");
        if (!f.exists())
        {
            LOGGER->error(_("Cannot find program <%1>.").arg(f.fileName()));
            setValue(++m_iProgress, QIcon::fromTheme("dialog-error-symbolic"),
                     _("Cannot find program <%1>.").arg(f.fileName()));
            return false;
        }
        if (!f.isExecutable())
        {
            LOGGER->error(_("Program %1 is not executable file.").arg(f.fileName()));
            setValue(++m_iProgress, QIcon::fromTheme("dialog-error-symbolic"),
                     _("Program %1 is not executable file.").arg(f.fileName()));
            return false;
        }
        m_oProcess->setProgram(f.fileName());
        m_oArguments << "-dvd-compat";
        m_oArguments << "-Z";
        m_oArguments << QString("%1=%2").arg(m_oCloneCDROM->device()).arg(m_oImage);
        if(m_isSpecialMachine==true){
            auditburner->set("burner-os-auditlog-name",auditlogpath);
            m_oBurnImageLog.appendFile(m_oImage);
        }
        else
        {
            auditburner->set("burner-os-auditlog-name","");

        }
    }
    else
    {
        LOGGER->error(_("Donot suppot medium type %1 in CDROM.").arg(m_oCloneCDROM->mediumType()));
        setValue(++m_iProgress, QIcon::fromTheme("dialog-error-symbolic"),
                 _("Donot suppot medium type %1 in CDROM.").arg(m_oCloneCDROM->mediumType()));
        return false;
    }
    LOGGER->info(_("Checking program %1 can be supported.").arg(m_oProcess->program()));
    setValue(++m_iProgress, m_oIcon, _("Checking program %1 can be supported.").arg(m_oProcess->program()));
    return m_oKeep;
}

bool ProcessBurnImage::config()
{
    int speed;
    DataCDROM *cdrom;

    LOGGER->debug(_("Step.%1: getting...").arg("config"));
    setValue(++m_iProgress, m_oIcon, _("Step.%1: getting...").arg("config"));
    if (!verifyCDROM()) {
        return false;
    }
    speed = m_oCloneCDROM->settingSpeed();
    if (0 == speed)
    {
        LOGGER->warn(_("Speed config to 0, means auto."));
        addValue(++m_iProgress, QIcon::fromTheme("dialog-warning-symbolic"), _("Speed config to 0, means auto."));
    }
    if (m_oCloneCDROM->mediumType().startsWith("CD"))
    {
        if (speed) m_oArguments << QString("speed=%1").arg(speed);
        m_oArguments << "-eject";
        m_oArguments << m_oImage;
        if(m_isSpecialMachine==true){
            auditburner->set("burner-os-auditlog-name",auditlogpath);
            m_oBurnImageLog.appendFile(m_oImage);
        }else{
            auditburner->set("burner-os-auditlog-name","");
        }
    }
    else if( (m_oCloneCDROM->mediumType().contains("DVD+R DL"))
             || (m_oCloneCDROM->mediumType().contains("DVD+RW"))
             || (m_oCloneCDROM->mediumType().contains("DVD-RW"))
             || (m_oCloneCDROM->mediumType().compare("DVD+R") == 0 && m_oCloneCDROM->display().contains("ASUS SDRW-08U1MT")) )
    {

        LOGGER->info(QString("ProcessBurnImage::config() 光驱：%1， 光盘：%2, 使用xorriso命令刻录镜像").arg(m_oCloneCDROM->display()).arg(m_oCloneCDROM->mediumType()));
    }
    else if( m_oCloneCDROM->mediumType().startsWith("DVD") )
    {
        //卸载光盘，没地方写了
        m_oCloneCDROM->umount();
        if (speed) m_oArguments << QString("-speed=%1").arg(speed);
        return m_oKeep;
    }
    return m_oKeep;
}

void ProcessBurnImage::slotsStart()
{
    LOGGER->info(_("Started process %1").arg(m_oProcess->program()));
    setValue(++m_iProgress, m_oIcon, _("Started process %1").arg(m_oProcess->program()));
}

void ProcessBurnImage::slotsOutput()
{
    int v;
    qreal basic;
    qreal si;
    qreal t;
    QStringList s;
    QStringList ss;

    QString output = m_oProcess->readAllStandardOutput();

    basic = 99 - m_iProgress;

    //CD 盘刻录镜像
    if (output.contains("Last chance"))
    {
        if (!output.contains("Operation starts."))
        {
            s = output.split(" ");
            s.takeLast();
            setValue(++m_iProgress, m_oIcon, _("Start left %1 seconds.").arg(s.last()));
        }
        setValue(++m_iProgress, m_oIcon, _("Started burn image, cannot stop."));
    }

    if (output.contains("Performing OPC"))
    {
        setValue(++m_iProgress, m_oIcon, _("Performing OPC"));
        cancel(false);
    }

    if (output.startsWith("\rTrack"))
    {
        //  例子：\rTrack 01:  567 of  569 MB written (fifo 100%) [buf 100%]  10.7x.
        s = output.split(" ");
        for (int i = 0; i < s.size(); ++i)
        {
            if (s.at(i).isEmpty())
            {
                s.takeAt(i);
                --i;
            }
        }
        si = s.at(2).toDouble();
        t = s.at(4).toDouble();
        if (s.size() > 5)
        {
            v = m_iProgress + (si / t * basic);
            if(v >= 99) {
                v = 99;
                setValue(99, m_oIcon, _("Fixating...(need waiting a moment.)"));
            }
            else {
                setValue(v , m_oIcon,
                         _("Writing track %1 speed %2 %3 of %4 %5 written.")
                         .arg(s.at(1)).arg(s.last()).arg(s.at(2)).arg(s.at(4)).arg(s.at(5)));
            }
        }
    }

    if (output.contains("Fixating"))
    {
        setValue(99, m_oIcon, _("Fixating...(need waiting a moment.)"));
    }

    //growisofs刻录DVD镜像
    if (!output.isEmpty())
    {
        LOGGER->debug(_("%1 output : \"%2\"").arg(m_oProcess->program()).arg(output));
        if (m_oProcess->program().contains("growisofs"))
        {
            // 例子：168329216/597309440 (28.2%) @2.4x, remaining 2:27 RBU 100.0% UBU  99.3%\n
            if (output.contains("UBU"))
            {
                s = output.split(")");
                ss = s.last().split("x,");
                ss = ss.first().split("@");
                m_fSpeed = ss.last().toDouble();
                s = s.first().split("(");
                t = s.last().split("%").first().toDouble();
                v = m_iProgress + ( t / 100 * basic );
                if (v > 99) {
                    v = 99;
                    setValue(v, m_oIcon, "");
                } else if (m_fSpeed > 0.00) {
                    setValue(v, m_oIcon, _("Burning image by speed %1x").arg(m_fSpeed));
                }
            }
        }
    }

    LOGGER->info(output);
}

void ProcessBurnImage::slotsError()
{
    QString output = m_oProcess->readAllStandardError();

    QList<QString> errorInfoList;
    errorInfoList << "Input/output error" << "OPC failed." << "FATAL:" << ":-(" << ":-["
                  << "xorriso : aborting" << "seems to be mounted!" << "target is busy" << "Cannot open busy device";

    bool isErrorInfo = false;
    foreach (QString index, errorInfoList) {
        if(output.contains(index)){
            m_oKeep = false;
            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("xorriso : aborting")) {
        m_lastErrorMsg = QString(_("Burning command aborted."));
    }

    if(output.contains("seems to be mounted!")) {
        // CD盘光盘不能卸载
        m_lastErrorMsg = QString(_("Disc umount failed."));
    }

    if(output.contains("target is busy")) {
        // CD盘光盘不能卸载
        m_lastErrorMsg = QString(_("Disc is busy , umount failed."));
    }

    if(output.contains("Cannot open busy device")) {
        //xorriso卸载失败
        m_oKeep = false;
        m_lastErrorMsg = QString(_("Disc is busy , umount failed."));
    }

    if(output.contains("No such device")) {
        m_lastErrorMsg = QString(_("No such device."));
        /** CD盘刻录镜像时，光驱拔出后报错，进程需等待10分钟结束，不会立即结束，用户感知刻录镜像
         * 失败后不弹框，所以直接杀死进程
         **/
        if (m_oProcess->state() != QProcess::NotRunning)
        {
            m_oKeep = false;
            LOGGER->debug(_("An error occurs when the CD is removed during image burning. The image burning process is terminated."));
            m_oProcess->kill();
        }
    }

    if(output.contains(":-( write failed: Invalid argument") ) {
        m_oErrorAndIgnoreInfo.append(":-( write failed: Invalid argument");
    }

    if (!output.isEmpty())
    {
        //xorriso刻录镜像slotsError
        if(m_oProcess->program().contains("xorriso"))
        {
            //qDebug() << "slotError:  运行的程序为 xorriso";
            if( output.startsWith("xorriso : UPDATE : Blanking") )
            {
                QRegExp rx2("(\\d+\.\\d)");
                int value1;
                int value2;
                int pos2 = rx2.indexIn(output);
                if (pos2 > -1)
                {
                    value1 = rx2.cap(1).toInt();
                    //qDebug() << "value1 = " << value1;
                }
                value2 = m_iProgress + (value1 / 100.0 * (49 - m_iProgress));
                if(value2 > 49) value2 =49;
                setValue(value2, m_oIcon, _("Blanking... "));
            }
            else if( output.startsWith("xorriso : UPDATE : Formatting"))
            {
                // 例子：xorriso : UPDATE : Blanking  ( 1.0% done in 1 seconds )
                setValue(m_iProgress, m_oIcon, _("Formatting..."));
            }
            else if( output.startsWith("xorriso : UPDATE : Thank you for being patient."))
            {
                setValue(m_iProgress, m_oIcon, _("Preparing for burning image(wait a few minutes)")); //刻录镜像准备中(需要等待几分钟)
            }
            else if( output.startsWith("xorriso : UPDATE :") && output.contains("written"))
            {
                //例子：xorriso : UPDATE :    0 of  569 MB written (fifo 99%) [buf 100%]   0.4x.
                QRegExp rx3("(\\d+)\\s+of\\s+(\\d+)");
                int value1;
                int value2;
                QString speed;
                int pos3 = rx3.indexIn(output);
                if (pos3 > -1)
                {
                    value1 = rx3.cap(1).toInt();    //获取已刻录镜像的容量

                    value2 = rx3.cap(2).toInt();    //获取镜像的总容量

                }
                QRegExp rx4("(\\d+\.\\d[x])");
                int pos4 = rx4.indexIn(output);
                if (pos4 > -1)
                {
                    speed = rx4.cap(1);     //获取刻录的速度

                }
                int value3 = m_iProgress + ((value1 * 1.0) / (value2 * 1.0) * 98);
                if(value3 > 98) value3 = 98;
                setValue(value3, m_oIcon, _("Burning image by speed %1. %2 of %3 written.").arg(speed).arg(value1).arg(value2)); //速度 0.4x.已写入569M中的1M
            }
            else if(output.startsWith("Blanking done"))
            {
                m_iProgress = 50;
                setValue(m_iProgress, m_oIcon, _("Blanking done"));
            }
            else if(output.contains("Closing track/session")){
                setValue(99, m_oIcon, _("Closing track/session"));
            }
            else if(output.contains("completed successfully.")){
                setValue(99, m_oIcon, _("burn successfully"));
            }
        }
    }

    if(isErrorInfo){
        LOGGER->error(output);
    }else{
        LOGGER->info(output);
    }

    if (output.contains("libburn : FATAL : SCSI error on write")) {
        m_lastErrorMsg = QString(_("Input/output error. Disc may be damaged, please clean or replace a disc and try again."));
    }
    emit cancel(false);
}

void ProcessBurnImage::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 ("cdrecord" == m_oProcess->program())
    {
        if (QProcess::NormalExit == status  && 0 == code)
        {
            LOGGER->info(QString("file:[%1] line:[%2] : slotsFinished() CD Medium burn image success.").arg(__FILE__).arg(__LINE__));
        }
        else
        {
            LOGGER->error(QString("file:[%1] line:[%2] : slotsFinished() CD Medium failure in burn image. [%3]").arg(__FILE__).arg(__LINE__).arg(m_oProcess->errorString()));
            m_oKeep = false;
        }
    }
    else //DVD
    {
        if (QProcess::NormalExit != status  || code != 0)
        {
            m_oKeep = false;
            LOGGER->error(QString("file:[%1] line:[%2] : slotsFinished() DVD Medium have error in burn image. [%3]").arg(__FILE__).arg(__LINE__).arg(m_oProcess->errorString()));
        }
        else {
            if(m_oErrorAndIgnoreInfo.size() > 0) {
                m_oKeep = true;
                m_oErrorAndIgnoreInfo.clear();
                qDebug() << __LINE__ << __func__ << "出现 :-( write failed : flushing cache, 不认为是错误输出  m_oKeep = " << m_oKeep ;
            }

            LOGGER->info(QString("file:[%1] line:[%2] : slotsFinished() DVD Medium burn image success.").arg(__FILE__).arg(__LINE__));
       }
    }
    //m_oBurnImageLog.outputResult(m_oCloneCDROM->display(), m_oCloneCDROM->mediumType(), m_oProcessName, true==m_oKeep);
    if(m_isSpecialMachine==true){
        auditburner->set("burner-os-auditlog-name",auditlogpath);
        m_oBurnImageLog.outputResult(m_oCloneCDROM->display(), m_oCloneCDROM->mediumType(), m_oProcessName, true==m_oKeep);
    }else{
        auditburner->set("burner-os-auditlog-name","");
    }
}

void ProcessBurnImage::slotsRemoveDisc(QString dev)
{
    if(dev == m_oCloneCDROM->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();
        }
    }
}

bool ProcessBurnImage::verifyCDROM()
{
    if (nullptr == m_oCloneCDROM)
    {
        LOGGER->error(_("CDROM doesnot exists any more."));
        addValue(0, QIcon::fromTheme("dialog-error-symbolic"), _("CDROM doesnot exists any more."));
        return false;
    }
    if (!m_oCloneCDROM->isCDROM())
    {
        LOGGER->error(_("CDROM doesnot a real physical cdrom."));
        addValue(0, QIcon::fromTheme("dialog-error-symbolic"), _("CDROM doesnot a real physical cdrom."));
        return false;
    }
    if (DataMedium::MEDIUM_EMPTY != m_oCloneCDROM->mediumStatus())
    {
        LOGGER->error(_("Medium in CDROM is not empty any more."));
        addValue(0, QIcon::fromTheme("dialog-error-symbolic"), _("Medium in CDROM is not empty any more."));
        return false;
    }
    return true;
}

ProcessBurnImage::~ProcessBurnImage()
{
    if (auditburner){
        delete auditburner;
        auditburner = nullptr;
    }
    LOGGER->info("( ProcessBurnImage::~ProcessBurnImage) -------------");
    //g_user_signal->setDeviceBusyStatus(m_oDeviceBlock,false);

}
