#include "logicmain.h"
#include "src/frame/logger.h"
#include "src/frame/configuration.h"
#include "src/frame/signals.h"
#include "src/po/translation.h"
#include "src/model/monitorcdrom.h"
#include "src/model/menufactory.h"
#include "src/data/datacdrom.h"
#include "src/data/newdevicetype.h"
#include "src/view/modaldialog.h"
#include "src/view/mainwindow.h"
#include "src/view/filefilterrules.h"
#include "src/frame/signals.h"
#include "src/status-fix/discmonitor.h"
#include <unistd.h>
#include <pwd.h>
#include <QDebug>
#include <QStandardPaths>
#include <QFileInfo>
#include <QMutex>
#include <QThread>
#include <QDebug>
#include <QEventLoop>
#include <windowmanager/windowmanager.h>

#define USE_NEW_DEVICE

LogicMain *LogicMain::m_oInstance = nullptr;
QMutex     LogicMain::m_oMutex;

LogicMain::LogicMain(QObject *parent) :
    QObject(parent),
    m_iBusness(-1),
    m_oTheme(nullptr)
{
    QFileInfo f;
    struct passwd *pwd;
    setObjectName("LogicMain");

    m_oCDROMS.clear();
    m_oDiscMonitor = new DiscMonitor();
    m_oDiscList    = DiscMonitor::allDiscDevices();   //静态获取已连接的光盘

    LOGGER->registerName(this, _("MAIN"));

    if (QGSettings::isSchemaInstalled("org.ukui.style"))
    {
        m_oTheme = new QGSettings("org.ukui.style");
        m_oCurrentIcon = m_oTheme->get("iconThemeName").toString();
        m_oCurrnetThemeBackground = theme();

        connect(m_oTheme, &QGSettings::changed, [=](){
            if (m_oCurrentIcon != m_oTheme->get("iconThemeName").toString())
            {
                m_oCurrentIcon = m_oTheme->get("iconThemeName").toString();
                emit g_user_signal->iconChange(QIcon::fromTheme("burner"));
            }

            QString themeBg = theme();
            if (m_oCurrnetThemeBackground != themeBg) {
                m_oCurrnetThemeBackground = themeBg;
                WindowManager::activateWindow(MainWindow::Instance()->getWinId());
            }
        });

        auto handleFontSizeChange = [&](){
            int fontSizeKey = m_oTheme->get("systemFontSize").toString().toInt();
            //fix theme fontsize bug
            if(fontSizeKey == 0){
                fontSizeKey = 14;
            }
            if (fontSizeKey > 0) {
                emit systemFontSizeChanged(fontSizeKey);
            }
        };

        //monitor fontsize change
        connect(m_oTheme, &QGSettings::changed,this,[=] (const QString &key) {
            if(key == "systemFontSize") {
                handleFontSizeChange();
            }else if(key == "systemFont"){
                QString fontName = m_oTheme->get(key).toString();
                emit systemFontnameChanged(fontName);
            }

        });
    }

    pwd = getpwuid(getuid());

    msg = QDBusMessage::createMethodCall( QString("com.kylinUserGuide.hotel_%1")
                                          .arg(pwd->pw_gid),"/", "com.guide.hotel",
                                          "showGuide");
    f.setFile("/usr/share/kylin-user-guide/data/guide/kylin-burner");
    msg << "kylin-burner";
    msg.setDelayedReply(false);


#if 0
    connect(MONITOR, SIGNAL(addCDROM(QString)), this, SLOT(createCDROM(QString)));
    connect(MONITOR, SIGNAL(changeCDROM(QString)), this, SLOT(changeCDROM(QString)));
    connect(MONITOR, SIGNAL(removeCDROM(QString)), this, SLOT(removeCDROM(QString)));
    LOGGER->debug(_("Connect MONITOR signal addCDROM to createCDROM, for creating new"
            " cdrom information."));
#else
    connect(m_oDiscMonitor, &DiscMonitor::discDriveAdd, this, &LogicMain::driveAddedSlot);
    connect(m_oDiscMonitor, &DiscMonitor::discDriveRemove, this, &LogicMain::driveRemovedSlot);
    connect(m_oDiscMonitor, &DiscMonitor::discMediaAdd, this, &LogicMain::discAddedSlot);
    connect(m_oDiscMonitor, &DiscMonitor::discMediaRemove, this, &LogicMain::discRemovedSlot);
#endif

    connect(MenuFactory::instance(), SIGNAL(burnSetting(bool)), this, SLOT(slotsMenuBurnSetting()));

    connect(this, &LogicMain::newCDromArrived, MenuFactory::instance(), &MenuFactory::newCDROM);

    connect(MenuFactory::instance(), SIGNAL(dataSetting(bool)), this, SLOT(slotsDataSetting()));
    connect(MenuFactory::instance(), SIGNAL(diskCheck(bool)), this, SLOT(slotsDiskCheck()));
    connect(MenuFactory::instance(), SIGNAL(help(bool)), this, SLOT(slotsHelp()));

    connect(this, &LogicMain::CDRomRemoved, MenuFactory::instance(), &MenuFactory::takeCDROM);

    connect(g_user_signal, &GlobalUserSignal::noMedium, this, &LogicMain::mediumRmoved);
}

