/***************************************************************************
            PluginManager.h  -  manager class for kwave's plugins
                             -------------------
    begin                : Sun Aug 27 2000
    copyright            : (C) 2000 by Thomas Eschenbacher
    email                : Thomas.Eschenbacher@gmx.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef PLUGIN_MANAGER_H
#define PLUGIN_MANAGER_H

#include "config.h"
#include "libkwave_export.h"

#include <QtGlobal>
#include <QList>
#include <QListIterator>
#include <QMap>
#include <QMutableListIterator>
#include <QObject>
#include <QPointer>
#include <QWidget>

#include "libkwave/InsertMode.h"
#include "libkwave/Sample.h"
#include "libkwave/ViewManager.h"

class QString;

class KPluginFactory;

namespace Kwave
{
    class PlaybackController;
    class PlayBackParam;
    class Plugin;
    class SampleSink;
    class SignalManager;

    /**
     * Manages the loading, initializing, starting, running and closing
     * of the plugins of kwave. Each instance of a TopWidget creates a
     * new instance of the PluginManager to be independent from other
     * toplevel widgets.
     */
    class LIBKWAVE_EXPORT PluginManager: public QObject
    {
        Q_OBJECT

    public:

        /**
         * Constructor.
         * @param parent reference to the toplevel widget (our parent)
         * @param signal_manager reference to a SignalManager
         */
        PluginManager(QWidget *parent, Kwave::SignalManager &signal_manager);

        /**
         * Default destructor
         */
        ~PluginManager() override;

        /**
         * Tries to load all plugins. If a pesistent plugin is found,
         * it will stay loaded in memory, all other (non-persistent)
         * plugins will be unloaded afterwards. This also filters out
         * all plugins that do not correctly load.
         * @internal used once by each toplevel window at startup
         * @return true if at least one plugin was loaded, false if none
         */
        bool loadAllPlugins();

        /**
         * Stops all currently running plugins
         */
        void stopAllPlugins();

        /**
         * Executes a plugin in the context of a given parent widget.
         * @param name the name of the plugin
         * @param params pointer to a parameter list
         *               or null if defaults should be used
         * @return zero on success or negative error code
         */
        int executePlugin(const QString &name, QStringList *params);

        /**
         * Returns true if there is no running plugin that blocks
         * a "close" operation.
         */
        bool canClose();

        /** Returns true if at least one plugin is currently running */
        bool onePluginRunning();

        /**
         * Waits until all currently running actions have completed.
         */
        void sync();

        /**
         * Loads a plugin, calls it's setup function and then closes
         * it again. The parameters will be loaded before the setup
         * and saved if the setup has not been aborted.
         * @param name the name of the plugin
         * @param params pointer to a parameter list
         *               or null if defaults should be used
         * @retval  0 if succeeded and accepted
         * @retval  1 if canceled
         * @retval -1 if failed
         */
        int setupPlugin(const QString &name, const QStringList &params);

        /**
         * loads a plugin's default parameters from the user's
         * configuration file. If nothing is found in the config file,
         * the return value will be 0. If the current version number of
         * the plugin does not match the version number in the config file,
         * the return value will also be 0.
         * @param name the name of the plugin
         * @return list of strings
         */
        QStringList defaultParams(const QString &name);

        /**
         * Returns the length of the current signal in samples.
         * If no signal is present the return value will be 0.
         */
        sample_index_t signalLength();

        /**
         * Returns the current sample rate in samples per second.
         * If no signal is present the return value will be 0.
         */
        double signalRate();

        /**
         * Returns the start of the selection. If nothing is currently
         * selected this will be the first sample (0).
         */
        sample_index_t selectionStart();

        /**
         * Returns the end of the selection. If nothing is currently
         * selected this will be the last sample (length-1).
         */
        sample_index_t selectionEnd();

        /**
         * Sets the current start and length of the selection to new values.
         * @param offset index of the first sample
         * @param length number of samples
         */
        void selectRange(sample_index_t offset, sample_index_t length);

        /**
         * Opens a Kwave::MultiTrackSink for playback purposes.
         * @param tracks number of tracks
         * @param playback_params points to a class that holds all playback
         *                        parameters. If null, the default parameters
         *                        of the current signal will be used
         * @return a multitrack sink that receives the playback stream
         */
        Kwave::SampleSink *openMultiTrackPlayback(
            unsigned int tracks,
            const Kwave::PlayBackParam *playback_params = nullptr
        );

        /**
         * Returns a reference to the current playback controller. This is
         * only needed for plugins doing playback.
         */
        Kwave::PlaybackController &playbackController();

        /**
         * assigns a new parent widget, to be used for messages
         * @param new_parent pointer to a QWidget
         */
        inline void setParentWidget(QWidget *new_parent) {
            m_parent_widget = new_parent;
        }

        /** returns a pointer to the parent widget */
        inline QPointer<QWidget> parentWidget()
        {
            return m_parent_widget;
        }

        /** returns a reference to our signal manager */
        inline Kwave::SignalManager &signalManager()
        {
            return m_signal_manager;
        }

        /**
         * Insert a new signal view into this widget (or the upper/lower
         * dock area.
         * @param view the signal view, must not be a null pointer
         * @param controls a widget with controls, optionally, can be null
         */
        void insertView(Kwave::SignalView *view, QWidget *controls);

        /**
         * registers a view manager, must only be called once!
         */
        void registerViewManager(Kwave::ViewManager *view_manager);

        /**
         * Enqueues a command that will be processed threadsafe in the X11
         * thread.
         */
        void enqueueCommand(const QString &command);

        /**
         * Searches the standard KDE data directories for plugins (through
         * the KDE's standard search algorithm) and creates a map of
         * plugin names and file names. First it collects a list of
         * filenames and then filters it to sort out invalid entries.
         */
        void searchPluginModules();

        /** structure with information about a plugin */
        typedef struct  {
            QString            m_name;        /**< name of the plugin   */
            QString            m_author;      /**< name of the author   */
            QString            m_description; /**< short description    */
            QString            m_version;     /**< settings version     */
            KPluginFactory    *m_factory;     /**< plugin factory       */
            int                m_use_count;   /**< usage counter        */
        } PluginModule;

        /**
         * returns a list with info of all known plugins
         * @todo rename to pluginModuleList
         */
        const QList<PluginModule> pluginInfoList() const;

        /**
         * Migrate a plugin to the currently active file context (which
         * might be different from the one that is currently executing
         * the plugin). The plugin will be removed from our lists and
         * inserted into the currently active plugin manager instance.
         * @param plugin the plugin to migrate
         */
        void migratePluginToActiveContext(Kwave::Plugin *plugin);

        /** Let this instance be the active one */
        void setActive() { m_active_instance = this; }

    signals:

        /**
         * Forwards commands to the parent TopWidget execute a command
         */
        void sigCommand(const QString &command);

        /**
         * Informs all plugins and client windows that we close down
         */
        void sigClosed();

        /**
         * Informs the plugins that the name of the signal has changed.
         * This might be used to update the caption of a window.
         */
        void sigSignalNameChanged(const QString &name);

        /**
         * informs about progress, e.g. for showing a message in
         * a splash screen or status bar.
         */
        void sigProgress(const QString &message);

    public slots:

        /**
         * Notify all plugins that the signal or file is to be closed
         */
        void signalClosed();

        /**
         * Called if the name of the current signal has changed. This will be
         * forwarded to all plugins by emitting the signal sigSignalNameChanged.
         * @see sigSignalNameChanged()
         */
        void setSignalName(const QString &name);

    private slots:

        /**
         * Will be connected to the plugin's "closed" signal.
         * @param p pointer to the plugin to be closed
         */
        void pluginClosed(Kwave::Plugin *p);

        /** called when a plugin has started (running) it's worker thread */
        void pluginStarted(Kwave::Plugin *p);

        /** called when a plugin has finished it's worker thread */
        void pluginDone(Kwave::Plugin *p);

    private:

        /** typedef: QPointer to a Kwave::Plugin */
        typedef QPointer<Kwave::Plugin> KwavePluginPointer;

        /** typedef: list of pointers to kwave plugins */
        typedef QList< KwavePluginPointer > PluginList;

        /** typedef: mutable iterator for PluginList */
        typedef QMutableListIterator< KwavePluginPointer >
            PluginListMutableIterator;

        /** typedef: const iterator for PluginList */
        typedef QListIterator< KwavePluginPointer >
            PluginListIterator;

    private:

        /**
         * Creates an instance of a plugin.
         * @param name the name of the plugin (filename)
         * @return pointer to the loaded plugin or zero if the
         *         plugin was not found or invalid
         */
        Kwave::Plugin *createPluginInstance(const QString &name);

        /**
         * Saves a plugin's default parameters to the user's configuration
         * file. The whole section in the configuration file will be deleted
         * before saving the new settings in order to wipe out invalid
         * entries and settings that belong to an older version.
         * @param name the name of the plugin
         * @param params a list of configuration strings
         */
        void savePluginDefaults(const QString &name,
                                QStringList &params);

        /** connects all signals of and for a plugin */
        void connectPlugin(Kwave::Plugin *plugin);

        /** connects all signals from and to a plugin */
        void disconnectPlugin(Kwave::Plugin *plugin);

    private:

        /** pointer to the currently active instance */
        static Kwave::PluginManager *m_active_instance;

        /**
         * map with plugin information: key = short name of the plugin,
         * data = plugin info (description, author, version etc...)
         */
        static QMap<QString, PluginModule> m_plugin_modules;

        /** list of all plugins that were loaded by this instance */
        PluginList m_plugin_instances;

        /** list of currently running plugins */
        PluginList m_running_plugins;

        /** reference to our parent toplevel widget */
        QPointer<QWidget> m_parent_widget;

        /** reference to our signal manager */
        Kwave::SignalManager &m_signal_manager;

        /** interface for registering a SignalView */
        ViewManager *m_view_manager;

    };
}

#endif /* PLUGIN_MANAGER_H */

//***************************************************************************
//***************************************************************************
