#include "datacdrom.h"
#include "src/frame/logger.h"
#include "src/frame/signals.h"
#include "src/po/translation.h"
#include "src/view/modaldialog.h"
#include "src/model/logicmain.h"
#include "src/process/processearse.h"
#include "src/process/processmediainfo.h"
#include "src/process/processisoinfo.h"
#include "src/data/iso9660.h"
#include "src/data/iso9660backend.h"
#include "src/status-fix/discscsi.h"

#include <unistd.h>
#include <fcntl.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <mntent.h>

#include <QThread>
#include <QDebug>
#include <QCoreApplication>
#include <QFileInfo>
#include <QTimer>
#include <QDate>

#define WAIT_SEC   10
#define FACTOR_CD  175
#define FACTOR_DVD 1385

DataMedium::DataMedium(QString mediuType, QString blockName, QObject *parent) :
    QObject(parent),
    m_oMediumType(mediuType),
    m_oBlockName(blockName),
    m_bAppRunning(true),
    m_bErasable(false),
    m_u64Capacity(0),
    m_u64FreeCapacity(0),
    m_u32TrackNumber(0),
    m_u64UsedCapacity(0),
    m_mountPointTimer(new QTimer),
    m_oCDROM(qobject_cast<DataCDROM *>(parent))
{

    m_oCapacity = _("%1 B").arg(0.00);
    m_oUsedCapacity = _("%1 B").arg(0.00);
    m_oFreeCapacity = _("%1 B").arg(0.00);
    m_oMediumName.clear();
    m_oCDROM->updateVerficationData(false); // 刚插入的光盘默认不进行校验数据
    connect(this, SIGNAL(info(QString)), LOGGER, SLOT(info(QString)));
    connect(this, SIGNAL(warn(QString)), LOGGER, SLOT(warn(QString)));
    connect(this, SIGNAL(error(QString)), LOGGER, SLOT(error(QString)));
    connect(this, SIGNAL(debug(QString)), LOGGER, SLOT(debug(QString)));
    LOGGER->registerName(this, _("Medium Data<%1>").arg(m_oBlockName));
    connect(MAIN, &LogicMain::exit, [=](){m_bAppRunning = false;});

    //UDF功能：不管做什么操作，刚插入光盘显示为ISO9660格式
    connect(g_user_signal,SIGNAL(changBurnFormat()),this,SLOT(slotsChangeBurnFormat_CDROM()));
#if 0
    connect(this, SIGNAL(empty()), m_oCDROM, SIGNAL(empty()));
    connect(this, SIGNAL(appendale()), m_oCDROM, SIGNAL(appendale()));
    connect(this, SIGNAL(finished()), m_oCDROM, SIGNAL(finished()));
#endif
    if (m_oMediumType.startsWith("CD"))
        m_oIcon = QIcon::fromTheme("media-optical-cd-audio");
    else
        m_oIcon = QIcon::fromTheme("media-optical-dvd");
    //load();
    m_discread = new DiscRead(blockName);

    connect(m_mountPointTimer, &QTimer::timeout, this, &DataMedium::slotsMountPoint);
}

DataMedium::DataMedium(DataMedium &other, QObject *parent) :
    QObject(parent)
{
    m_u8Status = other.status();
    //m_bAppRunning;
    m_bErasable = other.isErasable();
    //m_bUDF;
    m_bEnableUDF = other.isEnableUDF()    ;
    m_oCDROM = qobject_cast<DataCDROM *>(parent);
    //m_u32TrackNumber;
    m_u64UsedCapacity = other.get32UsedCapacity();
    m_u64FreeCapacity = other.get32FreeCapacity();
    m_u64Capacity  = other.get32Capacity();
    //m_oVolumnID;
    m_oBlockName = other.getBlockName();
    m_oMediumType = other.getType();
    m_oUsedCapacity = other.getUsedCapacity();
    m_oFreeCapacity = other.getFreeCapacity();
    m_oCapacity = other.getCapacity();
    m_oMountPoint = other.getMountPoint();
    //m_oMediumName;
    m_oDisplay = other.display();
    m_oIcon = other.icon();
    m_iSpeeds = other.getNumSpeed();
    m_oSpeeds = other.speeds();
    m_mountPointTimer = nullptr;
}

void DataMedium::load()
{
    m_oCDROM->open();
    type();
    /*原本通过ProcessISO获得光盘的volmnID、publisher、preparer和copyright，现不获得，所以现不需要这个类
    ProcessISOInfo *info;
    if (m_u8Status != MEDIUM_EMPTY)
    {
        info = new ProcessISOInfo(m_oCDROM, this);
        info->exec();
        delete info;
    }
    */
    //capacity();
    //if (m_u8Status != MEDIUM_FINALIZED && 0 == m_u64FreeCapacity) capacity();
    speed();
    m_oCDROM->close();
//    if (!(m_u8Status != MEDIUM_EMPTY && m_u64UsedCapacity == 0 && m_u64FreeCapacity !=0))
//        mountPoint();
    m_oDisplay = _("%1 - %2 medium, left space : %3")
            .arg(m_oMediumName.isEmpty() ? _("Empty medium") : m_oCDROM->settingVolumnID())
            .arg(m_oMediumType).arg(m_oFreeCapacity);
#if 0
    m_oDisplay = _("%1 - %2 (%3) medium, left space : %4")
            .arg(m_oMediumName.isEmpty() ? _("Empty medium") : m_oCDROM->settingVolumnID())
            .arg(m_bUDF ? "UDF" : "")
            .arg(m_oMediumType).arg(m_oFreeCapacity);
#endif
    //UDF功能
    EnableUDF();
    emit g_user_signal->changBurnFormat();
    connect(m_discread, &DiscRead::workFinished, this, &DataMedium::fixDiscStatus);
    m_discread->work();
}

void DataMedium::fixDiscStatus(){
    m_discread->disconnect(); //关闭connect,可能会有第二次，使用不同槽函数

    if(m_discread->isBlank()){
        m_u8Status = MEDIUM_EMPTY;
    }else if(m_discread->canAppend()){
        m_u8Status = MEDIUM_INCOMPLETE;
    }else
        m_u8Status = MEDIUM_FINALIZED;

    capacity();
    m_mountPointTimer->start(1000);
//    m_oDisplay = _("%1 - %2 medium, left space : %3")
//        .arg(m_oMediumName.isEmpty() ? _("Empty medium") : m_oCDROM->settingVolumnID())
//        .arg(m_oMediumType).arg(m_oFreeCapacity);

    //    Q_EMIT g_user_signal->changeDiscStatus();
}

void DataMedium::secondFixDiscStatus()
{
    qDebug() << "m_discread->canAppend() = " << m_discread->canAppend();
    if(m_discread->canAppend()) {
        m_u8Status = MEDIUM_INCOMPLETE;
        capacity();
        Q_EMIT g_user_signal->secondChangeDiscStatus();
         qDebug() << QString("[%1][%2] 挂载点已查到：[%3]  定时器已关，发送信号").arg(__func__).arg(__LINE__).arg(m_oMountPoint);

         if (!m_discread->discLabel().isEmpty()) {
                 m_oVolumnID = m_discread->discLabel();
                 qDebug() << QString("[%1][%2]: set卷标=%3").arg(__func__).arg(__LINE__).arg(m_oVolumnID);
                 m_oCDROM->setVolumnID(m_oVolumnID);
                 m_oDisplay = _("%1 - %2 medium, left space : %3")
                         .arg(m_oMediumName.isEmpty() ? _("Empty medium") : m_oVolumnID)
                         .arg(m_oMediumType).arg(m_oFreeCapacity);
                 qDebug()<<__LINE__<<__func__<<m_oFreeCapacity;
             } else {
                 QString currentTime = QDate::currentDate().toString("yyyy-MM-dd");
                 //m_oCDROM->setVolumnID(currentTime); // V10空卷标时使用当前日期作为卷标
                 m_oCDROM->setVolumnID(_("KylinBurner"));
                 m_oDisplay = _("%1 - %2 medium, left space : %3")
                         .arg(m_oMediumName.isEmpty() ? _("Empty medium") : _("KylinBurner"))
                         .arg(m_oMediumType).arg(m_oFreeCapacity);
             }

//             m_oDisplay = _("%1 - %2 medium, left space : %3")
//                 .arg(m_oMediumName.isEmpty() ? _("Empty medium") : m_oCDROM->settingVolumnID())
//                 .arg(m_oMediumType).arg(m_oFreeCapacity);
    }

}