LogicMain *LogicMain::instance()
{
    if (nullptr == m_oInstance)
    {
        QMutexLocker locker(&m_oMutex);

        if (nullptr == m_oInstance)
        {
            m_oInstance = new LogicMain();
            QThread * thread = LogicMain::getThread();
            m_oInstance->moveToThread(thread);
            thread->start();
        }
    }
    return m_oInstance;
}

QThread * LogicMain::getThread()
{
    static QThread thread;
    return &thread;
}

void LogicMain::destroy()
{
    LOGGER->info(QString("( LogicMain::destroy ) Line:%1 准备析构LogicMain").arg(__LINE__));
    LogicMain::getThread()->quit();
    LogicMain::getThread()->wait();
}

void LogicMain::init()
{
     currentDiscs();
}

void LogicMain::initISO()
{
    DataCDROM *c;
    QString path;

    path = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
    createCDROM(CFG->get("CreateISO", "DefaultPath", path).toString());
    path = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
    path = CFG->get("CopyImage", "DefaultPath", path).toString();
    if (!path.endsWith("/")) path.append("/");
#ifdef USE_NEW_DEVICE
    c = new ExtendDataCDRom(path,this);
#else
    c = new DataCDROM(path, this);
#endif
    createCopyImage(c);
}

QString LogicMain::theme()
{
    QString ret;

    ret = m_oTheme->get("styleName").toString();
    if (ret == "ukui-dark") ret = "ukui-black";
    return ret;
}

QString LogicMain::icon()
{
    return m_oTheme->get("iconThemeName").toString();
}

int LogicMain::GetSystemFontSize()
{
    if (QGSettings::isSchemaInstalled("org.ukui.style"))
    {
        int fontSizeKey = m_oTheme->get("systemFontSize").toString().toInt();

        //fix theme fontsize bug
        if(fontSizeKey == 0){
            fontSizeKey = 14;
        }

        if (fontSizeKey > 0) {
            return fontSizeKey;
        }
    }
    return 10;
}

