/***************************************************** vim:set ts=4 sw=4 sts=4:
  This file is the template for the processing plug ins.
  -------------------
  Copyright : (C) 2002-2003 by José Pablo Ezequiel "Pupeno" Fernández
  Copyright : (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net>
  -------------------
  Original author: José Pablo Ezequiel "Pupeno" Fernández <pupeno@kde.org>
  Current Maintainer: Gary Cramblitt <garycramblitt@comcast.net>
 ******************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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; version 2 of the License.               *
 *                                                                         *
 ***************************************************************************/

#ifndef _PLUGINPROC_H_
#define _PLUGINPROC_H_

#include <qobject.h>
#include <qstring.h>
#include <kdemacros.h>
#include "kdeexportfix.h"
#include <kconfig.h>

/**
* @interface PlugInProc
*
* pluginproc - the KDE Text-to-Speech Deamon Plugin API.
*
* @version 1.0 Draft 1
*
* This class defines the interface that plugins to KTTSD must implement.
*
* @warning The pluginproc interface is still being developed and is likely
* to change in the future.
*
* A KTTSD Plugin interfaces between KTTSD and a speech engine.  It provides the methods
* used by KTTSD to synthesize and/or audibilize text into speech.
*
* @section Goals
*
* The ideal plugin has the following features (listed most important to least important):
* - It can synthesize text into an audio file without sending the audio to
*   the audio device. If the plugin can do this, the @ref supportsSynth function
*   should return True.
* - When @ref stopText is called, is able to immediately stop an in-progress
*   synthesis or saying operation.
* - It can operate asynchronously, i.e., returns immediately from a
*   @ref sayText or @ref synthText call and emits signals @ref sayFinished or
*   @ref synthFinished when completed.  If the plugin can do this, the @ref supportsAsync
*   function should return True.
*
* As a plugin author, your goal is to provide all 3 of these features.  However,
* the speech engine you are working with might not be able to support all three
* features.
*
* If a plugin cannot do all 3 of the features above, the next best combinations
* are (from best to worst):
*
* - @ref supportsSynth returns True, @ref supportsAsync returns False, and
*   @stopText is able to immediately stop synthesis.
* - @ref supportsSynth returns True, @ref supportsAsync returns False, and
*   @stopText returns immediately without stopping synthesis.
* - @ref supportsAsync returns True, @ref supportsSynth returns False, and
*   @ref stopText is able to immediately stop saying.
* - Both @ref supportsSynth and @ref supportsAsync both return False, and
*   @ref stopText is able to immediately stop saying.
* - @ref supportsAsync returns True, @ref supportsSynth returns False, and
*   @ref stopText returns immediately without stopping saying.
* - Both @ref supportsSynth and @ref supportsAsync both return False, and
*   @ref stopText returns immediately without stopping saying.
*
* Notice that aynchronous support is not essential because KTTSD is able to
* provide aynchronous support by running the plugin in a separate thread.
* The ability to immediately stop audio output (or support separate synthesis
* only) is more important.
*
* @section Minimum Implementations
*
* All plugins should implement @ref init in order to initialize the speech engine,
* set language codes, etc.
*
* If @ref supportsSynth return False, a plugin must implement @ref sayText .
*
* If @ref supportsSynth returns True, a plugin must implement the following methods:
* - @ref synthText
* - @ref getFilename
* - @ref ackFinished
* The plugin need not implement @ref sayText .
*
* If @ref supportsAsync returns True, the plugin must implement @ref getState .
*
* If @ref supportsAsync returns True, and @ref supportsSynth returns True, 
* a plugin must emit @ref synthFinished signal when synthesis is completed.
*
* If @ref supportsAsync returns True, and @ref supportsSynth returns False, 
* a plugin must emit @ref sayFinished signal when saying is completed.
*
* If @ref supportsAsync returns False, do not emit signals @ref sayFinished
* or @ref synthFinished .
*
* @section Implementation Guidelines
*
* In no case, will a plugin need to perform more than one @ref sayText or
* @ref synthText at a time.  In other words, in asynchronous mode, KTTSD will
* not call @ref sayText or @ref synthText again until @ref @getState returns
* psFinished.
*
* If @ref supportsAsync returns False, KTTSD will run the plugin in a separate
* QThread.  As a consequence, the plugin must not make use of the KDE Library,
* when @ref sayText or @ref synthText is called,
* with the exception of KProcess and family (KProcIO, KShellProcess).
* This restriction comes about because the KDE Libraries make use of the
* main Qt event loop, which unfortunately, runs only in the main thread.
* This restriction will likely be lifted in Qt 4 and later.
*
* Since the KDE library is not available from the @ref sayText and @ref synthText methods,
* it is best if the plugin reads configuration settings in the @ref init method.
* The KConfig object is passed as an argument to @ref init .
*
* If the synthesis engine requires a long initialization time (more than a second),
* it is best if the plugin loads the speech engine from the @ref init method.
* Otherwise, it will be more memory efficient to wait until @ref sayText or 
* @ref synthText is called, because it is possible that the plugin will be created
* and initialized, but never used.
*
* All plugins, whether @ref supportsAsync returns True or not, should try to
* implement @ref stopText .  If a plugin returns False from @ref supportsAsync,
* @ref stopText will be called from the main thread, while @ref sayText and/or
* @ref synthText will be called from a separate thread.  Hence, it will be
* possible for @ref stopText to be called while @ref sayText or @ref synthText is
* running.  Keep this in mind when implementing the code.
*
* If the plugin returns True from @ref supportsAsync, you will of course
* need to deal with similar issues.  If you have to use QThreads
* to implement asynchronous support, do not be concerned about emitting
* the @ref sayFinished or @ref synthFinished signals from your threads, since
* KTTSD will convert the received signals into postEvents and
* return immediately.
*
* If it is not possible for @ref stopText to stop an in-progress operation, it
* must not wait for the operation to complete, since this would block KTTSD.
* Instead, simply return immediately.  Usually, KTTSD will perform other operations
* while waiting for the plugin to complete its operation.  (See threadedplugin.cpp.)
*
* If the @ref stopText implementation returns before the operation has actually
* completed, it must emit the @ref stopped() signal when it is actually completed.
*
* If a plugin returns True from @ref supportsAsync, and @ref stopText is called,
* when the plugin has stopped or completed the operation, it must return psIdle
* on the next call to @ref getState ; not psFinished.  The following state diagram
* might be helpful to understand this:
*
 @verbatim
          psIdle <<----------------------------------------------------------
         /      \                                                           ^
  psSaying      psSynthing --- stopText called and operation completed -->> ^
         \      /                                                           ^
        psFinished --- ackFinished called ------------------------------->> ^
 @endverbatim
*
* If your plugin can't immediately stop an in-progress operation, the easiest
* way to handle this is to set a flag when stopText is called, and then in your
* getState() implementation, if the operation has completed, change the
* psFinished state to psIdle, if the flag is set.  See the flite plugin for
* example code.
*         
* If a plugin returns True from @ref supportsSynth, KTTSD will pass a suggested
* filename in the @ref synthText call.  The plugin should synthesize the text
* into an audio file with the suggested name.  If the synthesis engine does not
* permit this, i.e., it will pick a filename of its own, that is OK.  In either
* case, the actual filename produced should be returned in @ref getFilename .
* In no case may the plugin re-use this filename once @ref getFilename has been
* called.  If for some reason the synthesis engine cannot support this, the
* plugin should copy the file to the suggested filename.  The file must not be
* locked when @ref getFilename is called.  The file will be deleted when
* KTTSD is finished using it.
*
* The preferred audio file format is wave, since this is the only format
* guaranteed to be supported by KDE (aRts).  Other formats may or may not be
* supported on a user's machine.
*
* The plugin destructor should take care of terminating the speech engine.
*
* @section Error-handling Error Handling
*
* Plugins may emit the @ref error signal when an error occurs.
*
* When an error occurs, plugins should attempt to recover as best they can and
* continue accepting @ref sayText or @ref synthText calls.  For example,
* if a speech engine emits an error in response to certain characters embedded
* in synthesizing text, the plugin should discard the text and
* emit signal @ref error with True as the first argument and the speech
* engine's error message as the second argument.  The plugin should then
* treat the operation as a completed operation, i.e., return psFinished when
* @ref getState is called.
*
* If the speech engine crashes, the plugin should emit signal @ref error with
* True as the first argument and then attempt to restart the speech engine. 
* The plugin will need to implement some protection against an infinite
* restart loop and emit the @ref error signal with False as the first argument
* if this occurs.
*
* If a plugin emits the @ref error signal with False as the first argument,
* KTTSD will no longer call the plugin.
*
* @section PlugInConf
*
* The plugin should implement a configuration dialog where the user can specify
* options specific to the speech engine.  This dialog is displayed in the KDE
* Control Center and also in the KTTS Manager (kttsmgr).  See pluginconf.h.
*
* If the user changes any of the settings while the plugin is created,
* the plugin will be destroyed and re-created.
*/