void DataMedium::slotsMountPoint()
{
    qDebug() << QString("[%1][%2] 进入start").arg(__func__).arg(__LINE__);

    if ((m_u8Status > MEDIUM_EMPTY && m_u8Status < MEDIUM_OTHER) &&
            (m_oMountPoint.isEmpty() || m_oMediumName.isEmpty()) &&
            m_bAppRunning)
    {
        FILE *fp = nullptr;
        struct mntent *mp = nullptr;

        fp = setmntent("/proc/mounts", "r");
        if (nullptr == fp)
        {
            error(_("Cannot load mount information.%1").arg(strerror(errno)));
            //qDebug() << QString("[%1][%2] ").arg(__func__).arg(__LINE__);
            return;
        }
        while (nullptr != (mp = getmntent(fp))) {
            if (QString::fromUtf8(mp->mnt_fsname) == m_oBlockName)
            {
                m_oMountPoint = QString::fromUtf8(mp->mnt_dir);
                m_oMediumName = m_oMountPoint.split("/").takeLast();
                if (QString::fromUtf8(mp->mnt_type).contains("UDF") ||
                        QString::fromUtf8(mp->mnt_type).contains("udf"))
                {
                    m_bUDF = true;
                    debug(_("Medium %1 mounted by UDF").arg(m_oMountPoint));
                }

                // Fix 112786 获取挂载点后，再次确认光盘状态
                if(m_u8Status != MEDIUM_INCOMPLETE) {
                    connect(m_discread, &DiscRead::workFinished, this, &DataMedium::secondFixDiscStatus);
                    m_discread->work();
                }
                m_mountPointTimer->stop();
                m_mountPointTimer->disconnect();
                //qDebug() << QString("[%1][%2] 挂载点已查到：[%3]  定时器已关").arg(__func__).arg(__LINE__).arg(m_oMountPoint);
                info(QString("[%1][%2] 挂载点已查到：[%3]  定时器已关").arg(__func__).arg(__LINE__).arg(m_oMountPoint));
                break;
            }
        }
        endmntent(fp);
    } else {
        if (m_u8Status == MEDIUM_EMPTY) {
            m_mountPointTimer->stop();
            m_mountPointTimer->disconnect();
            //qDebug() << QString("[%1][%2] 空光盘  定时器已关").arg(__func__).arg(__LINE__);
            info(QString("[%1][%2] 空光盘  定时器已关").arg(__func__).arg(__LINE__));
        }
    }

    if (!m_discread->discLabel().isEmpty()) {
        m_oVolumnID = m_discread->discLabel();
        qDebug() << QString("[%1][%2]: set卷标=%3").arg(__func__).arg(__LINE__).arg(m_oVolumnID);
        m_oCDROM->setVolumnID(m_oVolumnID);
        m_oDisplay = _("%1 - %2 medium, left space : %3")
                .arg(m_oMediumName.isEmpty() ? _("Empty medium") : m_oVolumnID)
                .arg(m_oMediumType).arg(m_oFreeCapacity);
    } else {
        QString currentTime = QDate::currentDate().toString("yyyy-MM-dd");
        //m_oCDROM->setVolumnID(currentTime); // V10空卷标时使用当前日期作为卷标
        m_oCDROM->setVolumnID(_("KylinBurner"));
        m_oDisplay = _("%1 - %2 medium, left space : %3")
                .arg(m_oMediumName.isEmpty() ? _("Empty medium") : _("KylinBurner"))
                .arg(m_oMediumType).arg(m_oFreeCapacity);
    }

    m_oDisplay = _("%1 - %2 medium, left space : %3")
        .arg(m_oMediumName.isEmpty() ? _("Empty medium") : m_oCDROM->settingVolumnID())
        .arg(m_oMediumType).arg(m_oFreeCapacity);

    Q_EMIT g_user_signal->changeDiscStatus();
}

void DataMedium::slotsChangeBurnFormat_CDROM()
{
    //qDebug()<<"signals:changBurnFormat()-->DataMedium:slotsChangeBurnFormat_CDROM--->DataCDROM burnFormat()";
    emit m_oCDROM->burnFormat(QString("ISO9660"));
}

void DataMedium::EnableUDF()
{
    if(m_oMediumType.startsWith("DVD") && MEDIUM_EMPTY == m_u8Status)
    {
        //qDebug()<<"DataMedium::EnableUDF()  m_bEnableUDF == [true]";
        m_bEnableUDF = true;
    }
    else
    {
        //qDebug()<<"DataMedium::EnableUDF()  m_bEnableUDF == [false]";
        m_bEnableUDF = false;
    }
}

void DataMedium::type()
{
    unsigned short len = 0;
    ProcessISOInfo *isoInfo;
    unsigned char  cdb[10] = { 0x51,
                               0x00,
                               0x00,
                               0x00,
                               0x00,
                               0x00,
                               0x00,
                               0x00,
                               0x20,
                               0 };
    unsigned char  result[32] = { 0 };

    if (!m_oCDROM->execSCSI(cdb, 10, result, 32)) return;

    len = (result[0] << 8) | result[1];
    if ((len - 32) % 8)
    {
        error(_("Get wrong response when load medium information."));
        return; // len is 32 + 8 * n
    }

    if (((result[2] & 0x10) >> 4) == 1)
    {
        debug(_("Can erase mediun"));
        m_bErasable = true;
    }

    switch (result[2] & 0x03)
    {
    case 0x00: // empty
        m_u8Status = MEDIUM_EMPTY;
        debug(_("empty mediun."));
        //emit empty();
        break;
    case 0x01: // incomplete
        m_u8Status = MEDIUM_INCOMPLETE;
        //mountPoint();
//#if 1
//        if (m_oMediumType.contains("DVD-RW") && m_oMountPoint.isEmpty())
//        {
//            m_u8Status = MEDIUM_EMPTY;
//        }
//#endif
        debug(_("incomplete mediun."));
        //emit appendale();
        break;
    case 0x02:
        m_u8Status = MEDIUM_FINALIZED;
//        if ((m_oMediumType.contains("DVD+RW") || m_oMediumType.contains("DVD-RW") )
//                && m_oMountPoint.isEmpty())
//        {
//            isoInfo = new ProcessISOInfo(m_oCDROM, this);
//            isoInfo->exec();
//            if (isoInfo->isEmpty())
//            {
//                delete isoInfo;
//                m_u8Status = MEDIUM_EMPTY;
//                debug(_("empty DVD+RW mediun."));
//                break;
//            }
//            delete isoInfo;
//        }
        //mountPoint();
        debug(_("finalized mediun."));
        //emit finished();
        break;
    case 0x03:
        m_u8Status = MEDIUM_OTHER;
        break;
    default:
        break;
    }

    m_u32TrackNumber = (((result[11] << 8) & 0xFF00) | result[6]);
}

