#include "logicburndata.h"
#include "logicmain.h"
#include "src/frame/configuration.h"
#include "src/frame/logger.h"
#include "src/po/translation.h"
#include "src/data/datacdrom.h"
#include "src/data/datadirectory.h"
#include "src/data/datafile.h"
#include "src/view/modaldialog.h"
#include "src/view/execdialog.h"
#include "src/view/filefilterrules.h"
#include "src/process/processcreateiso.h"
#include "src/process/processburndata.h"
#include "src/process/processburnudfdata.h"

#include <QDebug>
#include <QFont>
#include <QDir>
#include <QUrl>
#include <QCoreApplication>
#include <QStorageInfo>
#include <QMimeData>
#include <ukuistylehelper.h>

#define APPLICATIONTYPE QString("application/x-kylinburnerdata")

LogicBurnData::LogicBurnData(QObject *parent)
    : QAbstractItemModel(parent),
      m_iIndex(-1),
      m_bCancel(false),
      m_bAppRunning(true)
{
    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, _("BURNDATA"));

    m_oHHeaders.clear();
    m_oVHeaders.clear();
    m_oRoots.clear();
    connect(this, SIGNAL(setting(void*)), BUSNESSSETTING, SLOT(load(void *)));
    connect(this, SIGNAL(title(QString)), PROGRESS, SIGNAL(title(QString)));
    connect(this, SIGNAL(setRange(int,int)), PROGRESS, SIGNAL(setRange(int, int)));
    connect(this, SIGNAL(setValue(int,QIcon,QString)), PROGRESS, SIGNAL(setValue(int,QIcon,QString)));
    connect(this, SIGNAL(addValue(int,QIcon,QString)), PROGRESS, SIGNAL(addValue(int,QIcon,QString)));

    connect(MAIN, &LogicMain::exit, [=](){m_bAppRunning = false;});
}

QVariant LogicBurnData::headerData(int section, Qt::Orientation orientation, int role) const
{
    QFont f;
    if (orientation == Qt::Horizontal)
    {
        switch (role)
        {
        case Qt::DisplayRole:
            if (m_oHHeaders.size() && section < m_oHHeaders.size())
                return m_oHHeaders.at(section);
            else
                return QString();
            break;
        case Qt::FontRole:
            f.setPixelSize(14);
            f.setWeight(60);
            return f;
#if 0
        case Qt::TextAlignmentRole:
            if (0 == section)
                return (Qt::AlignLeft);
            else
                return (Qt::AlignCenter);
            break;
#endif
        default:
            break;
        }
    }
    else
    {
        switch (role)
        {
        case Qt::DisplayRole:
            if (m_oVHeaders.size() && section < m_oHHeaders.size())
                return m_oVHeaders.at(section);
            break;
        default:
            break;
        }
    }

    return QVariant();
}

bool LogicBurnData::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
{
    if (value != headerData(section, orientation, role)) {
        switch (role)
        {
        case Qt::DisplayRole:
            if (orientation == Qt::Horizontal)
            {
                if (section > m_oHHeaders.size())
                    return false;
                else if (section == m_oHHeaders.size())
                    m_oHHeaders << value.toString();
                else
                    m_oHHeaders.replace(section, value.toString());
            }
            else
            {
                if (section > m_oVHeaders.size())
                    return false;
                else if (section == m_oVHeaders.size())
                    m_oVHeaders << value.toString();
                else
                    m_oVHeaders.replace(section, value.toString());
            }
            break;
        }
        emit headerDataChanged(orientation, section, section);
        return true;
    }
    return false;
}

QModelIndex LogicBurnData::index(int row, int column, const QModelIndex &parent) const
{
    DataDirectory *dir;

    if (!parent.isValid() && row < m_oRoots.size() && row > -1)
        return createIndex(row, column, m_oRoots.at(row));

    dir = static_cast<DataDirectory *>(parent.internalPointer());
    if (nullptr == dir)
        return QModelIndex();
    return createIndex(row, column, dir->at(row));
    return QModelIndex();
}

QModelIndex LogicBurnData::parent(const QModelIndex &index) const
{
    DataDirectory *p;
    DataDirectory *pp;
    DataFile *item;

    if (!index.isValid()) return QModelIndex();
    item = static_cast<DataFile *>(index.internalPointer());
    if (nullptr == item) return QModelIndex();
    p = static_cast<DataDirectory *>(item->parent());
    if (nullptr == p) return QModelIndex();
    if (p->isRoot()) return createIndex(m_oRoots.indexOf(p), 0, p);
    pp = static_cast<DataDirectory *>(p->parent());
    if (!pp->isDirectory()) return QModelIndex();
    return createIndex(pp->of(p), 0, p);
}

int LogicBurnData::rowCount(const QModelIndex &parent) const
{
    DataDirectory *dir;
    if (!parent.isValid())
        return m_oRoots.size();
    if (m_oVHeaders.size())
        return m_oVHeaders.size();
    // return children counts.
    dir = static_cast<DataDirectory *>(parent.internalPointer());
    if (nullptr == dir || !dir->isDirectory()) return 0;
    return dir->counts();
}

int LogicBurnData::columnCount(const QModelIndex &parent) const
{
    if (!parent.isValid())
        return m_oHHeaders.size();
    return m_oHHeaders.size();
}