void LogicMain::createCDROM(QString blockName)
{
    //当插入光驱后在打开刻录 不会接收到changeDevice信号；
    //当打开软件后 在插入光驱，会有一个creatDevice信号，2个changeDevice信号。
    //光驱门 打开后 发出两个changeDevice信号
    //光驱门 关闭   发出

    LOGGER->info(QString("( LogicMain::createCDROM ) Line:%1 进入函数，dev = %2").arg(__LINE__).arg(blockName));
    DataCDROM *cdrom;

    if (nullptr == sender())
    {
        blockName.append("/");
        blockName.append(CFG->get("CreateISO", "VolumnID", _("KylinBurner")).toString());
        blockName.append(".iso");
    }
    LOGGER->debug(_("Creating new cdrom named %1").arg(blockName));
    if (!MODAL->isHidden()) MODAL->accept();
    cdrom = static_cast<DataCDROM *>(m_oCDROMS.value(blockName));
    if (cdrom)
    {
        LOGGER->warn(QString("LogicMain::createCDROM Line:%1  dev=%2 已经被加载，退出").arg(__LINE__).arg(blockName));
        return;
    }
    else
    {
        qDebug()<<__LINE__<<__func__<<"Creating new cdrom named"<<blockName << "current threadid:" << QThread::currentThreadId();
        LOGGER->info(QString("LogicMain::createCDROM Line:%1  dev=%2 将被创建").arg(__LINE__).arg(blockName));
#ifdef USE_NEW_DEVICE
        cdrom = new ExtendDataCDRom(blockName, this);
#else
        cdrom = new DataCDROM(blockName, this);
#endif
        m_oCDROMS.insert(blockName, cdrom);
    }

    if (cdrom->isCDROM())
    {
        QThread::msleep(200);
        qDebug()<<__LINE__<<__func__<<"new cdrom "<<blockName << "is real CDROM" << "current threadid:" << QThread::currentThreadId();
        connect(cdrom, SIGNAL(empty()),this, SLOT(slotsEmptyMedium()),Qt::UniqueConnection);
        connect(cdrom, SIGNAL(appendale()), this, SLOT(slotsAppendableMedium()),Qt::UniqueConnection);
        connect(cdrom, SIGNAL(finished()), this, SLOT(slotsFinishedMedium()),Qt::UniqueConnection);

        if(!cdrom->m_bUpdateStart)
        {
            bool result = false;
            cdrom->m_bUpdateStart = true;
            qDebug()<<"createCDROM update() start " << blockName;
#ifdef USE_NEW_DEVICE
            result = dynamic_cast<ExtendDataCDRom*>(cdrom)->update();
#else
            result = cdrom->update();
#endif
            LOGGER->warn(QString("LogicMain::createCDROM Line:%1  dev=%2 update() 执行结果为 result=%3 ").arg(__LINE__).arg(blockName).arg(result));
            qDebug()<<"createCDROM update() end " << blockName;
            cdrom->m_bUpdateStart = false;
            if (!result) {
                LOGGER->warn(QString("LogicMain::createCDROM Line:%1  dev=%2 result=%3 update()获取光盘信息失败，光盘不可用").arg(__LINE__).arg(blockName).arg(result));
                return;
            }
         }
    }
    else
    {
        emit createISO(cdrom);
    }
    emit newCDromArrived(cdrom);
}
/*
 * changeCDROM接收来自光驱的信号，当插入光驱、弹出光驱托盘、关光驱托盘都会接收到changeCDROM信号
 * 注意：不同光驱接收到的changeCDROM信号次数不太一样
 *
 */

//插入光盘： 接收一次createCDROM 和 两次changeCDROM（插入光驱打开刻录软件和打开刻录软件在再插入光驱接收到的changeCDROM信号次数不一样）
  //       createCDROM
/*
void LogicMain::changeCDROM(QString blockName)
{
    DataCDROM *cdrom;
    LOGGER->debug(_("Change CDROM, block name is %1").arg(blockName));
    QThread::sleep(1);

    cdrom = static_cast<DataCDROM *>(m_oCDROMS.value(blockName));
    if (nullptr == cdrom)
    {
        // createCDROM(blockName);
        return;
    }
    if(!cdrom->m_bStart)
    {
        //光驱没有开始刻录

        if(!cdrom->m_bUpdateStart)
        {
            //update没有开始

            qDebug() << "change update() 开始了";
            cdrom->m_bUpdateStart = true;

            if (!MODAL->isHidden() && !cdrom->isEjected()) MODAL->close();
            LOGGER->debug(_("Change CDROM %1").arg(cdrom->display()));
            noMedium(cdrom);

            qDebug()<<"changeCDROM update() "<< blockName;


            emit g_user_signal->showTip_loadMedium();
            cdrom->update();
            qDebug() << "changeCDROM update() 结束";
            qDebug()<< "";
            emit g_user_signal->hideTip_loadMedium();
            cdrom->m_bUpdateStart = false;


            //如果removeDevice后update()又执行完了，会delete 光驱。
            //如果不进行判断光驱是否存在，且不能用cdrom==null，因为没有置空，只能用计数值判断
            //否则cdrom->mediumStatus出错
            if(cdrom->m_iUsing > 0) //有光驱
            {
                if (cdrom->mediumStatus() < 0) //没光盘
                {
                    LOGGER->debug(_("There is no medium in CDROM %1").arg(cdrom->display()));
                    qDebug() << "changeDevice 执行update()后，光驱没有被拔，但没有光盘";
                    noMedium(cdrom);


                    qDebug() << "";
                    qDebug() << "这是一个changCDROM  end---------------";
                    qDebug() << "";


                    return;
                }
                else //有光盘
                {
                    qDebug() << "changeDevice 执行update()后，光驱没有被拔，且有光盘";
                }
            }
            else //没有光驱了，可能还在执行该条命令
            {
                qDebug() << "changeDevice 执行update()后，光驱被拔=NULL";
            }
        }
        else
        {
            qDebug() << "change update() 已经开始了，不操作了";


            qDebug() << "";
            qDebug() << "这是一个changCDROM  end---------------";
            qDebug() << "";

            return ;
        }
    }
    else
    {
        //光驱正在进行刻录，不进行操作
        qDebug() << "光驱正在进行刻录，不进行操作";
    }



    qDebug() << "";
    qDebug() << "这是一个changCDROM  end---------------";
    qDebug() << "";

}
*/