void DataMedium::capacity()
{
    unsigned char  cdb[10] = { 0x52,
                               0x01,
                               0x00,
                               0x00,
                               0x00,
                               0x00,
                               0x00,
                               0x00,
                               0x28,
                               0 };
    unsigned char  result[40] = { 0 }; // 256 + 12

    if (MEDIUM_FINALIZED == m_u8Status) // finalized all 0.
    {
        qDebug()<<__LINE__<<__func__<<"加载失败";
        warn(_("Donot load type of finalized capacity."));
        return;
    }
    if (m_oMediumType.contains("DVD+RW") || m_oMediumType.contains("DVD-RW"))
    {
        //计算光盘总容量
        ProcessMediaInfo media(m_oCDROM, this);
        media.exec();
        m_u64Capacity = media.size();
        if (MEDIUM_EMPTY == m_u8Status)
        {
            m_u64FreeCapacity = m_u64Capacity;
            m_oFreeCapacity = toUnitString(m_u64FreeCapacity);
            m_oCapacity = toUnitString(m_u64Capacity);
        }
        else if (m_oMediumType.contains("DVD-RW"))
        {
            m_u64FreeCapacity = media.size();
            m_oFreeCapacity = toUnitString(m_u64FreeCapacity);
        }
        else {}
        debug(_("SIZE IS %1 | %2").arg(m_u64Capacity).arg(m_oCapacity));

        //计算DVD-RW和DVD+RW的已使用容量
        if (m_u8Status == MEDIUM_INCOMPLETE) {
            Iso9660SimplePrimaryDescriptor isoDesc;
            isoDesc = Iso9660SimplePrimaryDescriptor();
            Iso9660 iso( new Iso9660DeviceBackend( m_oCDROM ) );
            iso.setStartSector( 0 );
            iso.setPlainIso9660( true );
            if( iso.open() ) {
                isoDesc = iso.primaryDescriptor();
                m_u64UsedCapacity = isoDesc.getUsedCapacity();

                m_oCapacity = toUnitString(m_u64Capacity);
                m_oUsedCapacity = toUnitString(m_u64UsedCapacity);
                m_u64FreeCapacity = m_u64Capacity - m_u64UsedCapacity;
                m_oFreeCapacity = toUnitString(m_u64FreeCapacity);

                info(_("Capacity: %1 - used(%2) - free(%3)").arg(m_oCapacity).arg(m_oUsedCapacity).arg(m_oFreeCapacity));
            } else {
                // 没有获取到已使用数据容量，可能是Win刻录的UDF盘或空光盘
                info(QString("[%1][%2] Failed to obtain the capacity. It may be a real UDF disk").arg(__func__).arg(__LINE__));
            }
            info(QString("[%1][%2] DVD+/-RW Capacity calculation completed"));

        }
        qDebug() << __func__ << __LINE__ << "容量计算完成";
        return;
    }

    cdb[2] = (m_u32TrackNumber >> 24);
    cdb[3] = (m_u32TrackNumber >> 16);
    cdb[4] = (m_u32TrackNumber >> 8);
    cdb[5] = m_u32TrackNumber;

    if (!m_oCDROM->execSCSI(cdb, 10, result, 40)) return;

    m_u64UsedCapacity = ((result[8] << 24) | (result[9] << 16) | (result[10] << 8) | result[11]);
    m_u64UsedCapacity *= 2048;
    m_u64FreeCapacity = ((result[16] << 24) | (result[17] << 16) | (result[18] << 8) | result[19]);
    m_u64FreeCapacity *= 2048;
    m_u64Capacity = m_u64UsedCapacity + m_u64FreeCapacity;

    m_oCapacity = toUnitString(m_u64Capacity);
    m_oUsedCapacity = toUnitString(m_u64UsedCapacity);
    m_oFreeCapacity = toUnitString(m_u64FreeCapacity);

    debug(_("Capacity: %1 - used(%2) - free(%3)").arg(m_oCapacity).arg(m_oUsedCapacity).arg(m_oFreeCapacity));
}

void DataMedium::speed()
{
    int            maxSpeed = 0;
    int            minSpeed = 0;
    int            factor = 0;
    unsigned short len = 0;
    unsigned short len2 = 0;
    unsigned char  cdb[12] = { 0xAC, // Get Performance
                               0x00, //
                               0x00,
                               0x00,
                               0x00,
                               0x00, // LBA
                               0x00, // reserve
                               0x00, // reserve
                               0x00,
                               0x00, // Maximun Number of Descriptors
                               0x03, /* type
                                      * 00 Performance data
                                      * 01 Unusable Area data
                                      * 02 Defect Status data
                                      * 03 Write Speed Descriptor
                                      * ...
                                      * MMC-5 6.8.2.5
                                      */
                               0 };
    unsigned char  result[2048] = { 0 };

    if (!m_oCDROM->execSCSI(cdb, 12, result, 8)) return;
    len = (result[0] << 24) | (result[1] << 16) | (result[2] << 8) | result[3];
    len += 4;

    len2 = len - 8; len2 /= 16;
    cdb[9] = (len2 & 0xFF);
    cdb[8] = ((len2 & 0xFF00) >> 8);
    if (!m_oCDROM->execSCSI(cdb, 12, result, len)) return;
    m_iSpeeds.clear();
    for (int i = 0; i < len2; ++i)
    {
        QCoreApplication::processEvents();
        m_iSpeeds << ((result[i * 16 + 20] << 24) |
                (result[i * 16 + 21] << 16) |
                (result[i * 16 + 22] << 8) |
                result[i * 16 + 23]);
    }
    if (m_iSpeeds.size() <= 0) return;
    maxSpeed = m_iSpeeds.first();
    minSpeed = m_iSpeeds.last();
    if (0 == maxSpeed)
    {
        error(_("Get invalid speed."));
        return;
    }
    if (m_oMediumType.startsWith("CD"))
        factor = FACTOR_CD;
    else
        factor = FACTOR_DVD;
    for (int i = 0; i < m_iSpeeds.size(); ++i)
    {
        m_oSpeeds << QString("%1").arg(m_iSpeeds.at(i) / factor);
        debug(_("Speed %1 : %2x - rate : %3 Kbps").arg(i).arg(m_oSpeeds.at(i)).arg(m_iSpeeds.at(i)));
    }

}

//弃用，使用slotMountPoint()
void DataMedium::mountPoint()
{
    FILE *fp = nullptr;
    struct mntent *mp = nullptr;
#if 0
    if (MEDIUM_EMPTY == m_u8Status)
    {
        warn(_("No mount point for empty type medium."));
        return;
    }
#endif
    do
    {
        //QCoreApplication::processEvents();
        fp = setmntent("/proc/mounts", "r");
        if (nullptr == fp)
        {
            error(_("Cannot load mount information.%1").arg(strerror(errno)));
            return;
        }
        m_oMountPoint.clear();
        while (mp = getmntent(fp))
        {
            //QCoreApplication::processEvents();
            if (QString::fromUtf8(mp->mnt_fsname) == m_oBlockName)
            {
                m_oMountPoint = QString::fromUtf8(mp->mnt_dir);
                m_oMediumName = m_oMountPoint.split("/").takeLast();
                if (QString::fromUtf8(mp->mnt_type).contains("UDF") ||
                        QString::fromUtf8(mp->mnt_type).contains("udf"))
                {
                    m_bUDF = true;
                    debug(_("Medium %1 mounted by UDF").arg(m_oMountPoint));
                }
                break;
            }
        }
        endmntent(fp);
        //if (m_oMediumType.contains("DVD-RW")) break;
    }  while ((m_u8Status > MEDIUM_EMPTY && m_u8Status < MEDIUM_OTHER) &&
              (m_oMountPoint.isEmpty() || m_oMediumName.isEmpty()) &&
              m_bAppRunning);
    debug(_("Mouted at %1, Medium name is %2").arg(m_oMountPoint).arg(m_oMediumName));
}

