/***************************************************************************
               Plugin.h  -  base class of all Kwave plugins
                             -------------------
    begin                : Thu Jul 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_H
#define PLUGIN_H

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

#include <QAtomicInt>
#include <QtGlobal>
#include <QList>
#include <QMutex>
#include <QObject>
#include <QRecursiveMutex>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QVariantList>
#include <QVector>

#include <KPluginFactory>

#include "libkwave/Runnable.h"
#include "libkwave/Sample.h"
#include "libkwave/String.h"

class QProgressDialog;
class QVariant;

/**
 * @def KWAVE_PLUGIN(name,class)
 * @param name short internal name of the plugin, used for the library
 *             file name, in most cases identical to the directory name
 * @param class name of the C++ class that implements the plugin, without
 *              namespace
 */
#define KWAVE_PLUGIN(name,class)                               \
    K_PLUGIN_FACTORY_WITH_JSON(kwaveplugin_##name##_factory,   \
                               "kwaveplugin_"#name".json",     \
                               registerPlugin<Kwave::class>();)

namespace Kwave
{

    class ConfirmCancelProxy;
    class PluginManager;
    class SignalManager;
    class TopWidget;
    class WorkerThread;

    /**
     * Generic class that should be used for all types of Kwave plugins.
     * This interface is the only one that should be used, it provides
     * all necessary functions to access the functionality of the main
     * Kwave program.
     */
    class LIBKWAVE_EXPORT Plugin: public QObject, public Kwave::Runnable
    {
        Q_OBJECT

    public:

        /**
         * Constructor
         * @param parent pointer to our plugin manager
         * @param args argument list for initializing the plugin
         */
        Plugin(QObject *parent, const QVariantList &args);

        /**
         * Destructor.
         */
        ~Plugin() override;

        /** Returns the name of the plugin. */
        virtual QString name() const;

        /** Returns the description of the plugin (already translated). */
        virtual QString description() const;

        /**
         * Returns a text for the progress dialog if enabled.
         * (already be localized)
         */
        virtual QString progressText();

        /**
         * Returns true if the plugin can be closed.
         * The default implementation returns "!isRunning()"
         */
        virtual bool canClose() const;

        /**
         * Returns true if the plugin has a running thread.
         */
        bool isRunning() const;

        /**
         * Returns true if the plugin should stop, e.g. when the
         * user has pressed "cancel"
         */
        bool shouldStop() const;

        /**
         * Called after the plugin has been loaded into memory. This is
         * useful for plugins that don't use start() and execute(),
         * maybe for some persistent plugins like playback and record.
         * The default implementation does nothing.
         */
        virtual void load(QStringList &params);

        /**
         * Called before the plugin gets unloaded. Can be used by a plugin
         * to do some cleanup tasks before it gets unloaded from memory.
         */
        virtual void unload();

        /**
         * Sets up all necessary parameters for executing the plugin. Could
         * be overwritten and show a dialog box. This only will be called if
         * currently no parameters are given from the function that loads
         * the plugin (the normal case) but not in the context of replaying
         * a previously recorded macro.
         * @param previous_params the parameters of a previous call, could
         *        be used to initialize the controls of a setup dialog
         * @return a string list with all parameters, an empty list (if nothing
         *         has to be set up) or null if the setup (dialog) has been
         *         aborted and the plugin should not get executed
         */
        virtual QStringList *setup(QStringList &previous_params);

        /**
         * Is called from the main program before the run() function and can
         * be overwritten to show a window or initialize some things before
         * the run() function gets called.
         * @param params list of strings with parameters
         * @return an error code if the execution failed or zero if everything
         *         was ok.
         */
        virtual int start(QStringList &params);

        /**
         * Stops any threads and is called from the close() function and
         * the plugin's destructor.
         */
        virtual int stop();

        /**
         * Gets called from the plugin's execute function and should be
         * overwritten to perform some action. This function runs in a
         * separate thread!
         * @param params list of strings with parameters
         * @see  sigDone
         */
        virtual void run(QStringList params);

        /**
         * Returns a reference to the manager of this plugin.
         */
        Kwave::PluginManager &manager() const;

        /** Returns a reference to the current signal manager */
        Kwave::SignalManager &signalManager();

        /**
         * Returns the parent widget of the plugin. This normally should be
         * a TopWidget of the Kwave main program.
         */
        QWidget *parentWidget() const;

        /**
         * Returns the name of the current signal. This can be used to set the
         * caption of a plugin's main window. If no signal is currently loaded
         * the returned string is empty.
         */
        QString signalName();

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

        /**
         * Returns the sample rate of the current signal. If no signal is
         * present the return value will be zero.
         */
        virtual double signalRate();

