#include "device_map.h"

#include <QJsonParseError>
#include <QJsonDocument>
#include <QJsonObject>

#include "ukui_printer.h"
#include "udev_devices.h"
#include "common.h"
#include "system_notification.h"
#include "event_notify_monitor.h"
#include "global_variable.h"
#include "gsettings_monitor.h"

DeviceMap::DeviceMap(/* args */)
{
    if(!GsettingsMonitor::getInstance()->gsettingsCheck()) {
        qDebug() << "GSettings init failed!";
        exit(0);
    }

    connect(EventNotifyMonitor::getInstance(), &EventNotifyMonitor::rhPrinterRemoved,
            this , &DeviceMap::doPrinterRemove);
    connect(EventNotifyMonitor::getInstance(), &EventNotifyMonitor::rhPrinterAdded,
            this , &DeviceMap::doPrinterAdd);

    // 开机启动重新扫一遍设备 更新gsetting
    generateMap();
    jsonWrite();
    // checkPathAdd("/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8.4");
}

DeviceMap::~DeviceMap()
{

}

bool DeviceMap::generateMap()
{
    QStringList devs = list_printers_dev_path();
    QStringList uris;
    for (auto dev : devs) {
        DeviceInformation devInfo;
        do_add(nullptr , QTC(dev), devInfo);
        qDebug() << devInfo;
        uris.append(devInfo.uri);
    }
    QList<UkuiUsbPrinter> printers;
    UkuiPrinterManager::getInstance().enumPrinters(printers);
    for (auto prt : printers) {
        int idx = uris.indexOf(prt.device_uri);
        if (idx == -1) {
            continue;
        }
        DevInfo info;
        info.uri = uris.at(idx);
        info.path = devs.at(idx);
        info.deviceId = get_device_id_from_path(devs.at(idx));
        this->m_devMp.insert(prt.name, info);
    }
    return true;
}

bool DeviceMap::jsonRead()
{
    QByteArray s = GsettingsMonitor::getInstance()->printerDevicesGet();

    QJsonParseError jsonParserError;
    QJsonDocument jsonDocument = QJsonDocument::fromJson(s, &jsonParserError);

    if (jsonDocument.isNull() || jsonParserError.error != QJsonParseError::NoError) {
        qDebug () << "json解析失败";
        return false;
    }
    if (!jsonDocument.isObject()) {
        return false;
    }
    QJsonObject jsonObject = jsonDocument.object();
    qDebug() << jsonObject;
    if (jsonObject.size() == 0) {
        qDebug() << "json is empty!";
        return false;
    }
    this->m_devMp.clear();
    QJsonObject::Iterator it = jsonObject.begin();
    for (it = jsonObject.begin(); it != jsonObject.end(); it++) {
        if (!it.value().isObject() || it.key().isEmpty())
            continue;
        QJsonObject subObj = it.value().toObject();
        DevInfo info;
        if (subObj.contains("path") && subObj.value("path").isString()
         && subObj.contains("uri")  && subObj.value("uri").isString()
         && subObj.contains("deviceId") && subObj.value("deviceId").isString()) {
            info.path = subObj.value("path").toString();
            info.uri  = subObj.value("uri").toString();
            info.deviceId  = subObj.value("deviceId").toString();
        }
        else {
            continue;
        }
        this->m_devMp.insert(it.key(), info);
    }

    if (this->m_devMp.isEmpty()) {
        return false;
    }
    return true;
}

int DeviceMap::jsonWrite()
{
    QJsonObject jsonObject;
    QMap<QString,DevInfo>::iterator it;
    for (it = this->m_devMp.begin(); it != this->m_devMp.end(); it++) {
        QJsonObject subObj;
        subObj.insert("path", it.value().path);
        subObj.insert("uri", it.value().uri);
        subObj.insert("deviceId", it.value().deviceId);
        jsonObject.insert(it.key(), subObj);
    }

    QJsonDocument jsonDoc;
    jsonDoc.setObject(jsonObject);

    GsettingsMonitor::getInstance()->printerDevicesSet(jsonDoc.toJson());
    return jsonObject.size();
}

bool DeviceMap::checkPathAdd(const QString &path)
{
    return checkPath("add", path);
}

bool DeviceMap::checkPathRemove(const QString &path)
{
    return checkPath("remove", path);
}

bool DeviceMap::checkPath(const QString &handle, const QString &path)
{
    if (handle.isEmpty() || path.isEmpty())
        return false;
    jsonRead();
    QStringList prts;
    QMap<QString,DevInfo>::iterator it;

    // 插入usb的时候需要使用device_id检测
    // 拔出usb的时候使用path检测
    if (handle == "add") {
        bool configNeedUpdate = false;
        QString deviceId = get_device_id_from_path(path);
        if (deviceId.isEmpty()) {
            return false;
        }

        for (it = this->m_devMp.begin(); it != this->m_devMp.end(); it++) {
            if (it.value().deviceId == deviceId) {
                prts.append(it.key());
                if (it.value().path != path) {
                    configNeedUpdate = true;
                    it.value().path = path;
                }
            }
        }

        // 之前安装过但是没有记录到gsetting中
        if (prts.isEmpty()) {
            configNeedUpdate = true;
            generateMap();
            for (it = this->m_devMp.begin(); it != this->m_devMp.end(); it++)
                if (it.value().deviceId == deviceId)
                    prts.append(it.key());
        }

        if (configNeedUpdate) {
            jsonWrite();
        }
    } else if (handle == "remove") {
        for (it = this->m_devMp.begin(); it != this->m_devMp.end(); it++) {
            if (it.value().path == path) {
                prts.append(it.key());
            }
        }
    }

    QString msg;
    int prtsCnt = prts.size();
    if (prtsCnt == 0)
        return false;
    if (prtsCnt == 1)
        msg = tr("Printer");
    else if (prtsCnt > 1)
        msg = tr("Printers");
    if (handle == "add")
        msg += QString(" ") + tr("plug-in:") + QString(" ") + prts.join(',');
    else if (handle == "remove")
        msg += QString(" ") + tr("unplugged:") + QString(" ") + prts.join(',');

    SystemNotification().sendMessage(msg);
    return true;
}

void DeviceMap::doPrinterRemove(const QString &prtName)
{
    jsonRead();
    this->m_devMp.remove(prtName);
    jsonWrite();
    return ;
}

void DeviceMap::doPrinterAdd(const QString &prtName)
{
    jsonRead();
    QStringList devs = list_printers_dev_path();
    QStringList uris;
    for (auto dev : devs) {
        DeviceInformation devInfo;
        do_add(nullptr , QTC(dev), devInfo);
        qDebug() << devInfo;
        uris.append(devInfo.uri);
    }
    QList<UkuiUsbPrinter> printers;
    UkuiPrinterManager::getInstance().enumPrinters(printers);
        for (auto prt : printers) {
        if (prtName != prt.name) {
            continue;
        }
        int idx = uris.indexOf(prt.device_uri);
        if (idx == -1) {
            continue;
        }
        DevInfo info;
        info.uri = uris.at(idx);
        info.path = devs.at(idx);
        info.path = get_device_id_from_path(devs.at(idx));
        this->m_devMp.insert(prt.name, info);
    }
    jsonWrite();
    return ;
}

DeviceMap *DeviceMap::getInstance()
{
    static DeviceMap *instance = nullptr;
    static QThread *thread;
    if (nullptr == instance) {
        instance = new DeviceMap();
        thread = new QThread();
        instance->moveToThread(thread);
        thread->start();
    }
    return instance;
}
