.. _particle-plugins-page:

##############
Particle Reader Plugins
##############

Particle Reader Plugin Manager
=====================

The :class:`ParticleReaderPluginManager <cee::pt::ParticleReaderPluginManager>` supports runtime discovery and loading
of particle reader plugins. Plugins are shared libraries following the naming convention ``cpt_{NAME}.dll`` (Windows),
``libcpt_{NAME}.so`` (Linux), or ``libcpt_{NAME}.dylib`` (macOS).

.. code-block:: cpp

    #include "CeeParticleModel/ParticleReaderPluginManager.h"

    auto* mgr = cee::pt::ParticleReaderPluginManager::instance();
    mgr->setPluginFolder("/opt/envision/plugins");
    mgr->loadPlugins();

    // List available reader factories
    for (size_t i = 0; i < mgr->readerFactoryCount(); ++i)
    {
        cee::Str name = mgr->readerName(i);
        // ...
    }

    // Create a reader for a specific file (auto-selects the right plugin)
    cee::pt::ParticleDatasetReader* reader = mgr->createReaderForFile("data/sim.custom");

    // IMPORTANT: destroy via the manager (the plugin owns the memory)
    mgr->destroyReader(0, reader);

Once plugins are loaded, they are also available for automatic format detection when calling
:func:`ParticleModel::open(filePath) <cee::pt::ParticleModel::open>`.

Call :func:`shutdown() <cee::pt::ParticleReaderPluginManager::shutdown>` before application exit to unload plugin
libraries cleanly.


Writing a Particle Reader Plugin
=================================

A reader plugin is a shared library that exports a single C entry point:

.. code-block:: c

    #include "CeeParticleModel/ParticleReaderPluginApi.h"

    extern "C" CPT_PLUGIN_EXPORT CPT_PluginExitFunc
    initializeReaderPlugin(const CPT_FrameworkServices* services)
    {
        // Register one or more readers
        CPT_RegisterReaderParams params = {};
        params.pluginApiMajorVer = CPT_API_MAJOR_VER;
        params.pluginApiMinorVer = CPT_API_MINOR_VER;
        params.createReaderFunc  = &myCreateReader;
        params.destroyReaderFunc = &myDestroyReader;
        params.canReadFunc       = &myCanRead;

        services->registerReaderFunc("MyFormat", &params);

        // Optionally set plugin metadata
        services->setPluginInfoFunc("version", "1.0.0");
        services->setPluginInfoFunc("author", "My Company");

        return &myPluginExit;  // Called on shutdown
    }

The ``createReaderFunc`` must return a new :class:`ParticleDatasetReader <cee::pt::ParticleDatasetReader>` subclass
instance. The ``canReadFunc`` should return ``true`` if the plugin can handle the given file path (typically by checking
the file extension or magic bytes).

See :doc:`/api/ParticleReaderPluginApi_8h` for the complete C API reference.