        /**
         * Returns an array of indices of currently selected channels.
         */
        virtual const QVector<unsigned int> selectedTracks();

        /**
         * Returns the left and right sample index of the current selection
         * in samples from 1 to (size-1). The left and right samples
         * are included in the selection and might be equal. The left
         * is always less or equal than the right. Note that there is
         * always at least one sample selected!
         *
         * @param tracks received a list of selected tracks
         *               (optional or null-pointer)
         * @param left receives the first selected sample
         *             (optional or null-pointer)
         * @param right receives the last selected sample
         *              (optional or null-pointer)
         * @param expand_if_empty if set to true, the selection will be made
         *                        equal to the whole signal if left==right
         * @return the number of selected samples (right-left+1) [1..length]
         */
        virtual sample_index_t selection(
            QVector<unsigned int> *tracks = nullptr,
            sample_index_t *left = nullptr,
            sample_index_t *right = nullptr,
            bool expand_if_empty = false);

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

        /**
         * Migrates this plugin instance to the currently active file context.
         * This might be necessary for plugins that create a new file context
         * and then expect that all operations are executed in the context of
         * that new file context. Example: the record plugin creates a new
         * context and does recording into that new created file context.
         */
        virtual void migrateToActiveContext();

    protected:

        friend class Kwave::PluginManager;

        /**
         * Gets called to execute the plugin's run function in a separate
         * thread.
         * @param params list of strings with parameters
         * @bug the return value is never evaluated
         */
        int execute(QStringList &params);

        /** emits a sigCommand() */
        void emitCommand(const QString &command);

        /** increments the usage counter */
        void use();

        /** assign this plugin to a new plugin manager (when migrating) */
        void setPluginManager(Kwave::PluginManager *new_plugin_manager);

    signals:

        /**
         * will be emitted when the "run" function starts
         * @see run
         */
        void sigRunning(Kwave::Plugin *plugin);

        /**
         * will be emitted when the "run" function has finished
         * @see run
         */
        void sigDone(Kwave::Plugin *plugin);

        /** will be emitted in the plugin's destructor */
        void sigClosed(Kwave::Plugin *p);

        /** can be used by plugins to execute toplevel commands */
        void sigCommand(const QString &command);

        /**
         * emitted when cancel() is called, can be connected
         * to the cancel() slot of child objects
         */
        void sigCancel();

        /**
         * Sets the text of the progress dialog
         * @param text new progress bar text, already be localized
         */
        void setProgressText(const QString &text);

    public slots:

        /**
         * Switches the support for a progress dialog on [default] or off
         */
        virtual void setProgressDialogEnabled(bool enable);

        /**
         * update the progress dialog
         * @param progress the current progress in percent [0...100]
         */
        virtual void updateProgress(qreal progress);

        /**
         * called when the user has pressed "Cancel" in the progress
         * dialog and also has confirmed the cancel confirmation
         * message box.
         */
        virtual void cancel();

        /**
         * Called to close the plugin. This will be called from the plugin
         * manager and can as well be used from inside the plugin if it
         * wishes to close itself.
         */
        virtual void close();

        /** decrements the usage counter */
        void release();

    private slots:

        /** closes the progress dialog and the confirm/cancel proxy */
        void closeProgressDialog(Kwave::Plugin *);

        /** updates the progress bar, triggered by timer */
        void updateProgressTick();

    protected:

        friend class Kwave::WorkerThread;

        /** Wrapper for run() that contains a call to release() */
        void run_wrapper(const QVariant &params) override;

    private:

        /** reference to the plugin manager */
        Kwave::PluginManager *m_plugin_manager;

        /** name of the plugin, for undo/redo */
        QString m_name;

        /** description of the plugin, for GUI purposes, captions etc */
        QString m_description;

        /**
         * Thread that executes the run() member function.
         */
        Kwave::WorkerThread *m_thread;

        /** Mutex for control over the thread */
        QMutex m_thread_lock;

        /** determines whether a progress dialog should be used in run() */
        bool m_progress_enabled;

        /** flag for stopping the process (stop if set to non-zero) */
        QAtomicInt m_stop;

        /** a progress dialog, if the audio processing takes longer... */
        QProgressDialog *m_progress;

        /**
         * proxy dialog that asks for confirmation if the user
         * pressed cancel in the progress dialog
         */
        Kwave::ConfirmCancelProxy *m_confirm_cancel;

        /** Usage counter */
        unsigned int m_usage_count;

        /** Mutex for locking the usage counter */
        QMutex m_usage_lock;

        /** timer for updating the progress dialog */
        QTimer m_progress_timer;

        /** latest progress value [percent] */
        qreal m_current_progress;

        /** Mutex for locking the progress bar */
        QRecursiveMutex m_progress_lock;

    };

}

#endif /* PLUGIN_H */

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