#ifndef Corrade_PluginManager_AbstractPlugin_h
#define Corrade_PluginManager_AbstractPlugin_h
/*
    This file is part of Corrade.

    Copyright © 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016,
                2017, 2018, 2019 Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

/** @file
 * @brief Class @ref Corrade::PluginManager::AbstractPlugin
 */

#include "Corrade/Containers/Pointer.h"
#include "Corrade/PluginManager/PluginManager.h"
#include "Corrade/PluginManager/visibility.h"
#include "Corrade/Utility/StlForwardString.h"
#include "Corrade/Utility/Utility.h"

#ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT
#include "Corrade/Utility/StlForwardVector.h"
#endif

namespace Corrade { namespace PluginManager {

/**
@brief Base class for plugin interfaces

Makes it possible to connects every plugin instance to its corresponding plugin
manager, which ensures the plugin can be unloaded only if there are no active
instances. Besides that, provides access to interface-specific and
plugin-specific metadata.

@section PluginManager-AbstractPlugin-subclassing Subclassing

Plugin interface classes have to provide the following:

-   A @ref pluginInterface() function, defining a unique plugin interface
    string. See its documentation for details.
-   A @ref pluginSearchPaths() function, defining hardcoded search paths for
    plugins in this interface (if any). See its documentation for details.
-   A pair of @ref initialize() / @ref finalize() functions, used to initialize
    and finalize global state of given plugin library. See their documentation
    for details.
-   A constructor with signature the same as
    @ref AbstractPlugin(AbstractManager&, const std::string&) so the plugin
    manager can load them
-   A constructor with signature the same as @ref AbstractPlugin(), in case it
    makes sense to instantiate the plugin directly without a plugin manager

A minimal example is below. The @cpp pluginSearchPaths() @ce function first
lists a path relative to executable directory (which would be used for example
when deploying a Windows build) and as a fallback a system location, depending
on `CMAKE_INSTALL_PREFIX` that's propagated from CMake via a @cpp #define @ce
generated by @cmake configure_file() @ce. The functions are inlined here, but
it's advised to move their definitions to a separate source file.

@snippet PluginManager.cpp AbstractPlugin

See @ref plugin-management for a detailed tutorial.

@see @ref AbstractManagingPlugin, @ref CORRADE_PLUGIN_REGISTER()
*/
class CORRADE_PLUGINMANAGER_EXPORT AbstractPlugin {
    template<class> friend class AbstractManagingPlugin;

    public:
        /**
         * @brief Plugin interface string
         *
         * Plugins implementing given interface have to define exactly the same
         * interface string, otherwise they won't be loaded. This can be used
         * to ensure the interface and plugin is correctly paired even in case
         * where there is no ABI mismatch A good practice is to use a "Java
         * package name"-style syntax. The interface name should also contain
         * version identifier to make sure the plugin will not be loaded with
         * incompatible interface version.
         */
        static std::string pluginInterface();

        #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT
        /**
         * @brief Plugin search paths
         *
         * List of hardcoded absolute or relative paths where to search for
         * plugins of given interface. Relative paths are relative to
         * @ref Utility::Directory::executableLocation() directory. Earlier
         * entries have more priority than later, search stops once an existing
         * directory is found. By default this function returns an empty list.
         * See also @ref PluginManager-Manager-paths for more information.
         * @partialsupport Not available on platforms without
         *      @ref CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT "dynamic plugin support".
         */
        static std::vector<std::string> pluginSearchPaths();
        #endif

        /**
         * @brief Initialize plugin
         *
         * You can override this function to perform initialization before any
         * plugin instance is created. Default implementation does nothing.
         *
         * If the plugin is static, this function is called on construction of
         * corresponding plugin manager, if the plugin is dynamic, this
         * function is called on plugin load. If the plugin is used directly
         * without plugin manager, you have to call this function manually (if
         * needed).
         *
         * @attention In case you derive a plugin from one that implements this
         *      function, it's advised to override it with an empty body so the
         *      base initialization routine doesn't get called second time when
         *      initializing the derived plugin.
         *
         * @see @ref finalize()
         */
        static void initialize();