class QTextCodec;

enum pluginState
{
    psIdle = 0,                  /**< Plugin is not doing anything. */
    psSaying = 1,                /**< Plugin is synthesizing and audibilizing. */
    psSynthing = 2,              /**< Plugin is synthesizing. */
    psFinished = 3               /**< Plugin has finished synthesizing.  Audio file is ready. */
};

class KDE_EXPORT PlugInProc : virtual public QObject{
    Q_OBJECT

    public:
        enum CharacterCodec {
            Local    = 0,
            Latin1   = 1,
            Unicode  = 2,
            UseCodec = 3
        };

        /**
        * Constructor.
        */
        PlugInProc( QObject *parent = 0, const char *name = 0);

        /**
        * Destructor.
        * Plugin must terminate the speech engine.
        */
        virtual ~PlugInProc();

        /**
        * Initialize the speech engine.
        * @param config          Settings object.
        * @param configGroup     Settings Group.
        *
        * Sample code for reading configuration:
        *
          @verbatim
            config->setGroup(configGroup);
            m_fliteExePath = config->readEntry("FliteExePath", "flite");
            kdDebug() << "FliteProc::init: path to flite: " << m_fliteExePath << endl;
            config->setGroup(configGroup);
          @endverbatim
        */
        virtual bool init(KConfig *config, const QString &configGroup);