QString DataMedium::toUnitString(unsigned long size64)
{
    qreal size;
    int u;
    QString ret;

    size = size64 * 1.0;
    u = -1;
    ret.clear();

    while (size > 1024.00)
    {
        QCoreApplication::processEvents();
        ++u; size /= 1024.00;
    }

    ret = QString("%1 ").arg(size, 0, 'f', 1);

    switch (u)
    {
    case 0:
        ret.append(_("KB"));
        break;
    case 1:
        ret.append(_("MB"));
        break;
    case 2:
        ret.append(_("GB"));
        break;
    default:
        ret.append(_("B"));
        break;
    }
    return ret;
}

DataMedium::~DataMedium()
{
    //qDebug()<<"~ Medium" << this << "  DataROM地址为::" << m_oCDROM << " (" <<  m_oCDROM->device() <<") -----------------";
    //qDebug() << "";
    //info("析构Medium");
    if(m_mountPointTimer)
        delete m_mountPointTimer;
    m_mountPointTimer=nullptr;

}

DataCDROM::DataCDROM(QString blockName, QObject *parent) :
    QObject(parent),
    m_oSetting(new DataSetting(this)),
    m_oBlockName(blockName),
    m_bCDROM(true),
    m_bStart(false),
    m_bAppRunning(true),
    m_iHandle(-1),
    m_iUsing(0),
    m_oMedium(nullptr),
    m_bUpdateStart(false),
    m_bPrepare_openDevice(false),
    m_oTimer(new QTimer(this))
{
    m_oMediumType.clear();

    connect(this, SIGNAL(info(QString)), LOGGER, SLOT(info(QString)));
    connect(this, SIGNAL(warn(QString)), LOGGER, SLOT(warn(QString)));
    connect(this, SIGNAL(error(QString)), LOGGER, SLOT(error(QString)));
    connect(this, SIGNAL(debug(QString)), LOGGER, SLOT(debug(QString)));
    LOGGER->registerName(this, _("CDROM Data<%1>").arg(m_oBlockName));
    connect(m_oSetting, SIGNAL(speed(int)), BUSNESSSETTING, SLOT(setSpeed(int)));
    connect(m_oSetting, SIGNAL(clean(bool)), BUSNESSSETTING, SLOT(setClean(bool)));
    connect(m_oSetting, SIGNAL(closeSession(bool)), BUSNESSSETTING, SLOT(setCloseSession(bool)));
    connect(m_oSetting, SIGNAL(open(bool)), BUSNESSSETTING, SLOT(setOpen(bool)));
    connect(m_oSetting, SIGNAL(createISO(bool)), BUSNESSSETTING, SLOT(setCreateISO(bool)));
    connect(m_oSetting, SIGNAL(verficationData(bool)), BUSNESSSETTING, SLOT(setVerficationData(bool)));
    connect(m_oSetting, SIGNAL(preparer(QString)), BUSNESSSETTING, SLOT(setPreparer(QString)));
    connect(m_oSetting, SIGNAL(publisher(QString)), BUSNESSSETTING, SLOT(setPublisher(QString)));
    connect(m_oSetting, SIGNAL(volumnID(QString)), BUSNESSSETTING, SLOT(setVolumnID(QString)));
    connect(m_oSetting, SIGNAL(copyright(QString)), BUSNESSSETTING, SLOT(setCopyright(QString)));
    connect(m_oSetting, SIGNAL(tempPath(QString)), BUSNESSSETTING, SLOT(setTempPath(QString)));
    connect(m_oSetting, SIGNAL(burnFormat(QString)), BUSNESSSETTING, SLOT(setBurnFormat(QString)));

    connect(this, SIGNAL(speed(int)), m_oSetting, SLOT(updateSpeed(int)));
    connect(this, SIGNAL(clean(bool)), m_oSetting, SLOT(updateClean(bool)));
    connect(this, SIGNAL(createISO(bool)), m_oSetting, SLOT(updateCreateISO(bool)));
    connect(this, SIGNAL(closeSession(bool)), m_oSetting, SLOT(updateCloseSession(bool)));
    connect(this, SIGNAL(verficationData(bool)), m_oSetting, SLOT(updateVerficationData(bool)));
    connect(this, SIGNAL(setOpen(bool)), m_oSetting, SLOT(updateOpen(bool)));
    connect(this, SIGNAL(copyright(QString)), m_oSetting, SLOT(updateCopyright(QString)));
    connect(this, SIGNAL(preparer(QString)), m_oSetting, SLOT(updatePreparer(QString)));
    connect(this, SIGNAL(publisher(QString)), m_oSetting, SLOT(updatePublisher(QString)));
    connect(this, SIGNAL(volumnID(QString)), m_oSetting, SLOT(updateVolumnID(QString)));
    connect(this, SIGNAL(volumnID(QString)), this, SLOT(changeISOName(QString)));
    connect(this, SIGNAL(tempPath(QString)), m_oSetting, SLOT(updateTempPath(QString)));
    connect(this, SIGNAL(burnFormat(QString)),m_oSetting, SLOT(updateBurnFormat(QString)));
    connect(MAIN, &LogicMain::exit, [=](){m_bAppRunning = false;});
    connect(m_oTimer, SIGNAL(timeout()), this, SLOT(slotsPrepareTimerOut()));

    if (!m_oBlockName.startsWith("/dev/"))
    {
        m_bCDROM = false;
        m_oDisplay.clear();
        m_oDisplay = _("No need burner.");
    }

    if (!m_bCDROM) changeISOName(m_oSetting->getVolumnID());

    use();

}

DataCDROM::DataCDROM(DataCDROM &other, QString blockName, QObject *parent) :
    QObject(parent)
{
    m_oBlockName = blockName;
    m_bCDROM = true;
    m_bStart = false;
    m_bAppRunning = true;
    m_iHandle = -1;
    m_iUsing = 0;
    m_oMedium = nullptr;
    m_oSetting = nullptr;
    m_bUpdateStart = false;
    m_bPrepare_openDevice = false;

    if (!m_oBlockName.startsWith("/dev/"))
    {
        m_bCDROM = false;
        m_oDisplay.clear();
        m_oDisplay = _("No need burner.");
    }
    use();
}

DataCDROM * DataCDROM::clone()
{
    DataCDROM *cdrom = new DataCDROM(*this, this->m_oBlockName);
    cdrom->m_oSetting = new DataSetting(*this->m_oSetting,cdrom);
    cdrom->m_oMedium = new DataMedium(*this->m_oMedium, cdrom);
    cdrom->m_iHandle = this->m_iHandle;
    cdrom->m_u32MediumRSupport = this->m_u32MediumRSupport;
    cdrom->m_u32MediumWSupport = this->m_u32MediumWSupport;
    cdrom->m_u32BurnModeSupport = this->m_u32BurnModeSupport;
    cdrom->m_bEjected = this->m_bEjected;
    cdrom->m_bCDROM = this->m_bCDROM;
    cdrom->m_bAppRunning = this->m_bAppRunning;
    cdrom->m_oDisplay = this->m_oDisplay;
    cdrom->m_oVendor = this->m_oVendor;
    cdrom->m_oProductor = this->m_oVendor;
    cdrom->m_oBlockName = this->m_oBlockName;
    cdrom->m_oMediumType = this->m_oMediumType;
    cdrom->m_oTimer = nullptr;
    connect(cdrom, &DataCDROM::updateCDROMBurnStart, this, &DataCDROM::slotsUpdateCDROMBurnStart);

    return cdrom;
}