/*
void LogicMain::removeCDROM(QString block)
{
    DataCDROM *cdrom;
    LOGGER->info(QString("( LogicMain::removeCDROM ) Line:%1 进入函数， dev = %2").arg(__LINE__).arg(block));
    cdrom = static_cast<DataCDROM *>(m_oCDROMS.value(block));
    if (!MODAL->isHidden()) MODAL->accept();
    if (nullptr == cdrom) return;
    LOGGER->debug(_("Remove CDROM %1").arg(block));
    qDebug()<<__LINE__<<__func__<<"Remove CDROM %1"<< block;
    m_oCDROMS.remove(block);
    qDebug()<<__LINE__<<__func__<<"Remove CDROM %1"<< block;
    emit g_user_signal->removeDisc(block);
    //cdrom->release();
    noMedium(cdrom); // 首先移除数据
    deleteCDROM(cdrom); // 移除界面展示中遗留的光驱信息。
}
*/
void LogicMain::slotsEmptyMedium()
{
    DataCDROM *cdrom;

    cdrom = qobject_cast<DataCDROM *>(sender());

    LOGGER->debug(_("Get empty medium in CDROM %1").arg(cdrom->display()));
    LOGGER->debug(_("BurnerName : %1 DiskName : %2").arg(cdrom->display()).arg(cdrom->mDisplay()));

    emit emptyMedium(sender());
}

void LogicMain::slotsAppendableMedium()
{
    DataCDROM *cdrom;

    cdrom = qobject_cast<DataCDROM *>(sender());
    LOGGER->debug(_("Get appendable medium in CDROM %1").arg(cdrom->display()));
    LOGGER->debug(_("BurnerName : %1 DiskName : %2").arg(cdrom->display()).arg(cdrom->mDisplay()));

    emit appendableMedium(sender());
}

void LogicMain::slotsFinishedMedium()
{
    DataCDROM *cdrom;

    cdrom = qobject_cast<DataCDROM *>(sender());

    LOGGER->debug(_("Get finished medium in CDROM %1").arg(cdrom->display()));
    LOGGER->debug(_("BurnerName : %1 DiskName : %2").arg(cdrom->display()).arg(cdrom->mDisplay()));

    emit finishedMedium(sender());
}

void LogicMain::slotsMenuBurnSetting()
{
    if (m_iBusness < CreateISO || m_iBusness > GetISO) return;
    if (m_iBusness < BurnImage) burnDataSetting();
    else if (m_iBusness == BurnImage) burnImageSetting();
    else burnCopySetting();
}


void LogicMain::slotsSetBusness(int bus)
{
    if (bus < CreateISO || bus > GetISO)
    {
        LOGGER->error(_("Undefined busness."));
        emit functionMenuChanged(MenuFactory::instance()->getMenu(bus));
        return;
    }
    m_iBusness = bus;
    emit functionMenuChanged(MenuFactory::instance()->getMenu(bus));
    LOGGER->debug(_("Change to do busness %1").arg(m_iBusness));
}