        /** 
        * Say a text.  Synthesize and audibilize it.
        * @param text                    The text to be spoken.
        *
        * If the plugin supports asynchronous operation, it should return immediately
        * and emit sayFinished signal when synthesis and audibilizing is finished.
        * It must also implement the @ref getState method, which must return
        * psFinished, when saying is completed.
        */
        virtual void sayText(const QString &text);

        /**
        * Synthesize text into an audio file, but do not send to the audio device.
        * @param text                    The text to be synthesized.
        * @param suggestedFilename       Full pathname of file to create.  The plugin
        *                                may ignore this parameter and choose its own
        *                                filename.  KTTSD will query the generated
        *                                filename using getFilename().
        *
        * If the plugin supports asynchronous operation, it should return immediately
        * and emit @ref synthFinished signal when synthesis is completed.
        * It must also implement the @ref getState method, which must return
        * psFinished, when synthesis is completed.
        */
        virtual void synthText(const QString &text, const QString &suggestedFilename);

        /**
        * Get the generated audio filename from call to @ref synthText.
        * @return                        Name of the audio file the plugin generated.
        *                                Null if no such file.
        *
        * The plugin must not re-use or delete the filename.  The file may not
        * be locked when this method is called.  The file will be deleted when
        * KTTSD is finished using it.
        */
        virtual QString getFilename();

        /**
        * Stop current operation (saying or synthesizing text).
        * Important: This function may be called from a thread different from the
        * one that called sayText or synthText.
        * If the plugin cannot stop an in-progress @ref sayText or
        * @ref synthText operation, it must not block waiting for it to complete.
        * Instead, return immediately.
        *
        * If a plugin returns before the operation has actually been stopped,
        * the plugin must emit the @ref stopped signal when the operation has
        * actually stopped.
        *
        * The plugin should change to the psIdle state after stopping the
        * operation.
        */
        virtual void stopText();

        /**
        * Return the current state of the plugin.
        * This function only makes sense in asynchronous mode.
        * @return                        The pluginState of the plugin.
        *
        * @see pluginState
        */
        virtual pluginState getState();

        /**
        * Acknowledges a finished state and resets the plugin state to psIdle.
        *
        * If the plugin is not in state psFinished, nothing happens.
        * The plugin may use this call to do any post-processing cleanup,
        * for example, blanking the stored filename (but do not delete the file).
        * Calling program should call getFilename prior to ackFinished.
        */
        virtual void ackFinished();