void DataCDROM::umountMedium(QString device)
{
    QString cmd;

    //use BlockName replace MountPoint,Becase MountPoint may has spaces
    //cmd = QString("umount %1").arg(m_oMedium ? m_oMedium->getMountPoint() : QString());
    cmd = QString("umount %1").arg(device);
    ::system(cmd.toUtf8().data());
}

void DataCDROM::update()
{
    connect(g_user_signal, &GlobalUserSignal::changeDiscStatus, this, &DataCDROM::receiveDiscStatusChange);
    use();

    if (!m_bCDROM)
    {
        release();
        return;
    }
    if (m_oMedium)
    {
        delete m_oMedium;
        m_oMedium = nullptr;
    }

    m_oMediumType.clear();
    // real cdrom, load information by block name
    // 0. ready cdrom.

    
    if (isOpen()){
        close();
    }

    if(!open()){
        release();
        return;
    }
    prepare();
    // 1. vendor and descriptio
    display(m_oBlockName);
    // 2. load support mediu types.
    support();
    //checkCDROM();
    // 3. check have medium or not.
    checkMedium();
    close();

    //在插入有光盘的光驱后，checkMedium会有异步可能会使得执行到这，光驱已经
    //拔了，而没有delete掉，所以后面可能出错，所以单独拿出来，做为一个独立的函数去运行
    //appendale()和finished()信号会引发加载数据界面
#if 0
    if (m_oMedium)
    {
        switch (m_oMedium->status())
        {
        case DataMedium::MEDIUM_EMPTY:
            emit empty();
            break;
        case DataMedium::MEDIUM_INCOMPLETE:
            emit appendale();
            break;
        case DataMedium::MEDIUM_FINALIZED:
            emit finished();
            break;
        }
        //speed(m_oMedium->speeds().last().toInt());
    }
#endif

    release();

}

void DataCDROM::receiveDiscStatusChange(){
    if(m_iUsing > 0 && isCDROM() && mediumStatus() >=0){
        sendMediumStatusToOtherView();
    }
}

//在update()后使用，需要判断（有光驱，有光盘）后使用
//在插入有光盘的光驱后，checkMedium会有异步可能会使得执行到这，光驱已经
//拔了，而没有delete掉，在发送光盘信号会导致界面出错。
void DataCDROM::sendMediumStatusToOtherView()
{
    if (m_oMedium)
    {
        switch (m_oMedium->status())
        {
        case DataMedium::MEDIUM_EMPTY:
            emit empty();
            break;
        case DataMedium::MEDIUM_INCOMPLETE:
	    if(!mediumMountPoint().isEmpty())
                emit appendale();
            break;
        case DataMedium::MEDIUM_FINALIZED:
            if(!mediumMountPoint().isEmpty())
                emit finished();
            break;
        }
        //speed(m_oMedium->speeds().last().toInt());
    }
}

void DataCDROM::loadMedium()
{

    if (m_oMedium)
    {
        delete m_oMedium;
        m_oMedium = nullptr;
    }

    if (!open()) return;
    checkMedium();
    close();
    if (m_oMedium)
    {
        qDebug()<<__LINE__<<__func__<<"DataCDROM";
        switch (m_oMedium->status())
        {
        case DataMedium::MEDIUM_EMPTY:
            emit empty();
            break;
        case DataMedium::MEDIUM_INCOMPLETE:
            emit appendale();
            break;
        case DataMedium::MEDIUM_FINALIZED:
            emit finished();
            break;
        }
        //speed(m_oMedium->speeds().last().toInt());
    }
}

void DataCDROM::umount()
{
    QString cmd;

    //use BlockName replace MountPoint,Becase MountPoint may has spaces
    //cmd = QString("umount %1").arg(m_oMedium ? m_oMedium->getMountPoint() : QString());
    cmd = QString("umount %1").arg(m_oMedium ? m_oMedium->getBlockName() : QString());
    ::system(cmd.toUtf8().data());
}

void DataCDROM::eject()
{
#if 1
    QString cmd;

    cmd = QString("eject %1").arg(m_oBlockName);

    ::system(cmd.toUtf8().data());
    if (doorIsOpen()) eject(false);
    else eject(true);
#endif
#if 0
    unsigned char cdb[6] = {  0x1E,
                              0x00,
                              0x00,
                              0x00,
                              0x00, // bit 4 |7 6 5 4 3 2 1 0|
                              0};
    sg_io_hdr_t sgio;
    close();
    open();
    memset(&sgio, 0, sizeof(sg_io_hdr_t));
    sgio.interface_id = 'S';
    sgio.dxfer_direction = SG_DXFER_TO_DEV;
    sgio.flags = SG_FLAG_LUN_INHIBIT;
    sgio.cmdp = (unsigned char *)cdb;
    sgio.cmd_len = 0;

    if (0 != ioctl(m_iHandle, SG_IO, &sgio))
    {
        cdb[0] = 0x1B;
        cdb[4] = 0x2;
    }
    cdb[0] = 0x1B;
    cdb[4] = 0x2;
    memset(&sgio, 0, sizeof(sg_io_hdr_t));
    sgio.interface_id = 'S';
    sgio.dxfer_direction = SG_DXFER_TO_DEV;
    sgio.flags = SG_FLAG_LUN_INHIBIT|SG_FLAG_DIRECT_IO;
    sgio.cmdp = (unsigned char *)cdb;
    sgio.cmd_len = 0;
    if (0 != ioctl(m_iHandle, SG_IO, &sgio))
    {
        cdb[0] = 0x1B;
        cdb[4] = 0x2;
    }
    close();
#endif
}

void DataCDROM::earse()
{
    ProcessEarse earse(this);
    earse.exec();
}

bool DataCDROM::isEjected()
{
    m_bEjected = doorIsOpen();
    return m_bEjected;
}

QString DataCDROM::mDisplay()
{
    if (m_bCDROM)
        return m_oMedium ? m_oMedium->display() : _("No medium");
    else
        return m_oBlockName;
}

QIcon DataCDROM::mediumIcon()
{
     return m_oMedium ? m_oMedium->icon() : QIcon::fromTheme("application-x-cd-image");
     //return m_oMedium ? m_oMedium->icon() : QIcon::fromTheme("application-json");
}

void DataCDROM::updateVerficationData(bool b)
{
    if (m_oSetting) {
        //qInfo() << __func__ << __LINE__ << "设置verficationData的值为：" << b;
        m_oSetting->updateVerficationData(b);
    } else {
        //qInfo() << __func__ << __LINE__ << "m_oSetting为空，不设置verficationData的值为：" << b;
    }
}

bool DataCDROM::open()
{
    use();
    if (-1 != m_iHandle)
    {
        release();
        return true;
    }
    //QThread::msleep(500); // bucause when loading new insert CDROM, will have no rights.
    m_iHandle = ::open(m_oBlockName.toUtf8().constData(), O_NONBLOCK | O_CLOEXEC | O_RDONLY);
    if (m_iHandle < 0)
    {
        error(_("Open block device %1 faied.%2").arg(m_oBlockName).arg(strerror(errno)));
        release();
        return false;
    }
    release();
    return true;
}

