#include "pluginmanager.h"
#include <QDir>
#include <QJsonParseError>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include "logger.h"

PluginManager::PluginManager(QObject *parent)
    : QObject{parent}
{
    initPluginInfo();
    loadPlugins();
}

PluginManager::~PluginManager()
{
    savePluginInfo();
    clear();
}

void PluginManager::initPluginInfo()
{
    QString fileName = QString("%1/%2").arg(QDir::currentPath(), "plugininfo.json");
    QFile file(fileName);
    if(file.exists() && file.open(QIODevice::ReadOnly))
    {
        QJsonParseError error;
        QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
        if(error.error == QJsonParseError::NoError && doc.isObject())
        {
            QJsonObject obj = doc.object();
            QJsonArray array = obj["configure"].toArray();
            foreach(auto item, array)
            {
                LOG_INFO("record plugin name: {}", item["name"].toString().toUtf8().data());
                LOG_INFO("record plugin path: {}", item["path"].toString().toUtf8().data());
                LOG_INFO("record plugin enabled: {}", item["enabled"].toBool());

                QFileInfo fileInfo(item["path"].toString());
                if(fileInfo.exists())
                {
                    plugin_info_st info;
                    info.fileName = item["name"].toString();
                    info.filePath = item["path"].toString();
                    info.isEnabled = item["enabled"].toBool();
                    m_pluginInfoMap.insert(info.fileName, info);
                }
            }
        }
    }

    QDir dir;
    QString path;
    QFileInfoList fileInfoList;
    path = QString("%1/%2").arg(QDir::currentPath(), "plugins");
    dir.setPath(path);
    fileInfoList = dir.entryInfoList(QStringList() << "*.so", QDir::Files | QDir::NoSymLinks);

    foreach(auto fileInfo, fileInfoList)
    {
        if(m_pluginInfoMap.contains(fileInfo.fileName()))
            continue;

        LOG_INFO("unrecord plugin name: {}", fileInfo.fileName().toUtf8().data());
        LOG_INFO("unrecord plugin path: {}", fileInfo.filePath().toUtf8().data());
        LOG_INFO("unrecord plugin enabled: {}", true);

        plugin_info_st info;
        info.fileName = fileInfo.fileName();
        info.filePath = fileInfo.filePath();
        info.isEnabled = true;
        m_pluginInfoMap.insert(fileInfo.fileName(), info);
    }

    savePluginInfo();
}

void PluginManager::savePluginInfo()
{
    QJsonObject info;
    QJsonArray array;

    QList<plugin_info_st> pluginInfoList = m_pluginInfoMap.values();
    foreach(auto info, pluginInfoList)
    {
        QJsonObject obj;
        obj["name"] = info.fileName;
        obj["path"] = info.filePath;
        obj["enabled"] = info.isEnabled;
        array.push_back(obj);
    }
    info["configure"] = array;

    QJsonDocument doc(info);
    QString fileName = QString("%1/%2").arg(QDir::currentPath(), "plugininfo.json");
    QFile file(fileName);
    if(file.open(QIODevice::ReadWrite | QIODevice::Truncate))
    {
        file.write(doc.toJson(QJsonDocument::Indented));
    }
    else
    {
        LOG_ERROR("{}", file.errorString().toUtf8().data());
    }
}

void PluginManager::loadPlugins()
{
    QList<plugin_info_st> pluginInfoList = m_pluginInfoMap.values();
    foreach(auto info, pluginInfoList)
    {
        PluginLoader *pPluginLoader = new PluginLoader(info.filePath);
        if(pPluginLoader->load())
        {
            if(info.isEnabled)
                m_customWidgets << pPluginLoader->customWidgets();
            else
                pPluginLoader->unload();
            m_pluginLoaderMap.insert(info.fileName, pPluginLoader);
        }
        else
        {
            LOG_ERROR("plugin loader load failed: {}", info.filePath.toUtf8().data());
            delete pPluginLoader;
            pPluginLoader = nullptr;
        }
    }
}

void PluginManager::loadPlugin(QString fileName)
{
    auto it = m_pluginLoaderMap.find(fileName);
    if(it == m_pluginLoaderMap.end())
    {
        plugin_info_st info = m_pluginInfoMap.value(fileName);
        PluginLoader *pPluginLoader = new PluginLoader(info.filePath);
        if(pPluginLoader->load())
        {
            if(info.isEnabled)
                m_customWidgets << pPluginLoader->customWidgets();
            else
                pPluginLoader->unload();
            m_pluginLoaderMap.insert(info.fileName, pPluginLoader);
        }
    }
    else
    {
        it.value()->load();
        QList<QDesignerCustomWidgetInterface *> widgets = it.value()->customWidgets();
        m_customWidgets << widgets;
    }
}

void PluginManager::unloadPlugin(QString fileName)
{
    auto it = m_pluginLoaderMap.find(fileName);
    if(it == m_pluginLoaderMap.end())
        return;
    QList<QDesignerCustomWidgetInterface *> widgets = it.value()->customWidgets();
    foreach(auto widget, widgets)
    {
        if(m_customWidgets.contains(widget))
            m_customWidgets.removeOne(widget);
    }
    it.value()->unload();
}

bool PluginManager::delPlugin(QString fileName, bool keepInfo)
{
    auto it = m_pluginLoaderMap.find(fileName);
    if(it == m_pluginLoaderMap.end())
        return false;
    QFile file(it.value()->fileName());
    QList<QDesignerCustomWidgetInterface *> widgets = it.value()->customWidgets();
    foreach(auto widget, widgets)
    {
        if(m_customWidgets.contains(widget))
            m_customWidgets.removeOne(widget);
    }
    delete it.value();
    it.value() = nullptr;
    m_pluginLoaderMap.erase(it);
    if(!keepInfo)
        m_pluginInfoMap.remove(fileName);
    return file.remove();
}

bool PluginManager::upgradePlugin(QString fileName, QString srcFile)
{
    bool ret;
    ret = delPlugin(fileName, true);
    if(ret)
    {
        QString dstFile = QString("%1/%2").arg(QDir::currentPath(), fileName);
        ret = QFile::copy(srcFile, dstFile);
        if(ret)
        {
            auto it = m_pluginInfoMap.find(fileName);
            if(it != m_pluginInfoMap.end() && it.value().isEnabled)
                loadPlugin(fileName);
        }
    }
    return ret;
}

bool PluginManager::addPlugin(QString srcFile)
{
    QFileInfo fileInfo(srcFile);
    QString dstFile = QString("%1/%2").arg(QDir::currentPath(), fileInfo.fileName());
    bool ret =  QFile::copy(srcFile, dstFile);
    if(ret)
    {
        plugin_info_st info;
        info.fileName = fileInfo.fileName();
        info.filePath = dstFile;
        info.isEnabled = true;
        m_pluginInfoMap.insert(info.fileName, info);
        loadPlugin(info.fileName);
    }
    return ret;
}

bool PluginManager::contains(QString fileName)
{
    return m_pluginInfoMap.contains(fileName);
}

void PluginManager::clear()
{
    qDeleteAll(m_pluginLoaderMap);
    m_pluginLoaderMap.clear();
    m_customWidgets.clear();
    m_pluginInfoMap.clear();
}

QList<plugin_info_st> PluginManager::pluginInfoList() const
{
    return m_pluginInfoMap.values();
}

QList<QDesignerCustomWidgetInterface *> PluginManager::customWidgets() const
{
    return m_customWidgets;
}