        /**
        * Returns True if the plugin supports asynchronous processing,
        * i.e., returns immediately from sayText or synthText.
        * @return                        True if this plugin supports asynchronous processing.
        *
        * If the plugin returns True, it must also implement @ref getState .
        * It must also emit @ref sayFinished or @ref synthFinished signals when
        * saying or synthesis is completed.
        */
        virtual bool supportsAsync();

        /**
        * Returns True if the plugin supports synthText method,
        * i.e., is able to synthesize text to a sound file without
        * audibilizing the text.
        * @return                        True if this plugin supports synthText method.
        *
        * If the plugin returns True, it must also implement the following methods:
        * - @ref synthText
        * - @ref getFilename
        * - @ref ackFinished
        *
        * If the plugin returns True, it need not implement @ref sayText .
        */
        virtual bool supportsSynth();

        /**
        * Returns the name of an XSLT stylesheet that will convert a valid SSML file
        * into a format that can be processed by the synth.  For example,
        * The Festival plugin returns a stylesheet that will convert SSML into
        * SABLE.  Any tags the synth cannot handle should be stripped (leaving
        * their text contents though).  The default stylesheet strips all
        * tags and converts the file to plain text.
        * @return            Name of the XSLT file.
        */
        virtual QString getSsmlXsltFilename();

        /**
        * Given the name of a codec, returns the QTextCodec for the name.
        * Handles the following "special" codec names:
        *   Local               The user's current Locale codec.
        *   Latin1              Latin1 (ISO 8859-1)
        *   Unicode             UTF-16
        * @param codecName      Name of desired codec.
        * @return               The codec object.  Calling program must not delete this object
        *                       as it is a reference to an existing QTextCodec object.
        *
        * Caution: Do not pass translated codec names to this routine.
        */
        static QTextCodec* codecNameToCodec(const QString &codecName);

        /**
        * Builds a list of codec names, suitable for display in a QComboBox.
        * The list includes the 3 special codec names (translated) at the top:
        *   Local               The user's current Locale codec.
        *   Latin1              Latin1 (ISO 8859-1)
        *   Unicode             UTF-16
        */
        static QStringList buildCodecList();

        /**
        * Given the name of a codec, returns index into the codec list.
        * Handles the following "special" codec names:
        *   Local               The user's current Locale codec.
        *   Latin1              Latin1 (ISO 8859-1)
        *   Unicode             UTF-16
        * @param codecName      Name of the codec.
        * @param codecList      List of codec names. The first 3 entries may be translated names.
        * @return               QTextCodec object.  Caller must not delete this object.
        *
        * Caution: Do not pass translated codec names to this routine in codecName parameter.
        */
        static int codecNameToListIndex(const QString &codecName, const QStringList &codecList);

        /**
        * Given index into codec list, returns the codec object.
        * @param codecNum       Index of the codec.
        * @param codecList      List of codec names. The first 3 entries may be translated names.
        * @return               QTextCodec object.  Caller must not delete this object.
        */
        static QTextCodec* codecIndexToCodec(int codecNum, const QStringList &codecList);

        /**
        * Given index into codec list, returns the codec Name.
        * Handles the following "special" codec names:
        *   Local               The user's current Locale codec.
        *   Latin1              Latin1 (ISO 8859-1)
        *   Unicode             UTF-16
        * @param codecNum       Index of the codec.
        * @param codecList      List of codec names. The first 3 entries may be translated names.
        * @return               Untranslated name of the codec.
        */
        static QString codecIndexToCodecName(int codecNum, const QStringList &codecList);

    signals:
        /**
        * Emitted when synthText() finishes and plugin supports asynchronous mode.
        */
        void synthFinished();
        /**
        * Emitted when sayText() finishes and plugin supports asynchronous mode.
        */
        void sayFinished();
        /**
        * Emitted when stopText() has been called and plugin stops asynchronously.
        */
        void stopped();
        /**
        * Emitted if an error occurs.
        * @param keepGoing               True if the plugin can continue processing.
        *                                False if the plugin cannot continue, for example,
        *                                the speech engine could not be started.
        * @param msg                     Error message.
        *
        * When an error occurs, plugins should attempt to recover as best they can
        * and continue accepting @ref sayText or @ref synthText calls.  For example,
        * if the speech engine emits an error while synthesizing text, the plugin
        * should return True along with error message.
        *
        * @see Error-handling
        *
        */
        void error(bool keepGoing, const QString &msg);
};

#endif // _PLUGINPROC_H_