void DataCDROM::close()
{
    use();
    if (-1 != m_iHandle) ::close(m_iHandle);
    m_iHandle = -1;
    release();
}

bool DataCDROM::execSCSI(const unsigned char *cdb, const int cdbLength,
                         unsigned char *result, const int resultLength)
{
    sg_io_hdr_t sgio;

    if (!open()) return false;
    if (!cdb || !result || resultLength < 1) return false;
    memset(&sgio, 0, sizeof(sg_io_hdr_t));
    sgio.interface_id = 'S';
    sgio.dxfer_direction = SG_DXFER_FROM_DEV;
    sgio.flags = SG_FLAG_LUN_INHIBIT;
    sgio.cmdp = (unsigned char *)cdb;
    sgio.cmd_len = cdbLength;
    sgio.dxferp = result;
    sgio.dxfer_len = resultLength;

    if (0 != ioctl(m_iHandle, SG_IO, &sgio))
    {
        // error(_("Do SCSI CDB [%1] failed.%2").arg(cdb[0]).arg(strerror(errno)));
        return false;
    }
    return true;
}
bool DataCDROM::read10(unsigned char *data, unsigned int dataLen, unsigned long startAdress, unsigned int length, bool fua)
{
    ::memset( data, 0, dataLen );

    DiscScsi cmd(m_oBlockName);
    cmd[0] = 0x28; //  MMC_READ_10
    cmd[1] = ( fua ? 0x8 : 0x0 );
    cmd[2] = startAdress>>24;
    cmd[3] = startAdress>>16;
    cmd[4] = startAdress>>8;
    cmd[5] = startAdress;
    cmd[7] = length>>8;
    cmd[8] = length;
    cmd[9] = 0;      // Necessary to set the proper command length

    if( !cmd.transport( SCSI_READ, data, dataLen ) ) {
        qDebug()  << m_oBlockName << ": READ 10 failed!";
        return false;
    }
    else
        return true;
}
void DataCDROM::updateMediumUsedSize(unsigned long size)
{
    if (0 == size) return;
    if (m_oMedium && m_oMedium->status() == DataMedium::MEDIUM_FINALIZED)
    {
        m_oMedium->setUsedSize(size);
    }
}

void DataCDROM::prepare()
{
    unsigned char cdb[6] = { 0x00,
                              0x00,
                              0x00,
                              0x00,
                              0x00,
                              0 };
    unsigned char result[8] = { 0 };

    //定时器5秒，如果5s内还卡在open函数中，强制退出该函数
    m_bPrepare_openDevice = false;
    m_oTimer->start(15000);
    do
    {
        QCoreApplication::processEvents();
        if(m_bPrepare_openDevice)
        {
              //qDebug() << "prepare() 设置规定的时间到了，返回" ;
              m_bPrepare_openDevice = false;
              //qDebug()<< "DataCDROM  prepare()定时到了，提前end";
              return;
        }
    } while (!execSCSI(cdb, 6, result, 8));
}



/*
 * 该函数用于prepare()中，当m_oTimer时间耗尽后，调用release() delete类DataCDROM，只执行一次
 * 以防打不开光驱设备，陷入死循环
 */
void DataCDROM::slotsPrepareTimerOut()
{
    //qDebug() << "15s定时到了";
    m_bPrepare_openDevice = true;
    m_oTimer->stop();
    //disconnect(m_oTimer, SIGNAL(timeout()), this, SLOT(slotsPrepareTimerOut()));

}

void DataCDROM::slotsUpdateCDROMBurnStart(bool b)
{
    //qDebug() << __func__ << __LINE__ << "m_bStart = " << b;
    m_bStart = b;
}

QString DataCDROM::display(QString blockName)
{
    Q_UNUSED(blockName);

    unsigned char  cdb[6] = {0x12, // inquiry
                             0x00,
                             0x00,
                             0x00,
                             0x24, // result length : 36
                             0
                            };
    char vendor[8] = {0};
    char productor[16] = {0};
    unsigned char  result[36] = {0};
    unsigned char *p = result;

    use();

    m_oVendor.clear(); m_oProductor.clear();
    m_oDisplay = _("Unkown.");
    if (!execSCSI(cdb, 6, result, 36))
    {
        release();
        return m_oDisplay;
    }
    ::memcpy(vendor, p+8, 7);
    m_oVendor.append(QString::fromUtf8(vendor));
    debug(_("Vendor is %1").arg(m_oVendor));
    ::memcpy(productor, p + 16, 15);
    m_oProductor.append(QString::fromUtf8(productor));
    debug(_("Productor is %1").arg(m_oProductor));
    m_oDisplay.clear();
    m_oDisplay.append(m_oVendor);
    m_oDisplay.append(" ");
    m_oDisplay.append(m_oProductor);
    release();
    return m_oDisplay;
}

bool DataCDROM::doorIsOpen()
{
    unsigned char cdb[10] = { 0x4A,
                              0x01,
                              0x00,
                              0x00,
                              0x10, // bit 4 |7 6 5 4 3 2 1 0|
                              0x00,
                              0x00,
                              0x00,
                              0x08,
                              0 };
    unsigned char result[8] = { 0 };
    if (!execSCSI(cdb, 10, result, 8)) return false;
    if (result[5] & 0x01) return true;
    return false;
}

void DataCDROM::setVolumnID(QString str)
{
    emit m_oSetting->volumnID(str);
}