QVariant LogicBurnData::data(const QModelIndex &index, int role) const
{
    DataFile *item;
    QFont f;

    if (!index.isValid())
        return QVariant();

    item = static_cast<DataFile *>(index.internalPointer());
    if (nullptr == item) return QVariant();

    switch (role)
    {
    case Qt::DisplayRole:
        switch (index.column())
        {
        case 0:
            return item->display();
        case 1:
            if (".." == item->display())
                return _("Parent");
            return item->type();
        case 2:
            if (".." == item->display())
                return QString("-");
            return item->stringSize();
        case 3:
            return item->source();
        }
        break;
    case Qt::EditRole:
        if (0 == index.column())
            return item->display();
    case Qt::DecorationRole:
        if (0 == index.column())
        {
            if (item->isDisk())
            {
                return QIcon(item->icon().pixmap(QSize(24, 24), QIcon::Disabled));
            }
            else
            {
                return item->icon();
            }
        }
        break;
    case Qt::FontRole:
        if (item->attrSymbolLink() && 0 == index.column())
        {
            f.setItalic(true);
            return f;
        }
        break;
    case Qt::ForegroundRole:
        if (item->isDisk())
            return QBrush(QColor(179, 179, 179));
        break;
    case Qt::ToolTipRole:
        switch (index.column())
        {
        case 0:
            return item->display();
        case 1:
            return item->type();
        case 2:
            return item->stringSize();
        case 3:
            return item->source();
        }
        break;
    }

    return QVariant();
}

bool LogicBurnData::setData(const QModelIndex &index, const QVariant &value, int role)
{
    DataFile *item;
    QString newName;

    if (data(index, role) != value) {
        if (Qt::EditRole == role)
        {
            item = static_cast<DataFile *>(index.internalPointer());
            if (nullptr == item) return false;
            newName = value.toString();
            if (newName.trimmed().isEmpty())
            {
                EXEC->error(_("Rename") + " - " + _("Empty Name"),
                            _("Cannot rename to empty."));
                return false;
            }
            if (newName.length() > 103)
            {
                EXEC->error(_("Rename") + " - " + _("Name Too Long"),
                            _("New name length must be less than 103 unicode charset."));
                return false;
            }
            if (duplicatedName(newName, index.parent()))
            {
                EXEC->error(_("Rename") + " - " + _("Duplicate Name"),
                            _("There is a item named by %1.").arg(newName));
                return false;
            }
            item->rename(value.toString());
        }
        emit dataChanged(index, index, QVector<int>() << role);
        return true;
    }
    return false;
}

Qt::ItemFlags LogicBurnData::flags(const QModelIndex &index) const
{
    DataCDROM *cdrom;
    DataFile *file;

    if (!index.isValid()) return QAbstractItemModel::flags(index);
    file = static_cast<DataFile *>(index.internalPointer());
    if (nullptr == file || file->isDisk()) return QAbstractItemModel::flags(index);
    cdrom = static_cast<DataCDROM *>(m_oBurners.at(m_iIndex));
    if (cdrom->mediumStatus() == DataMedium::MEDIUM_FINALIZED) return QAbstractItemModel::flags(index);
    if (".." == index.data().toString()) return QAbstractItemModel::flags(index);
    if (0 == index.column())
        return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
    return QAbstractItemModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}

Qt::DropActions LogicBurnData::supportedDropActions() const
{
    return Qt::CopyAction | Qt::MoveAction;
}

QStringList LogicBurnData::mimeTypes() const
{
    QStringList ret;

    ret.clear();
    ret = QAbstractItemModel::mimeTypes();
    ret.append(APPLICATIONTYPE);

    return ret;
}

QMimeData *LogicBurnData::mimeData(const QModelIndexList &indexes) const
{
    QMimeData  *ret = new QMimeData();
    unsigned long long ptr;
    QByteArray  data;
    QDataStream stream(&data, QIODevice::WriteOnly);

    foreach (QModelIndex idx, indexes)
    {
        if (0 == idx.column())
        {
            ptr = (unsigned long long)(idx.internalPointer());
            stream << ptr;
        }
    }

    ret->setData(APPLICATIONTYPE, data);

    return ret;
}

bool LogicBurnData::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    DataDirectory *newParent;
    DataDirectory *oldParent;
    DataFile *file;
    unsigned long long ptr;
    QStringList willAdd;
    QList<QUrl> urls;
    QByteArray bytes;
    QModelIndex oldParentIndex;
    QModelIndex newParentIndex;

    willAdd.clear();
    urls.clear();

    file = static_cast<DataFile *>(parent.internalPointer());
    if (!file->isDirectory()) return false;

    if (data->hasUrls())
    {
        urls = data->urls();
        while (urls.size())
            willAdd << urls.takeFirst().path();
        addDatasToIndex(willAdd, parent);
        rootIndex(parent);
        return true;
    }

    if (data->hasFormat(APPLICATIONTYPE))
    {
        bytes = data->data(APPLICATIONTYPE);
        QDataStream stream(bytes);
        file = static_cast<DataFile *>(parent.internalPointer());
        if (nullptr == file || !file->isDirectory()) return false;
        newParent = static_cast<DataDirectory *>(file);
        file = nullptr;
        newParentIndex = parent;
        if (".." == newParent->display())
        {
            newParent = qobject_cast<DataDirectory *>(newParent->parent()->parent());
            newParentIndex = parent.parent().parent();
        }
        if (nullptr == newParent) return false;
        while (!stream.atEnd())
        {
            ptr = 0;
            stream >> ptr;
            file = (DataFile *)(ptr);
            oldParent = qobject_cast<DataDirectory *>(file->parent());
            if (oldParent == newParent) continue;
            if (-1 != m_oRoots.indexOf(oldParent))
                oldParentIndex = createIndex(m_oRoots.indexOf(oldParent), 0, oldParent);
            else
                oldParentIndex = createIndex(qobject_cast<DataDirectory *>(oldParent->parent())->of(oldParent), 0, oldParent);
            beginInsertRows(newParentIndex, newParent->counts(), 1);
            beginRemoveRows(oldParentIndex, 0, 1);
            file->changeParent(newParent);
            endRemoveRows();
            endInsertRows();
            rootIndex(newParentIndex);
        }
        return true;
    }

    return false;
}

bool LogicBurnData::isUp(QModelIndex idx)
{
    if (".." == idx.siblingAtColumn(0).data().toString()) return true;
    return false;
}

bool LogicBurnData::isFolder(QModelIndex idx)
{
    DataDirectory *dir;

    dir = static_cast<DataDirectory *>(idx.internalPointer());
    if (dir && dir->isDirectory() /* && dir->count() > 0 */) return true;
    return false;
}