void LogicMain::slotsDoBusness()
{
    switch (m_iBusness)
    {
    case CreateISO:
        LOGGER->debug(_("Prepare to create iso file."));
        emit createISO();
        break;
    case BurnISO9660Data:
        LOGGER->debug(_("Prepare to burn data."));
        emit burnData();
        break;
    case BurnUDFData:
        //qDebug() << "Prepare to burn UDF Format data.";
        LOGGER->debug(_("Prepare to burn UDF Format data."));
        emit burnData();
        break;
    case AddBurn:
        LOGGER->debug(_("Prepare to append burn."));
        emit addBurn();
        break;
    case BurnImage:
        LOGGER->debug(_("Prepare to burn image."));
        emit burnImage();
        break;
    case CopyData:
        LOGGER->debug(_("Prepare to copy disk data."));
        emit copyData();
        break;
    case GetISO:
        LOGGER->debug(_("Prepare to get iso from disk data."));
        emit getISO();
        break;
    default:
        LOGGER->error(_("Invalid busness [%1]").arg(m_iBusness));
        break;
    }
}

void LogicMain::slotsDataSetting()
{
    ModalDialog::instance()->showPage(ModalDialog::DataSetting);
    ModalDialog::instance()->show();
}

void LogicMain::slotsDiskCheck()
{
    ModalDialog::instance()->showPage(ModalDialog::DiskCheck);
    ModalDialog::instance()->show();
}

void LogicMain::slotsHelp()
{
    QDBusConnection::sessionBus().call(msg);
}


void LogicMain::discAddedSlot(QString blockName)
{

    qDebug()<<"检测到光盘"<<blockName<<"插入";
    LOGGER->info(QString("new media [%1] insert.").arg(blockName));

    DataCDROM *cdrom = nullptr;
    if (m_oCDROMS.contains(blockName))
    {
        LOGGER->warn(QString("LogicMain::discAddedSlot Line:%1  dev=%2 已经被加载，退出").arg(__LINE__).arg(blockName));
        return;
    }
    else
    {
        qDebug()<<__LINE__<<__func__<<"Creating new cdrom named"<<blockName << "current threadid:" << QThread::currentThreadId();
        LOGGER->info(QString("LogicMain::discAddedSlot Line:%1  dev=%2 将被创建").arg(__LINE__).arg(blockName));
#ifdef USE_NEW_DEVICE
        cdrom = new ExtendDataCDRom(blockName, this);
#else
        cdrom = new DataCDROM(blockName, this);
#endif
        m_oCDROMS.insert(blockName, cdrom);
    }


    //判断是否是光驱
    if (cdrom->isCDROM())
    {
        qDebug()<<__LINE__<<__func__<<"new cdrom "<<blockName << "is real CDROM" << "current threadid:" << QThread::currentThreadId();

        //判断是空盘 还是可追加 还是不可写入
        connect(cdrom, SIGNAL(empty()),this, SLOT(slotsEmptyMedium()),Qt::UniqueConnection);
        connect(cdrom, SIGNAL(appendale()), this, SLOT(slotsAppendableMedium()),Qt::UniqueConnection);
        connect(cdrom, SIGNAL(finished()), this, SLOT(slotsFinishedMedium()),Qt::UniqueConnection);



        if(!cdrom->m_bUpdateStart)
        {
            bool result = false;
            cdrom->m_bUpdateStart = true;
            qDebug()<<"LogicMain::discAddedSlot update() start " << blockName;
#ifdef USE_NEW_DEVICE
            result = dynamic_cast<ExtendDataCDRom*>(cdrom)->update();
#else
            result = cdrom->update();
#endif
            LOGGER->warn(QString("LogicMain::discAddedSlot Line:%1  dev=%2 update() 执行结果为 result=%3 ").arg(__LINE__).arg(blockName).arg(result));
            qDebug()<<"LogicMain::discAddedSlot update() end " << blockName;
            cdrom->m_bUpdateStart = false;
            if (!result) {
                LOGGER->warn(QString("LogicMain::discAddedSlot Line:%1  dev=%2 result=%3 update()获取光盘信息失败，光盘不可用").arg(__LINE__).arg(blockName).arg(result));
                return;
            }
         }
    }
    else
    {
        emit createISO(cdrom);
    }

    emit this->newCDromArrived(cdrom);

    //运行完update()可能光驱被拔出了，所以要判断一下，不要出现段错误
    if(cdrom->m_is_using > 0 && cdrom->isCDROM())//有光驱
    {
        cdrom->use();
        if(cdrom->mediumStatus() >=0)//有光盘
        {
            //qDebug() << "LogicMain::discAddedSlot update()运行完后，有光驱，有光盘发信号haveMedium";
            emit g_user_signal->haveMedium(cdrom);
        }
        else
        {
            emit mediumRmoved(cdrom);
        }
        cdrom->release();
    }
}