void DataCDROM::support()
{
    unsigned short len = 0;
    unsigned char  cdb[12] = { 0x46, // get configuraion
                               0x00,
                               0x00,
                               0x00, // fearture low
                               0x00, // reserved
                               0x00, // reserved
                               0x00, // reserved
                               0x00, // allocation length high
                               0x0C,
                               0 };
    unsigned char  result[65536] = {0};

    // get header length first.
    if (!execSCSI(cdb, 12, result, 12)) return;
    if (!(result[8] == cdb[2] && result[9] == cdb[3])) return;

    len = (result[0] << 24) | (result[1] << 16) | (result[2] << 8) | result[3];
    cdb[7] = ((len & 0xFF00) >> 8);
    cdb[8] = (len & 0x00FF);
    // get real result.
    if (!execSCSI(cdb, 12, result, len)) return;
    len = result[11];

    use();
    m_u32MediumRSupport = 0;
    m_u32MediumWSupport = 0;
    m_u32BurnModeSupport = 0;

    // loop result, adjust support read medium type.
    for (int i = 0; i < len; ++i)
    {
        QCoreApplication::processEvents();
        switch ((result[i + 12] << 8) | result[i + 13])
        {
        case 0x0008: // CD-ROM
            m_u32MediumRSupport |= DataMedium::MEDIUM_CD_ROM;
            info(_("%1 support read mode : CD-ROM").arg(m_oDisplay));
            break;
        case 0x0009: // CD-R
            m_u32MediumRSupport |= DataMedium::MEDIUM_CD_R;
            info(_("%1 support read mode : CD-R").arg(m_oDisplay));
            break;
        case 0x000A: // CD-RW
            m_u32MediumRSupport |= DataMedium::MEDIUM_CD_RW;
            info(_("%1 support read mode : CD-RW").arg(m_oDisplay));
            break;
        // 000B - 000F reserved.
        case 0x0010: // DVD-ROM
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_ROM;
            info(_("%1 support read mode : DVD-ROM").arg(m_oDisplay));
            break;
        case 0x0011: // DVD-R
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_R;
            info(_("%1 support read mode : DVD-R").arg(m_oDisplay));
            break;
        case 0x0012: // DVD-RAM
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_RAM;
            info(_("%1 support read mode : DVD-RAM").arg(m_oDisplay));
            break;
        // define in MMC-5
        case 0x0013: // DVD-RW
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_RW_OVERWRITE;
            info(_("%1 support read mode : DVD-RW(OVERWRITE)").arg(m_oDisplay));
            break;
        case 0x0014:
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_RW_SEQ;
            info(_("%1 support read mode : DVD-RW(SEQ)").arg(m_oDisplay));
            break;
        case 0x0015:
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_R_DL_SEQ;
            info(_("%1 support read mode : DVD-R-DL(SEQ)").arg(m_oDisplay));
            break;
        case 0x0016:
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_R_DL_JUMP;
            info(_("%1 support read mode : DVD-R-DL(JUMP)").arg(m_oDisplay));
            break;
        case 0x001A: // DVD+RW
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_PLUS_RW;
            info(_("%1 support read mode : DVD+RW").arg(m_oDisplay));
            break;
        case 0x001B: //DVD+R
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_PLUS_R;
            info(_("%1 support read mode : DVD+R").arg(m_oDisplay));
            break;
        case 0x002B:
            m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_PLUS_R_DL;
            info(_("%1 support read mode : DVD+R-DL").arg(m_oDisplay));
            break;
        case 0x0002:
            debug(_("Removable CDROM."));
            break;
        default:
#if 0
            warn(_("Unkown medium type %1, to be supported in KylinBurner.")
                 .arg((result[i + 12] << 8) | result[i + 13]));
#endif
            break;
        }
    }

    // load write suppport
    // CD TAO
    cdb[2] = 0x00; cdb[3] = 0x2D; cdb[7] = 0x00; cdb[8] = 0x10;

    if (!execSCSI(cdb, 12, result, 16))
    {
        release();
        return;
    }
    if (result[8] == cdb[2] && result[9] == cdb[3])
    {
        m_u32BurnModeSupport |= BURN_CD_TAO;
        info(_("%1 supprot burn CD by mode TAO").arg(m_oDisplay));
        m_u32MediumWSupport |= DataMedium::MEDIUM_CD_R;
        info(_("%1 support write mode : CD-R").arg(m_oDisplay));
        if (result[12] & 0x02)
        {
            m_u32MediumWSupport |= DataMedium::MEDIUM_CD_RW;
            info(_("%1 support write mode : CD-RW").arg(m_oDisplay));
        }
    }
    //CD SAO
    cdb[2] = 0x00; cdb[3] = 0x2E; cdb[7] = 0x00; cdb[8] = 0x10;
    if (!execSCSI(cdb, 12, result, 16))
    {
        release();
        return;
    }
    if (result[8] == cdb[2] && result[9] == cdb[3])
    {
        m_u32MediumWSupport |= DataMedium::MEDIUM_CD_R;
        info(_("%1 support write mode : CD-R").arg(m_oDisplay));
        if (result[12] & 0x02)
        {
            m_u32MediumWSupport |= DataMedium::MEDIUM_CD_RW;
            info(_("%1 support write mode : CD-RW").arg(m_oDisplay));
        }
        if (result[12] & 0x20)
        {
            m_u32BurnModeSupport |= BURN_CD_SAO;
            info(_("%1 supprot burn CD by mode SAO").arg(m_oDisplay));
        }
    }
    //DVD
    cdb[2] = 0x00; cdb[3] = 0x1F; cdb[7] = 0x00; cdb[8] = 0x10;
    if (!execSCSI(cdb, 12, result, 16))
    {
        release();
        return;
    }
    if (result[8] == cdb[2] && result[9] == cdb[3])
    {
        m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_ROM;
        info(_("%1 sure support read mode : DVD-ROM").arg(m_oDisplay));
    }
    //DVD+RW
    cdb[2] = 0x00; cdb[3] = 0x2A; cdb[7] = 0x00; cdb[8] = 0x10;
    if (!execSCSI(cdb, 12, result, 16))
    {
        release();
        return;
    }
    if (result[8] == cdb[2] && result[9] == cdb[3])
    {
        if (result[12] & 0x01)
        {
            m_u32MediumWSupport |= DataMedium::MEDIUM_DVD_PLUS_RW;
            info(_("%1 support write mode : DVD+RW").arg(m_oDisplay));
        }
        else
        {
            m_u32MediumWSupport |= DataMedium::MEDIUM_DVD_ROM;
            info(_("%1 support write mode : DVD-ROM").arg(m_oDisplay));
        }
    }
    //DVD+R
    cdb[2] = 0x00; cdb[3] = 0x2B; cdb[7] = 0x00; cdb[8] = 0x10;
    if (!execSCSI(cdb, 12, result, 16))
    {
        release();
        return;
    }
    if (result[8] == cdb[2] && result[9] == cdb[3])
    {
        m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_PLUS_R;
        info(_("%1 sure support read mode : DVD+R").arg(m_oDisplay));
        if (result[12] & 0x01)
        {
            m_u32MediumWSupport |= DataMedium::MEDIUM_DVD_PLUS_R;
            info(_("%1 support write mode : DVD+RW").arg(m_oDisplay));
        }
    }
    //DVD-R / DVD-RW
    cdb[2] = 0x00; cdb[3] = 0x2F; cdb[7] = 0x00; cdb[8] = 0x10;
    if (!execSCSI(cdb, 12, result, 16))
    {
        release();
        return;
    }
    if (result[8] == cdb[2] && result[9] == cdb[3])
    {
        m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_R;
        info(_("%1 sure support read mode : DVD-R").arg(m_oDisplay));
        m_u32MediumWSupport |= DataMedium::MEDIUM_DVD_R;
        info(_("%1 support write mode : DVD-R").arg(m_oDisplay));
        m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_RW;
        info(_("%1 sure support read mode : DVD-RW").arg(m_oDisplay));
        if (result[12] & 0x02)
        {
            m_u32MediumWSupport |= DataMedium::MEDIUM_DVD_RW;
            info(_("%1 support write mode : DVD-RW").arg(m_oDisplay));
        }
    }
    if (m_u32MediumRSupport & ((DataMedium::MEDIUM_DVD_RW_OVERWRITE | DataMedium::MEDIUM_DVD_RW_SEQ)))
    {
        m_u32MediumRSupport |= DataMedium::MEDIUM_DVD_RW;
        info(_("%1 support read mode : DVD-RW from adjust.").arg(m_oDisplay));
    }
    release();
}