bool LogicBurnData::checkAdd(QModelIndex idx)
{
    DataCDROM *cdrom;
    DataDirectory *dir;
    dir = static_cast<DataDirectory *>(idx.internalPointer());
    cdrom = nullptr;
    if (-1 != m_iIndex && m_iIndex < m_oBurners.size())
        cdrom = static_cast<DataCDROM *>(m_oBurners.at(m_iIndex));

    do
    {

      if (cdrom && cdrom->mediumStatus() == DataMedium::MEDIUM_FINALIZED) break;
      if (nullptr == dir || !dir->isDirectory()) break;
      if (dir->isDisk()) break;
      add(true); folder(true); return true;
    } while (0);
    add(false); folder(false); return false;
}

bool LogicBurnData::checkAdd(QModelIndexList idxes)
{
    if (idxes.size() > 1) return false;
    return checkAdd(idxes.first());
}

bool LogicBurnData::checkDel(QModelIndexList idxes)
{
    DataDirectory *dir;
    DataFile *file;

    if (0 == idxes.size()) { del(false); return false; }

    while (idxes.size())
    {
        dir = static_cast<DataDirectory *>(idxes.takeFirst().internalPointer());
        file = static_cast<DataFile *>(dir);
        if (!file) { del(false); return false; }
        if (dir && dir->isDirectory() && (dir->isDisk() || dir->isRoot())) { del(false); return false; }
        if (file && file->isDisk()) { del(false); return false; }
        if (file && ".." == file->display()) { del(false); return false; }
    }
    del(true); return true;
}

bool LogicBurnData::checkCls(QModelIndex idx)
{
    bool ret;
    DataDirectory *dir;
    dir = static_cast<DataDirectory *>(idx.internalPointer());
    if (nullptr == dir || !dir->isDirectory()) { cls(false); return false; }
    if (dir->isDisk()) { cls(false); return false; }
    ret = dir->canClean();
    cls(ret); return ret;
}

bool LogicBurnData::checkTip(QModelIndex idx)
{
    DataDirectory *dir;
    dir = static_cast<DataDirectory *>(idx.internalPointer());
    if (nullptr == dir) { tip(true); return true; }
    if (dir->isRoot() && dir->count() == 0) { tip(true); return true; }
    tip(false); return false;
}

bool LogicBurnData::checkOpen(QModelIndexList idxes)
{
    DataFile *file;
    DataDirectory *dir;

    if (idxes.size() != 1) return false;
    file = static_cast<DataFile *>(idxes.first().internalPointer());
    if (nullptr == file) return false;
    if (!file->isDirectory()) return false;
    dir = static_cast<DataDirectory *>(file);
    //if (dir->count() < 1) return false;
    return true;
}

bool LogicBurnData::checkOpenLocal(QModelIndexList idxes)
{
    DataFile *file;
    if (idxes.size() != 1) return false;
    file = static_cast<DataFile *>(idxes.first().internalPointer());
    if (nullptr == file) return false;
    if (_("NewFolder") == file->type()) return false;
    return true;
}

bool LogicBurnData::checkProperties(QModelIndexList idxes)
{
    DataFile *file;
    if (idxes.size() != 1) return false;
    file = static_cast<DataFile *>(idxes.first().internalPointer());
    if (nullptr == file) return false;
    if (_("NewFolder") == file->type()) return false;
    if (".." == file->display()) return false;
    return true;
}

bool LogicBurnData::checkSelectModelItem(QModelIndexList idxes)
{
    if (0 == idxes.size()) return false;
    else
        return true;
}

bool LogicBurnData::checkSize()
{
    DataDirectory *dir;
    DataCDROM *cdrom;
    QStorageInfo storage;

    if (0 == m_oRoots.size()) return false;
    if (m_iIndex < 0 || m_iIndex >= m_oRoots.size()) return false;
    dir = static_cast<DataDirectory *>(m_oRoots.at(m_iIndex));
#if 1
    //既要添加的0B的文件能刻录，也要避免没有添加文件btnBusnes按钮置灰
    //使用文件数量代替文件大小来做判断条件
    if(dir->CountOneLevelNotDiskFile()<=0)  return false;
#else
    //使容量进行判断是否添加了新文件
    if (0 == dir->nSize()) return false;
#endif
    if (m_iIndex >= m_oBurners.size()) return false;
    cdrom = static_cast<DataCDROM *>(m_oBurners.at(m_iIndex));
    storage.setPath(cdrom->settingTempPath());
    if (dir->size() > storage.bytesAvailable()) return false;


    //bug120965,155809,解决由于要刻录的文件容量大于光盘剩余容量造成的刻录错误，
    if (cdrom->isCDROM() )
    {
        qDebug()<<__LINE__<<__func__<<dir->nSize();
        qDebug()<<__LINE__<<__func__<<cdrom->mediumFreeSize();
        if (dir->nSize() > cdrom->mediumFreeSize())
        return false;
    }
    return true;
}