/** 光盘状态信息查询完成的槽函数
 *  在此处可以对UI界面做一些信息的矫正更新操作、或者进行实质性的功能操作(如卸载光盘、刻录等)
*/
void LogicMain::discStatusQuerySlot(DiscRead* statusQuery){

}

void LogicMain::discRemovedSlot(QString block)
{
    LOGGER->info(QString("(LogicMain::discRemovedSlot) Line:%1 进入函数， dev = %2").arg(__LINE__).arg(block));
    DataCDROM *cdrom = static_cast<DataCDROM *>(m_oCDROMS.value(block));

    if (!MODAL->isHidden())
        MODAL->accept();

    if (nullptr == cdrom)
        return;

    LOGGER->info(_("Remove CDROM %1").arg(block));
    qDebug()<<__LINE__<<__func__<<"Remove CDROM %1"<< block;
    m_oCDROMS.remove(block);

    emit g_user_signal->removeDisc(block);

    emit mediumRmoved(cdrom); // 首先移除数据
    emit CDRomRemoved(cdrom); // 移除界面展示中遗留的光驱信息。

    QTimer timer;
    timer.setSingleShot(true);
    QEventLoop loop;
    connect(&timer,&QTimer::timeout,&loop,&QEventLoop::quit);
    timer.start(2000);
    loop.exec();

    delete cdrom;
    cdrom = nullptr;

}

void LogicMain::driveAddedSlot(QString block)
{
    qDebug()<<"检测到光驱"<<block<<"插入";
}

void LogicMain::currentDiscs()
{
    qDebug() <<"-----------光驱------------";
    for(int i=0;i<m_oDiscList.size();++i)
    {
        qDebug() << "(LogicMain::currentDiscs) 光驱设备【" << i <<"】"<< m_oDiscList.at(i);
        LOGGER->info(QString("(LogicMain::currentDiscs) 光驱设备[%1]:%2").arg(i).arg(m_oDiscList.at(i)));
        DataCDROM *cdrom;
        cdrom = static_cast<DataCDROM *>(m_oCDROMS.value(m_oDiscList.at(i)));
        if (cdrom){
            LOGGER->error(QString("(LogicMain::currentDiscs) CDROM %1 already loaded.").arg(cdrom->display()));
            break;
        } else {
            qDebug() << "(LogicMain::currentDiscs) currnetDiscs 创建DataCDROM";
            LOGGER->info(QString("(LogicMain::currentDiscs) currnetDiscs 创建DataCDROM"));
#ifdef USE_NEW_DEVICE
            cdrom = new ExtendDataCDRom(m_oDiscList.at(i), this);
#else
            cdrom = new DataCDROM(m_oDiscList.at(i), this);
#endif
        }

        m_oCDROMS.insert(m_oDiscList.at(i), cdrom);
        connect(cdrom, SIGNAL(empty()),this, SLOT(slotsEmptyMedium()),Qt::UniqueConnection);
        connect(cdrom, SIGNAL(appendale()), this, SLOT(slotsAppendableMedium()),Qt::UniqueConnection);
        connect(cdrom, SIGNAL(finished()), this, SLOT(slotsFinishedMedium()),Qt::UniqueConnection);

#ifdef USE_NEW_DEVICE
            dynamic_cast<ExtendDataCDRom*>(cdrom)->update();
#else
            cdrom->update();
#endif
        emit this->newCDromArrived(cdrom);
    }
}

void LogicMain::driveRemovedSlot(QString block)
{
    qDebug()<<"检测到光驱"<<block<<"移除";
}



LogicMain::~LogicMain()
{
    LOGGER->info(QString("[%1] [%2] 析构").arg(__func__).arg(__LINE__));
}
