#include "serial_manager.h"
#include "protocol_plugin.h"

#include <QDir>
#include <QPluginLoader>

void SerialManager::registerProtocol(const QString& name, const ProtocolBundle& bundle) {
    if (name.isEmpty())
        return;
    m_protocols.insert(name, bundle);
    if (m_activeName.isEmpty())
        m_activeName = name;
}

bool SerialManager::setActiveProtocol(const QString& name) {
    if (!m_protocols.contains(name))
        return false;
    m_activeName = name;
    return true;
}

ProtocolBundle SerialManager::activeBundle() const {
    if (!m_protocols.contains(m_activeName))
        return {};
    return m_protocols.value(m_activeName);
}

QStringList SerialManager::loadPlugins(const QString& dirPath, QStringList* errors) {
    QStringList loaded;
    if (!m_pluginLoaders.empty()) {
        return loaded;
    }

    QDir dir(dirPath);
    if (!dir.exists()) {
        if (errors)
            errors->append(QStringLiteral("Plugin dir not found: %1").arg(dirPath));
        return loaded;
    }

    QStringList filters;
#if defined(Q_OS_WIN)
    filters << QStringLiteral("*.dll");
#elif defined(Q_OS_MAC)
    filters << QStringLiteral("*.dylib");
#else
    filters << QStringLiteral("*.so");
#endif

    const QStringList entries = dir.entryList(filters, QDir::Files);
    for (const QString& fileName : entries) {
        const QString path = dir.absoluteFilePath(fileName);
        auto loader = std::make_unique<QPluginLoader>(path);
        QObject* instance = loader->instance();
        if (!instance) {
            if (errors)
                errors->append(QStringLiteral("Load failed: %1 (%2)").arg(path, loader->errorString()));
            continue;
        }

        auto* plugin = qobject_cast<IProtocolPlugin*>(instance);
        if (!plugin) {
            if (errors)
                errors->append(QStringLiteral("Invalid plugin: %1").arg(path));
            loader->unload();
            continue;
        }
        if (plugin->apiVersion() != kProtocolPluginApiVersion) {
            if (errors)
                errors->append(QStringLiteral("API mismatch: %1").arg(path));
            loader->unload();
            continue;
        }

        const ProtocolBundle bundle = plugin->createBundle();
        if (!bundle.codec || !bundle.decoder || !bundle.format) {
            if (errors)
                errors->append(QStringLiteral("Missing components: %1").arg(path));
            loader->unload();
            continue;
        }

        const QString name = plugin->protocolName();
        if (!m_protocols.contains(name)) {
            registerProtocol(name, bundle);
            loaded.append(name);
            m_pluginLoaders.push_back(std::move(loader));
        } else {
            loader->unload();
        }
    }

    return loaded;
}