void DataCDROM::checkMedium()
{
    int gap = 0;
    int sec = 0;
    unsigned short profile = 0;
    unsigned int len = 0;
    unsigned char  cdb[12] = {0x46, // operation code
                              0x01, // RT : 0x10b
                              0x00, // fearture high
                              0x00, // fearture low
                              0x00, // reserved
                              0x00, // reserved
                              0x00, // reserved
                              0x00, // allocation length high
                              0x0C, // allocation length low
                              0};
    unsigned char result[65536] = {0};
    struct timeval start;
    struct timeval current;


    use();

    m_oMediumType.clear();

    if (doorIsOpen())
    {
        m_bEjected = true;
        m_oMediumType.clear();
        if (m_oMedium)
        {
            delete m_oMedium;
            m_oMedium = nullptr;
        }

        info(_("No medium.Because the cdrom's door is open."));
        eject(false);

        release();
        return;
    }
    m_bEjected = false;
    eject(true);

    if (checkCDROM()) sec = 15;
    else sec = 5;

    ::gettimeofday(&start, nullptr);
    while (m_bAppRunning && !loading() && gap < sec && nullptr == m_oMedium)
    {
        //QCoreApplication::processEvents();会引起计时功能问题、使变量值出错。
        if (!m_bAppRunning)
        {
            release();         
            return;
        }
        ::gettimeofday(&current, nullptr);
        gap = current.tv_sec - start.tv_sec;
        QThread::usleep(500);
    }
    if (gap >= sec && m_bAppRunning)
    {
        // debug(_("CDROM have no medium."));
        release();
        return;
    }

    if (!execSCSI(cdb, 12, result, 12))
    {
        release();
        return;
    }
    if (!(result[8] == cdb[2] && result[9] ==cdb[3]))
    {
        release();
        return;
    }
    profile = (result[6] << 8) | result[7];
    if (0x0000 == profile) // 未读取到定义类型
    {
        // 再次尝试读取全量的返回信息
        len = result[11];
        if (len > 65536) len = 65536;
        cdb[7] = ((len & 0xFF00) >> 8);
        cdb[8] = (len & 0x00FF);
        if (!execSCSI(cdb, 12, result, len))
        {
            release();
            return;
        }
        for (int i = 0; i < len; ++i)
        {
            //QCoreApplication::processEvents();
            if (result[14 + i] & 0x01)
            {
                profile = (result[12 + i] << 8) | result[13 + i];
                break;
            }
        }
    }

    switch (profile)
    {
    case 0x0008: // CD-ROM
        if (m_u32MediumRSupport & DataMedium::MEDIUM_CD_ROM)
        {
            m_oMediumType = "CD-ROM";
            info(_("Loaded CD-ROM medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read CD-ROM medium"));
        break;
    case 0x0009: //CD-R
        if (m_u32MediumRSupport & DataMedium::MEDIUM_CD_R)
        {
            m_oMediumType = "CD-R";
            info(_("Loaded CD-R medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read CD-R medium"));
        break;
    case 0x000A: // CD-RW
        if (m_u32MediumRSupport & DataMedium::MEDIUM_CD_RW)
        {
            m_oMediumType = "CD-RW";
            info(_("Loaded CD-RW medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read CD-RW medium"));
        break;
    case 0x0010: // DVD-ROM
        if (m_u32MediumRSupport & DataMedium::MEDIUM_DVD_ROM)
        {
            m_oMediumType = "DVD-ROM";
            info(_("Loaded DVD-ROM medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read DVD-ROM medium"));
        break;
    case 0x0011: // DVD-R
        if (m_u32MediumRSupport & DataMedium::MEDIUM_DVD_R)
        {
            m_oMediumType = "DVD-R";
            info(_("Loaded DVD-R medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read DVD-R medium"));
        break;
    case 0x0012: // DVD-RAM
        if (m_u32MediumRSupport & DataMedium::MEDIUM_DVD_RAM)
        {
            m_oMediumType = "DVD-RAM";
            info(_("Loaded DVD-RAM medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read DVD-RAM medium"));
        break;
    case 0x0013: // DVD-RW
        if (m_u32MediumRSupport & DataMedium::MEDIUM_DVD_RW_OVERWRITE)
        {
            m_oMediumType = "DVD-RW";
            info(_("Loaded DVD-RW(OVERWRITE) medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read DVD-RW(OVERWRITE) medium"));
        break;
    case 0x0014: // DVD-RW
        if (m_u32MediumRSupport & DataMedium::MEDIUM_DVD_RW_SEQ)
        {
            m_oMediumType = "DVD-RW";
            info(_("Loaded DVD-RW medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read DVD-RW medium"));
        break;
    case 0x001A: // DVD+RW
        if (m_u32MediumRSupport & DataMedium::MEDIUM_DVD_PLUS_RW)
        {
            m_oMediumType = "DVD+RW";
            info(_("Loaded DVD+RW medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read DVD+RW medium"));
        break;
    case 0x001B: // DVD+R
        if (m_u32MediumRSupport & DataMedium::MEDIUM_DVD_PLUS_R)
        {
            m_oMediumType = "DVD+R";
            info(_("Loaded DVD+R medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read DVD+R medium"));
        break;
    case 0x002B: // DVD+R DL
        if (m_u32MediumRSupport & DataMedium::MEDIUM_DVD_PLUS_R_DL)
        {
            m_oMediumType = "DVD+R DL";
            info(_("Loaded DVD+R DL medium in %1").arg(m_oDisplay));
            break;
        }
        error(_("CDROM %1 cannot support to read DVD+R DL medium"));
        break;
    default:
        //warn(_("To be supported in KylinBurner %1").arg(profile));
        break;
    }
    if (!m_oMediumType.isEmpty())
    {
        if (m_oMedium)
        {
            delete m_oMedium;
            m_oMedium = nullptr;
        }

        //将new DataMedium 分为2步，先new后调用load()，防止出现类A中调用类A的成员变量造成的错误
        m_oMedium = new DataMedium(m_oMediumType, m_oBlockName, this);
        m_oMedium->load();

        debug(_("Created medium of type %1.").arg(m_oMediumType));
        release();
        return;
    }
    debug(_("There is no mediun in CDROM"));
    release();
}

bool DataCDROM::loading()
{
    unsigned char cdb[10] = { 0x4A,
                              0x01,
                              0x00,
                              0x00,
                              0x10,
                              0x00,
                              0x00,
                              0x00,
                              0x08,
                              0 };
    unsigned char result[8] = { 0 };
    //QThread::msleep(500);//使DVD-RW能加载上数据
    if (!execSCSI(cdb, 10, result, 8)) return false;
    if (result[5] & 0x0F) return true;
    return false;
}

bool DataCDROM::checkCDROM()
{
    int ret;
    unsigned char buf[4096] = {0};

    ret = ::read(m_iHandle, buf, 4096);
    if (ret <= 0) return false;

    return true; // have mediun.
}

void DataCDROM::changeISOName(QString name)
{
    QFileInfo f;
    QStringList after;
    if (!m_bCDROM)
    {
        f.setFile(m_oBlockName);
        if (f.isDir())
        {
            return;
        }
        after = m_oBlockName.split(f.baseName());
        m_oBlockName = after.first();
        m_oBlockName.append(name).append(after.last());
    }
}

void DataCDROM::changeISOPath(QString path)
{
    QFileInfo f;
    if (!m_bCDROM)
    {
        f.setFile(m_oBlockName);
        m_oBlockName = path + "/" + f.fileName();
        volumnID(m_oSetting->getVolumnID());
    }
}


DataCDROM::~DataCDROM()
{
    //qDebug() << "";
    //qDebug()<<"~ DataCDROM"<< this << "  DataROM地址为::" << this << " (" <<  this->device() <<") -------start----------";
    //info(QString("析构 DataCDROM, m_iUsing = [%2]").arg(m_iUsing));

    if (-1 != m_iHandle) ::close(m_iHandle);
    m_iHandle = -1;

    if(m_oMedium)
    {
        //qDebug() << "DataCDROM:~DataCDROM()  ： delete m_oMedium = " << m_oMedium;
        delete m_oMedium;
        m_oMedium = nullptr;
        //info(QString("[%1] [%2] : 已delete m_oMedium").arg(__func__).arg(__LINE__));
    }
    //delete m_oSetting;
    //m_oSetting = nullptr;
    //delete m_oTimer;
    if(m_oTimer) {
        delete m_oTimer;
        //info(QString("[%1] [%2] : 已delete m_oTimer").arg(__func__).arg(__LINE__));
    }
    m_oTimer = nullptr;
}

bool DataCDROM::isOpen()
{
    return m_iHandle >= 0;
}