        /**
         * @brief Finalize plugin
         *
         * You can override this function to perform finalization after all
         * plugin instances are destroyed. Default implementation does nothing.
         *
         * If the plugin is static, this function is called on destruction of
         * corresponding plugin manager, if the plugin is dynamic, this
         * function is called on plugin unload. If the plugin is used directly
         * without plugin manager, you have to call this function manually (if
         * needed).
         *
         * @attention In case you derive a plugin from one that implements this
         *      function, it's advised to override it with an empty body so the
         *      base finalization routine doesn't get called when finalizing
         *      only the derived plugin.
         *
         * @see @ref initialize()
         */
        static void finalize();

        /**
         * @brief Default constructor
         *
         * Define this constructor in your subclass only if you want to allow
         * using the interface or plugin without plugin manager.
         *
         * The @ref metadata() function will return @cpp nullptr @ce.
         */
        explicit AbstractPlugin();

        /**
         * @brief Plugin manager constructor
         *
         * Used by plugin manager. Don't forget to redefine this constructor in
         * all your subclasses.
         * @see @ref plugin(), @ref metadata()
         */
        explicit AbstractPlugin(AbstractManager& manager, const std::string& plugin);

        /** @brief Copying is not allowed */
        AbstractPlugin(const AbstractPlugin&) = delete;

        /**
         * @brief Move constructor
         *
         * In order to avoid unnecessary allocations of internal state, the
         * move is destructive, which means none of @ref plugin(),
         * @ref metadata(), @ref configuration() or
         * @ref AbstractManagingPlugin::manager() can be called on a moved-out
         * instance.
         */
        AbstractPlugin(AbstractPlugin&& other) noexcept;

        /**
         * @brief Destructor
         *
         * If instantiated through plugin manager, unregisters this instance
         * from it.
         */
        /* This is needed to be public virtual as AbstractPlugin* are deleted */
        virtual ~AbstractPlugin() = 0;

        /** @brief Copying is not allowed */
        AbstractPlugin& operator=(const AbstractPlugin&) = delete;

        /** @brief Only move construction is allowed */
        AbstractPlugin& operator=(AbstractPlugin&&) = delete;

        /**
         * @brief Whether the plugin can be deleted
         *
         * Called from PluginManager on all active instances before the plugin
         * is unloaded. Returns @cpp true @ce if it is safe to delete the
         * instance from the manager, @cpp false @ce if not. If any instance
         * returns @cpp false @ce, the plugin is not unloaded. See
         * @ref PluginManager-Manager-reload for more information.
         */
        virtual bool canBeDeleted();

        /**
         * @brief Identifier string
         *
         * Name under which the plugin was instantiated, either its true name
         * or an alias. If the plugin was not instantiated via plugin manager,
         * returns empty string. Use @cpp metadata()->name() @ce to get plugin
         * true name unconditionally.
         *
         * Can't be called on a moved-out instance.
         */
        const std::string& plugin() const;

        /**
         * @brief Metadata
         *
         * Metadata associated with given plugin. If the plugin was not
         * instantiated through a plugin manager, returns @cpp nullptr @ce.
         *
         * Can't be called on a moved-out instance.
         * @see @ref AbstractManager::metadata()
         */
        const PluginMetadata* metadata() const;

        /**
         * @brief Plugin-specific configuration
         *
         * Configuration associated with given plugin instance. Can be used to
         * modify plugin behavior beyond what's possible through the plugin
         * interface. Every instance gets a fresh copy from
         * @ref PluginMetadata::configuration(), modifications are affecting
         * only particular plugin instance. If the plugin was not instantiated
         * through a plugin manager or the @cb{.ini} [configuration] @ce
         * group was not present in the metadata, the returned group is empty.
         *
         * Can't be called on a moved-out instance.
         */
        Utility::ConfigurationGroup& configuration();
        const Utility::ConfigurationGroup& configuration() const; /**< @overload */

    private:
        /* used by AbstractManagingPlugin */
        explicit AbstractPlugin(AbstractManager& manager);
        AbstractManager* manager();
        const AbstractManager* manager() const;

        struct State;
        Containers::Pointer<State> _state;
};

}}

#endif
