/*=========================================================================

  Program:   ParaView
  Module:    vtkPVPluginFileLoader.h

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/**
 * @class vtkPVPluginFileLoader
 * @brief loads a ParView plugin file.
 *
 * vtkPVPluginFileLoader loads a plugin file. The default implementation
 * supports XML files and platform-specific shared libraries that provide
 * standard API calls.
 *
 * A plugin, in ParaView, is a vtkPVPlugin subclass that is activated by calling
 * `vtkPVPlugin::Import()`. On successful load of the plugin file,
 * `vtkPVPluginFileLoader` will call `vtkPVPlugin::Import` to active that plugin.
 *
 * @section SupportedFileFormats Supported File Formats
 *
 * vtkPVPluginFileLoader supports loading shared libraries and XML files.
 * Additional types of files can be supported by registering
 * a callback via `vtkPVPluginFileLoader::RegisterLoadPluginFileCallback`.
 *
 * Registered plugin callbacks are invoked in reverse registration order before
 * attempting to use the default mechanisms to load supported files. Thus,
 * custom applications can use this mechanism to add support for new formats as
 * well override default behavior.
 *
 * @section MultiplePlugins Loading multiple plugins
 *
 * vtkPVPluginFileLoader also supports loading multiple plugins.
 * TODO:
 */

#ifndef vtkPVPluginFileLoader_h
#define vtkPVPluginFileLoader_h

#include "vtkObject.h"
#include "vtkPVClientServerCoreCoreModule.h" //needed for exports

#include <functional> // for std::function
#include <vector> // for std::vector
#include <memory> // for std::shared_ptr
#include <string> // for std::string

class vtkPVPlugin;

class VTKPVCLIENTSERVERCORECORE_EXPORT vtkPVPluginFileLoader : public vtkObject
{
public:
  static vtkPVPluginFileLoader* New();
  vtkTypeMacro(vtkPVPluginFileLoader, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Load a plugin given its filename. vtkPVPluginFileLoader will attempt, via various
   * registered plugin file loading callbacks, to determine how to load the plugin. If the
   * all callbacks fail (including the default), then returns false.
   * Otherwise, the plugin will be attempted to be imported (using `vtkPVPlugin::Import`).
   * If the import succeeds, returns true, otherwise returns false.
   */
  bool LoadPluginFile(const char* fname);

  /**
   * Locates a plugin file (using `LoadPlugin`), given its name and
   * if found loads the file using `LoadPluginFile`.
   */
  bool LocateAndLoadPlugin(const char* pluginname);

  /**
   * Locates a plugin file given a plugin name and returns the full path to file. This will not
   * attempt to load the file and hence it may not point to a usable plugin.
   * The plugin file is located using the following strategy:
   * TODO: document search strategy.
   */
  virtual std::string LocatePlugin(const char* pluginname);

  /**
   * This methods is used to load a collection of plugins defined in what we call a
   * `plugin configuration`. This is a XML file of the following format:
   *
   * @code{xml}
   *
   *  <?xml version="1.0" ?>
   *  <Plugins>
   *    <Plugin name="[plugin name]"
   *            filename="[if present, path to plugin path]"
   *            auto_load="[if present, plugin is flagged as an auto-load plugin]"
   *    />
   *    <Plugin ... >
   *    ...
   *  </Plugins>
   *
   * @endcode
   *
   * Except for the `name` attribute on the `<Plugin>` node, all other attributes are optional.
   * If `filename` is missing, then vtkPVPluginFileLoader will attempt to locate the plugin file
   * using the same mechanism as `LocateAndLoadPlugin`.
   * If `auto_load` is present and set to 1, then plugin will be loaded, if located successfully.
   * Applications can load all located plugins, irrespective of the `auto_load` attributes by
   * passing the `force_load` parameter to this method.
   *
   */
   void LoadPluginConfiguration(const char* xmlconfig, bool force_load=false);

   /**
    * Load plugin configuration from a file.
    */
   void LoadPluginConfigurationFile(const char* configfilename, bool force_load=false);

   /**
    * Loads plugin files from plugin search paths. This is a legacy mechanism and
    * should not be used. It may be removed in future.
    * TODO: document default paths/env variables.
    */
   virtual void LoadPluginsFromSearchPaths();

   /**
    * Loads plugin configurations from search paths and environment variables.
    * TODO: document default paths/env variables.
    */
   virtual void LoadPluginConfigurationsFromSearchPaths();

  //@{
  /**
   */
  using LoadPluginFileCallbackType = std::function<std::shared_ptr<vtkPVPlugin>(const char*)>;
  static int RegisterLoadPluginFileCallback(LoadPluginFileCallbackType callback);
  static void UnregisterLoadPluginFileCallback(int id);
  //@}

  //@{
  /**
   *
   */
  static void RegisterSupportedPluginExtension(const std::string& ext, const std::string& prefix=std::string());

protected:
  vtkPVPluginFileLoader();
  ~vtkPVPluginFileLoader() override;

private:
  vtkPVPluginFileLoader(const vtkPVPluginFileLoader&) = delete;
  void operator=(const vtkPVPluginFileLoader&) = delete;

  static std::vector<LoadPluginFileCallbackType> Callbacks;

  static std::vector<std::pair<std::string, std::string>> PluginPrefixAndExtensions;

  /**
   * Goes over each of the registered callbacks in reverse order till the first
   * one returns a non-null vtkPVPlugin instance.
   *
   * `nameOrFile` generally refers to a valid filename, except in case of static
   * builds, in which case it refers to a plugin name instead.
   */
  static std::shared_ptr<vtkPVPlugin> CallPluginLoaderCallbacks(const char* nameOrFile);
};


#endif