QString LogicBurnData::stringSize(unsigned long size)
{
    int unit;
    qreal rsize;
    QString ret;

    unit = -1;
    rsize = size * 1.0;

    while (rsize > 1024.00)
    {
        ++unit; rsize /= 1024.00;
    }

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

    switch (unit)
    {
    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;
}

unsigned int LogicBurnData::level(QModelIndex idx)
{
    DataFile *data;

    data = static_cast<DataFile *>(idx.internalPointer());
    if (nullptr == data) return 0;
    return data->level();
}

int LogicBurnData::filter(QModelIndex idx)
{
    int ret;
    DataDirectory *node;

    ret = 0;
    node = nullptr;
    if (!idx.isValid()){
        node = (DataDirectory *)(m_oRoots.at(m_iIndex));
        qDebug()<<"LogicBurnData "<<__func__<<__LINE__<<node;
    }
    else{
        node = static_cast<DataDirectory *>(idx.internalPointer());
        qDebug()<<"LogicBurnData "<<__func__<<__LINE__<<node;
    }
    if (!node) return ret;
    FILTERRULES->init(node);
    return ret;
}

int LogicBurnData::doFilterHidden(QModelIndex idx)
{
    int ret;
    DataDirectory *node;

    ret = 0;
    node = nullptr;
    if (!idx.isValid()){
        qDebug()<<__LINE__<<__func__<<"1";
        node = (DataDirectory *)(m_oRoots.at(m_iIndex));
    }
    else{
        qDebug()<<__LINE__<<__func__<<"2";
        node = static_cast<DataDirectory *>(idx.internalPointer());
    }
    if (!node){
        qDebug()<<__LINE__<<__func__<<"3";
        return ret;
    }
    node->execHidden();

    if (idx.isValid()){
        qDebug()<<__LINE__<<__func__<<"4";
        rootIndex(idx);
    }
    else {
        qDebug()<<__LINE__<<__func__<<"5";
        rootIndex(createIndex(m_iIndex, 0, node));
    }
    return ret;
}

int LogicBurnData::doRecoveryHidden(QList<void *> data, QModelIndex idx)
{
    DataFile *file;
    DataDirectory *parent;
    DataDirectory *root;

    if (data.size() == 0)
    {
        if (!idx.isValid()){
            parent = (DataDirectory *)(m_oRoots.at(m_iIndex));
        }
        else{
            parent = static_cast<DataDirectory *>(idx.internalPointer());
        }
        parent->execHidden(false);
        qDebug()<<"LogicBurnData"<<__func__<<__LINE__;
    }

    while (data.size())
    {
        QCoreApplication::processEvents();
        file = static_cast<DataFile *>(data.takeFirst());
        parent = qobject_cast<DataDirectory *>(file->parent());
        root = qobject_cast<DataDirectory *>(parent->parent());
        if (root){
            beginInsertRows(createIndex(root->of(parent), 0, parent), parent->counts(), 1);
        }
        else{
            beginInsertRows(createIndex(m_iIndex, 0, m_oRoots.at(m_iIndex)), parent->counts(), 1);
        }
        parent->execHidden(false);
        parent->append(file);
        file->size(file->size(), true);
        connect(parent, SIGNAL(hidden(bool)), file, SLOT(slotsDoHidde(bool)));
        endInsertRows();
    }
    return 0;
}

int LogicBurnData::doFilterBroken(QModelIndex idx)
{
    int ret;
    DataDirectory *node;

    ret = 0;
    node = nullptr;
    if (!idx.isValid())
        node = (DataDirectory *)(m_oRoots.at(m_iIndex));
    else
        node = static_cast<DataDirectory *>(idx.internalPointer());
    if (!node) return ret;
    node->execBroken();
    if (idx.isValid()) rootIndex(idx);
    else rootIndex(createIndex(m_iIndex, 0, node));
    return ret;
}

int LogicBurnData::doRecoveryBroken(QList<void *> data, QModelIndex idx)
{
    DataFile *file;
    DataDirectory *parent;
    DataDirectory *root;

    if (data.size() == 0)
    {
        if (!idx.isValid())
            parent = (DataDirectory *)(m_oRoots.at(m_iIndex));
        else
            parent = static_cast<DataDirectory *>(idx.internalPointer());
        parent->execBroken(false);
    }

    while (data.size())
    {
        QCoreApplication::processEvents();
        file = static_cast<DataFile *>(data.takeFirst());
        parent = qobject_cast<DataDirectory *>(file->parent());
        root = qobject_cast<DataDirectory *>(parent->parent());
        if (root)
            beginInsertRows(createIndex(root->of(parent), 0, parent), parent->counts(), 1);
        else
            beginInsertRows(createIndex(m_iIndex, 0, m_oRoots.at(m_iIndex)), parent->counts(), 1);
        parent->execBroken(false);
        parent->append(file);
        file->size(file->size(), true);
        connect(parent, SIGNAL(broken(bool)), file, SLOT(slotsDoBroken(bool)));
        endInsertRows();
    }
    return 0;
}

int LogicBurnData::doRecoveryRepleace(QList<void *> data)
{
    DataFile *file;
    DataDirectory *parent;
    DataDirectory *root;

    if (0 == data.size())
    {
        parent = static_cast<DataDirectory *>(m_oRoots.at(m_iIndex));
        if (parent)
            parent->execRepleace(false);
        return 0;
    }

    while (data.size())
    {
        QCoreApplication::processEvents();
        file = static_cast<DataFile *>(data.takeFirst());
        parent = qobject_cast<DataDirectory *>(file->parent());
        root = qobject_cast<DataDirectory *>(parent->parent());
#if 1
        if (root)
            beginInsertRows(createIndex(root->of(parent), 0, parent), parent->counts(), 1);
        else
            beginInsertRows(createIndex(m_iIndex, 0, m_oRoots.at(m_iIndex)), parent->counts(), 1);
#endif
        parent->execRepleace(false);

#if 0
        if (parent->take(file->linkTarget()))
            parent->append(file);
        else
            delete file;
        connect(parent, SIGNAL(repleace(bool)), file, SLOT(slotsDoRepleace(bool)));
        endInsertRows();
#else
        //解决122706的闪退问题，功能还有问题。
        if (parent->take(file->linkTarget()))
            parent->append(file);
        else
        {
            file->disconnect();
            delete file;
            file = nullptr;
        }
        if(file)
            connect(parent, SIGNAL(repleace(bool)), file, SLOT(slotsDoRepleace(bool)));
        endInsertRows();
#endif
    }
    return 0;
}

int LogicBurnData::doFilterRepleace(QModelIndex idx)
{
    int ret;
    DataDirectory *node;
    DataDirectory *link;

    ret = 0;
    node = nullptr;
    link = nullptr;
    if (!idx.isValid())
        node = (DataDirectory *)(m_oRoots.at(m_iIndex));
    else
        node = static_cast<DataDirectory *>(idx.internalPointer());
    if (!node) return ret;
    node->execRepleace();
    if (node->linkTarget()) link = static_cast<DataDirectory *>(node->linkTarget());
    if (link) link->execRepleace();
    if (idx.isValid()) rootIndex(idx);
    else rootIndex(createIndex(m_iIndex, 0, node));
    return ret;
}

void LogicBurnData::changeIndex(int idx)
{
    DataDirectory *root;

    if (idx < 0 || idx >= m_oRoots.size()) return;
    root = static_cast<DataDirectory *>(m_oRoots.at(idx));
    if (root)
    {
        m_iIndex = idx;
        rootIndex(createIndex(idx, 0, root));
        size(root->size());
    }
}

int LogicBurnData::addDatasToIndex(QStringList files, QModelIndex idx)
{
    int ret;
    DataDirectory *parent;
    DataDirectory *root;
    DataCDROM     *cdrom;
    QString str;
    QFileInfo f;
    QList<DataDirectory *>parents;

    parent = static_cast<DataDirectory *>(idx.internalPointer());
    if (nullptr == parent || !parent->isDirectory()) return 0;
    ret = 0;
    parents.clear();
    parents << parent;
    str = parent->display();
    if (CFG->get("DataSetting", "Add Data", true).toBool()) // for all roots
    {
        for (int i = 0; i < m_oRoots.size(); ++i)
        {
            if (i == m_iIndex) continue; // current idx has saved.
            root = (DataDirectory *)(m_oRoots.at(i));
            if (parent->isRoot())
                parent = (DataDirectory *)(m_oRoots.at(i));
            else
                parent = (DataDirectory *)(root->findChildByDisplay(str));
            cdrom = static_cast<DataCDROM *>(m_oBurners.at(i));
            if (cdrom && cdrom->mediumStatus() == DataMedium::MEDIUM_FINALIZED) continue;
            if (parent) parents << parent;
        }
    }
    m_bCancel = false;
    PROGRESS;
    MODAL->show();
    title(_("Add File(s)"));
    setRange(0, parents.size() * needAdd(files));
    setValue(0);
    connect(MODAL, &ModalDialog::rejected, [=](){
        debug(_("User canceled when add file(s)"));
        m_bCancel = true;
    });
    foreach (parent, parents)
    {
        root = qobject_cast<DataDirectory *>(parent->parent());
        if (nullptr == root)
            idx = createIndex(m_oRoots.indexOf(parent), 0, parent);
        else
            idx = createIndex(root->of(parent), 0, parent);
        beginInsertRows(idx, parent->counts(), files.size());
        foreach (str, files)
        {
            QCoreApplication::processEvents();
            f.setFile(str);
            if (!f.exists())
            {
                addValue(1);
                continue;
            }
            if (f.isDir())
                ret += addDirectoy(f.absoluteFilePath(), parent);
            else
                ret += addFile(f.absoluteFilePath(), parent);
            if (m_bCancel) break;
        }
        qDebug()<<__func__<<__LINE__<<"LogicBurnData"<<str;
        endInsertRows();
    }

    disconnect(MODAL);
    return ret;
}

int LogicBurnData::deleteDatasFromIndex(QModelIndexList idxes, QModelIndex p)
{
    int ret;
    int size;
    DataDirectory *parent;
    DataDirectory *root;
    DataFile *child;
    QModelIndex idx;
    QString str;
    QStringList childNames;
    QList<DataDirectory *>parents;

    if (0 == idxes.size()) return 0;
    parent = static_cast<DataDirectory *>(p.internalPointer());
    if (nullptr == parent || !parent->isDirectory()) return 0;
    parents.clear();
    parents << parent;
    str = parent->display();
    child = static_cast<DataFile *>(idxes.at(0).internalPointer());
    if (nullptr == child) return 0;
    ret = 0;
    if (CFG->get("DataSetting", "Delete Data", false).toBool())
    {
        for (int i = 0; i < m_oRoots.size(); ++i)
        {
            if (i == m_iIndex) continue; // current idx has saved.
            root = (DataDirectory *)(m_oRoots.at(i));

            if (parent->isRoot()){
                parent = (DataDirectory *)(m_oRoots.at(i));

            }
            else{
                parent = (DataDirectory *)(root->findChildByDisplay(str));

            }
            if (parent){

                parents << parent;
            }
        }
    }
    childNames.clear();
    foreach (idx, idxes)
    {
        QCoreApplication::processEvents();
        child = static_cast<DataFile *>(idx.internalPointer());
        if (".." == child->display()) continue;
        childNames << child->display();
    }
    foreach (parent, parents)
    {
        QCoreApplication::processEvents();
        root = qobject_cast<DataDirectory *>(parent->parent());
        if (nullptr == root)
            p = createIndex(m_oRoots.indexOf(parent), 0, parent);
        else
            p = createIndex(root->of(parent), 0, parent);
        size = 0;
        child = nullptr;
        foreach (str, childNames)
        {
            if (parent->findChildByDisplay(str, true)) ++size;
            if (nullptr == child)
                child = (DataFile *)(parent->findChildByDisplay(str, true));
        }

        beginRemoveRows(p, parent->of(child), size);

        foreach (str, childNames)
        {
            QCoreApplication::processEvents();
            child = (DataFile *)(parent->findChildByDisplay(str, true));
            if (child && ".." != child->display() && !child->isDisk()) delete child;
        }
        endRemoveRows();
#if 0
        if (0 == parent->count() && !parent->isRoot())
        {
            child = parent->at(0);
            beginRemoveRows(p, 0, 1);
            delete child;
            endInsertRows();
        }
#endif
    }
    if (parents.first()->count() == 0)
    {
        root = qobject_cast<DataDirectory *>(parents.first()->parent());
        if (root)
        {
            if (-1 != m_oRoots.indexOf(root))
                rootIndex(createIndex(m_oRoots.indexOf(root), 0, root));
            else
            {
                parent = qobject_cast<DataDirectory *>(root->parent());
                if (parent) rootIndex(createIndex(parent->of(root), 0, root));
            }
        }
        else
            rootIndex(createIndex(m_oRoots.indexOf(parents.first()), 0, parents.first()));
    }
    return ret;
}

int LogicBurnData::clearDatasUnderIndexd(QModelIndex parent)
{
    int ret;
    DataDirectory *p;

    p = static_cast<DataDirectory *>(parent.internalPointer());
    if (nullptr == p || !p->isDirectory()) return 0;
    ret = 0;
    beginRemoveRows(parent, 0, p->count());
    p->clear();
    endRemoveRows();
    if (p->isRoot())
        rootIndex(createIndex(m_oRoots.indexOf(p), 0, p));
    else
        rootIndex(parent.parent().parent());
    return ret;
}

int LogicBurnData::clearDatasUnderIndex(QModelIndex p)
{
    int ret;
    DataDirectory *parent;
    DataDirectory *root;
    QString str;
    QList<DataDirectory *>parents;

    ret = 0;
    parent = static_cast<DataDirectory *>(p.internalPointer());
    if (nullptr == parent || !parent->isDirectory()) return 0;
    str = parent->display();
    parents.clear();
    parents << parent;
    if (CFG->get("DataSetting", "Clean Data", false).toBool())
    {
        for (int i = 0; i < m_oRoots.size(); ++i)
        {
            if (i == m_iIndex) continue; // current idx has saved.
            root = (DataDirectory *)(m_oRoots.at(i));
            if (parent->isRoot())
                parent = (DataDirectory *)(m_oRoots.at(i));
            else
                parent = (DataDirectory *)(root->findChildByDisplay(str));
            if (parent) parents << parent;
        }
    }

    foreach (parent, parents)
    {
        root = qobject_cast<DataDirectory *>(parent->parent());
        if (nullptr == root)
            p = createIndex(m_oRoots.indexOf(parent), 0, parent);
        else
            p = createIndex(root->of(parent), 0, parent);
        beginRemoveRows(p, 0, parent->count());
        ret += parent->count();
        parent->clear();
        endRemoveRows();
    }


    if (parents.first()->isRoot())
        rootIndex(createIndex(m_oRoots.indexOf(parents.first()), 0, parents.first()));
    else if (p.parent().isValid())
        rootIndex(p.parent());
    else
        rootIndex(createIndex(m_oRoots.indexOf(parents.first()), 0, parents.first()));

    return ret;
}

bool LogicBurnData::duplicatedName(QString name, QModelIndex p)
{
    DataDirectory *parent;

    parent = static_cast<DataDirectory *>(p.internalPointer());
    if (nullptr == parent) return true;
    return parent->duplicate(name);
}

void LogicBurnData::addEmptyFolder(QString name, QModelIndex parent)
{
    QString path;
    DataDirectory *p;
    DataDirectory *item;

    p = static_cast<DataDirectory *>(parent.internalPointer());
    if (nullptr == p || !p->isDirectory()) return;
    path = p->destination() + "/" + name;
    beginInsertRows(parent, p->count() - 1, 1);
    item = new DataDirectory(path, p);
    endInsertRows();
}

void LogicBurnData::deleteRoot(int idx)
{
    DataDirectory *root;

    if (idx >= m_oRoots.size() || idx < 0) return;
    root = static_cast<DataDirectory *>(m_oRoots.at(idx));
    m_oBurners.takeAt(idx);

    beginRemoveRows(createIndex(idx, 0, m_oRoots.at(idx)), 0, root->count());
    root->clean();
    endRemoveRows();
    disconnect(root, SIGNAL(updateSize(ulong)), this, SLOT(slotsUpdateSize(ulong)));
    delete root;
    m_oRoots.takeAt(idx);
}

void LogicBurnData::recovery(QList<void *> hiddens, QList<void *> brokens, QList<void *> replaces)
{
    DataDirectory *root;
    DataDirectory *parent;
    DataFile *child;
    QModelIndex idx;

    root = nullptr;

    while (hiddens.size())
    {
        child = static_cast<DataFile *>(hiddens.takeFirst());
        if (nullptr == child) continue;
        parent = static_cast<DataDirectory *>(child->parent());
        root = static_cast<DataDirectory *>(parent->parent());
        if (parent->isRoot()){
            idx = createIndex(m_oRoots.indexOf(parent), 0, parent);
        }
        else{
            idx = createIndex(root->of(parent), 0, parent);

        }
        beginInsertRows(idx, parent->counts(), 1);
        qDebug()<<"LogicBurnData"<<__func__<<__LINE__;
        parent->execHidden(false);
        parent->append(child);
        endInsertRows();
        connect(parent, SIGNAL(hidden(bool)), child, SLOT(slotsDoHidde(bool)));
        qDebug()<<__func__<<__LINE__<<"LogicBurnData recovery";
    }

    while (brokens.size())
    {
        child = static_cast<DataFile *>(hiddens.takeFirst());
        if (nullptr == child) continue;
        parent = static_cast<DataDirectory *>(child->parent());
        root = static_cast<DataDirectory *>(parent->parent());
        if (nullptr == root)
            idx = createIndex(m_oRoots.indexOf(parent), 0, parent);
        else
            idx = createIndex(root->of(parent), 0, parent);
        beginInsertRows(idx, parent->counts(), 1);
        parent->execBroken(false);
        parent->append(child);
        endInsertRows();
        connect(parent, SIGNAL(broken(bool)), child, SLOT(slotsDoBroken(bool)));
    }
    if (replaces.size()) doRecoveryRepleace(replaces);
    rootIndex(QModelIndex());
}

void LogicBurnData::slotsSetting()
{
    BUSNESSSETTING;
    MODAL->show();
    setting(m_oBurners.at(m_iIndex));
}

void LogicBurnData::slotsCreateISO(void *d)
{
    DataCDROM *cdrom;
    DataDirectory *root;
    QDir dir;
    QFileInfo f;
    QString s;
    QStringList ss;
    cdrom = (DataCDROM *)d;
    if (-1 != m_oBurners.indexOf(d)) return;
    debug(_("Add new create iso data file. %1").arg(cdrom->display()));
    s =cdrom->settingTempPath();
    s.append("/");
    s.append(cdrom->settingVolumnID());
    dir.setPath(s);
    if (dir.exists()) dir.removeRecursively();
    dir.mkpath(s);
    root = new DataDirectory(s, this);
    m_oRoots << root;
    m_oBurners << d;
    add(true);
    folder(true);
    //connect(root, SIGNAL(updateSize(ulong)), this, SIGNAL(size(ulong)));
    connect(root, SIGNAL(updateSize(ulong)), this, SLOT(slotsUpdateSize(ulong)));
    connect(cdrom, SIGNAL(volumnID(QString)), this, SLOT(slotsUpdateCDROM()));
    connect(cdrom, SIGNAL(tempPath(QString)), this, SLOT(slotsUpdateCDROM()));
    connect(MAIN, SIGNAL(storagePath(QString)), cdrom, SLOT(changeISOPath(QString)));
    if (!(cdrom->mediumMountPoint().isEmpty()))
    {
        dir.setPath(cdrom->mediumMountPoint());
        foreach (f, dir.entryInfoList(QDir::Files | QDir::AllEntries | QDir::Hidden))
        {
            if ("." == f.fileName() || ".." == f.fileName()) continue;
            ss << f.absoluteFilePath();
        }
        /*
        if (cdrom->mediumStatus() == DataMedium::MEDIUM_FINALIZED)
        {
            connect(this, SIGNAL(size(ulong)), cdrom, SLOT(updateMediumUsedSize(ulong)));
        }
        */
        if (ss.size()) addDiskData(ss, root);
        if (cdrom->mediumStatus() == DataMedium::MEDIUM_FINALIZED)
        {
            cdrom->updateMediumUsedSize(root->size());
        }
    }
    else
    {
        MODAL->accept();
    }
}

void LogicBurnData::slotsCreateISO()
{
    ProcessCreateISO *createISO;
    qDebug()<<__func__<<__LINE__<<m_iIndex;

    createISO = new ProcessCreateISO(m_oRoots.at(m_iIndex),
                                     m_oBurners.at(m_iIndex),
                                     this);
    connect(createISO, &ProcessCreateISO::clear, [=](){
        clearDatasUnderIndex(createIndex(m_iIndex, 0, m_oRoots.at(m_iIndex)));
    });
    createISO->exec();
    delete createISO;
}

void LogicBurnData::slotsBurnData()
{
#if 1
    DataCDROM *cdrom = (DataCDROM*)m_oBurners.at(m_iIndex);
    ProcessBurnData burnData(m_oRoots.at(m_iIndex), cdrom);
    cdrom->m_bStart = true;
    burnData.exec();
    for (int i = 0; i < m_oBurners.size(); ++i) {
        if(m_oBurners.at(i) == cdrom) {
            cdrom->m_bStart = false;
            break;
        }
    }
#else
    qDebug()<<"\n_________________________";
    qDebug()<<"call ProcessBurnData,finish ISO9660 Format burner";
    qDebug()<<"-------------------------\n";
#endif
}

//刻录UDF数据的槽函数
void LogicBurnData::slotsBurnUDFData()
{
#if 1
    ProcessBurnUDFData burnData(m_oRoots.at(m_iIndex) ,m_oBurners.at(m_iIndex));
    burnData.exec();
#else
    qDebug()<<"\n_________________________";
    qDebug()<<"call ProcessBurnUDFData,finish UDF Format burner";
    qDebug()<<"-------------------------\n";
#endif
}

void LogicBurnData::slotsEmptyMedium(void *)
{
    debug(_("Add new create iso data file."));
}

void LogicBurnData::slotsUpdateCDROM()
{
    int idx;
    DataCDROM *cdrom;
    DataDirectory *root;
    QString rootPath;

    idx = m_oBurners.indexOf(sender());
    if (-1 == idx) return;
    cdrom = (DataCDROM *)(sender());
    root = (DataDirectory *)(m_oRoots.at(idx));
    rootPath = cdrom->settingTempPath() + "/" + cdrom->settingVolumnID();
    root->rename(rootPath);
    size(root->size());
    updateDisk(idx, sender());
}

void LogicBurnData::slotsDoFilterHidden(void *data)
{
    emit doHidden(m_iIndex, data);
}

void LogicBurnData::slotsDoFilterBroken(void *data)
{
    emit doBroken(m_iIndex, data);
}

void LogicBurnData::slotsDoRepleace(void *data)
{
    emit doRepleace(m_iIndex, data);
}

void LogicBurnData::slotsUpdateSize(unsigned long s)
{
    DataDirectory *root;

    root = static_cast<DataDirectory *>(sender());
    if (root && -1 != m_iIndex && m_oRoots.indexOf(root) == m_iIndex) size(s);
}

int LogicBurnData::addDiskData(QStringList files, void *r)
{
    int ret;
    int idx;
    bool b;
    DataCDROM *cdrom;
    QString str;
    QFileInfo f;
    QModelIndex parent;

    idx = m_oRoots.indexOf(r);
    if (-1 == idx) return 0;

    cdrom = static_cast<DataCDROM *>(m_oBurners.at(idx));
    if (!cdrom) return 0;

    ret = 0;
    b = false;

    PROGRESS;
    title(_("Loading Disk Data."));
    MODAL->cancelEnable(false);
    MODAL->show();
    setRange(0, needAdd(files));
    b = true;

    //cdrom->use();

#if 0
    if (!MODAL->isHidden() && MODAL->canBeClose())
    {
        MODAL->close();
        PROGRESS;
        title(_("Loading Disk Data."));
        MODAL->cancelEnable(false);
        MODAL->show();
        setRange(0, needAdd(files));
        b = true;
    }
    if (MODAL->isHidden())
    {
        PROGRESS;
        title(_("Loading Disk Data."));
        MODAL->cancelEnable(false);
        MODAL->show();
        setRange(0, needAdd(files));
        b = true;
    }
#endif
    parent = createIndex(idx, 0, r);

    beginInsertRows(parent, 0, files.size());
    foreach (str, files)
    {
        QCoreApplication::processEvents();
        if (!m_bAppRunning) break;
        f.setFile(str);
        if (f.isSymLink() && f.absoluteFilePath().contains(f.symLinkTarget()))
            ret += addFile(f.absoluteFilePath(), r, true);
        else if (f.isDir())
            ret += addDirectoy(f.absoluteFilePath(), r, true);
        else
            ret += addFile(f.absoluteFilePath(), r, true);
        if (b) setValue(ret, cdrom->icon(), _("Loading %1...").arg(f.fileName()));
    }
    endInsertRows();
    //MODAL->close();
    MODAL->accept();
    //cdrom->release();
    return ret;
}

int LogicBurnData::needAdd(QStringList list)
{
    int ret;
    QFileInfo file;
    QDir dir;

    ret = 0;

    foreach (QString s, list)
    {
        file.setFile(s);
        if (file.isDir())
        {
            dir.setPath(s);
            ret += needAdd(dir.absolutePath());
        }
        ++ret;
    }
    return ret;
}

int LogicBurnData::needAdd(QString path)
{
    int ret;
    QFileInfo file(path);
    QDir dir(path);

    ret = 0;

    foreach (file, dir.entryInfoList())
    {
        if ("." == file.fileName() || ".." == file.fileName()) continue;
        if (file.isDir())
        {
            ret += needAdd(file.absoluteFilePath());
        }
        ++ret;
    }
    return ret;
}

int LogicBurnData::addDirectoy(QString path, void *d, bool disk)
{
    int ret;
    DataDirectory *parent;
    DataDirectory *item;
    QDir dir;
    QFileInfo f;
    QFileInfoList files;

    parent = static_cast<DataDirectory *>(d);
    if (nullptr == parent || !parent->isDirectory()) return 0;

    item = new DataDirectory(path, parent, disk);
    if (!item->valid())
    {
        addValue(1, item->icon(), _("Cannot added %1 to %2").arg(item->display()).arg(parent->display()));
        qDebug()<<__LINE__<<__func__<<"LogicBurnData"<<item->display()<<parent->display();
        delete item;
        return 0;
    }
    if (!disk)
    {
        connect(item, SIGNAL(doHidden(void*)), this, SLOT(slotsDoFilterHidden(void*)));
        connect(item, SIGNAL(doBroken(void*)), this, SLOT(slotsDoFilterBroken(void*)));
        connect(item, SIGNAL(doRepleace(void*)), this, SLOT(slotsDoRepleace(void*)));
    }
    ret = 1;
    dir.setPath(path);
    files = dir.entryInfoList(QDir::Files | QDir::AllEntries | QDir::Hidden);
    beginInsertRows(createIndex(parent->of(item), 0, item), 0, files.size() - 1);
    foreach (f, files)
    {
        QCoreApplication::processEvents();
        if ("." == f.fileName() || ".." == f.fileName()) continue;
        if (f.isSymLink() && (f.symLinkTarget().endsWith("./") || f.symLinkTarget().endsWith("../"))) continue;
        if (f.isDir())
            ret += addDirectoy(f.absoluteFilePath(), item, disk);
        else
            ret += addFile(f.absoluteFilePath(), item, disk);
        if (m_bCancel || !m_bAppRunning) break;
    }
    endInsertRows();
    if (!disk) item->filter();
    addValue(1, item->icon(), _("Added %1 to %2").arg(item->display()).arg(parent->display()));
    return ret;
}

int LogicBurnData::addFile(QString path, void *d, bool disk)
{
    DataDirectory *parent;
    DataFile *item;

    parent = static_cast<DataDirectory *>(d);
    if (nullptr == parent || !parent->isDirectory()) return 0;
    item = new DataFile(path, parent, disk);
    if (!item->valid())
    {
        addValue(1, item->icon(), _("Cannot add %1 to %2").arg(item->display()).arg(parent->display()));
        delete item;
        return 0;
    }
    if (!disk)
    {
        connect(item, SIGNAL(doHidden(void*)), this, SLOT(slotsDoFilterHidden(void*)));
        connect(item, SIGNAL(doBroken(void*)), this, SLOT(slotsDoFilterBroken(void*)));
        connect(item, SIGNAL(doRepleace(void*)), this, SLOT(slotsDoRepleace(void*)));
        item->filter();
    }
    addValue(1, item->icon(), _("Added %1 to %2").arg(item->display()).arg(parent->display()));
    qDebug()<<__LINE__<<__func__<<item->display()<<"LogicBurnData"<<parent->display();
    return 1;
}

//bool LogicBurnData::isAbleBurnData()
//{
//    DataDirectory *dir;
//    DataCDROM *cdrom;

//    if (0 == m_oRoots.size()) return false;
//    if (m_iIndex < 0 || m_iIndex >= m_oRoots.size()) return false;
//    dir = static_cast<DataDirectory *>(m_oRoots.at(m_iIndex));
//    if (m_iIndex >= m_oBurners.size()) return false;
//    cdrom = static_cast<DataCDROM *>(m_oBurners.at(m_iIndex));
//    //qDebug() <<__func__ << __LINE__ << m_iIndex << m_oRoots;
//    // DVD+/-RW的容量问题已解决
//    if (cdrom->isCDROM())
//    {
//        // zyj:添加文件的容量 > 光盘剩余容量时，"刻录/追加刻录"按钮不置灰
//        // 按钮点击后，出现"添加文件容量大于光盘剩余容量"提示弹框
//        //qDebug() << __func__ << __LINE__ << dir->nSize();
//        //qDebug() << __func__ << __LINE__ << cdrom->mediumFreeSize();
//        if (dir->nSize() >= cdrom->mediumFreeSize())
//            return false;
//    }
//    return true;
//}
