/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM 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; only version 2 of the License.      
 * 
 * NMM is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 
 * License for more details.  
 * 
 * You should have received a copy of the GNU General Public License 
 * along with NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */


#ifndef NMM_GENERIC_NODE_HPP
#define NMM_GENERIC_NODE_HPP NMM_GENERIC_NODE_HPP

#include "nmm/nmm_prefix.hpp"

#include "QueuedInputJack.hpp"
#include "ThreadedNode.hpp"
#include "Usage.hpp"
#include "JackGroup.hpp"

#include "IBiDirQueuedNodeImpl.hpp"
#include "IInstreamImpl.hpp"
#include "IBufferManagerImpl.hpp"
#include "IFilterNode.hpp"
#include "IFormatChangeImpl.hpp"
#include "ISwitchImpl.hpp"

#include "nmm/comm/messaging/Buffer.hpp"
#include "nmm/comm/messaging/IExternalBufferManager.hpp"

#include "IDelayMeasurementImpl.hpp"

#include "nmm/Exception.hpp"

#include "visibility.hpp"

namespace NMM {

  /** A generic node in an NMM flow graph.
   * This class is the base class of the generic node classes. It provides the complete implementation of the
   * interface of a node, management of streams and jacks, and other common functionality.
   */
  class DLLEXPORT GenericNode
    : public ThreadedNode,
      public IExternalBufferManager,
      public ISignalReceiver,
      public IBiDirQueuedNodeImpl,
      public IInstreamImpl,
      public IBufferManagerImpl,
      public IFormatChangeImpl,
      public ISwitchImpl,
      public IDelayMeasurementImpl,
      public Usage
  {
  private:
    IFilterNode* filter_node;                   ///< Interface to self, if this node is a GenericFilterNode
    int flush_counter;                          ///< Number of doFlush calls which have returned DELETE (-1) or not DELETE (+1)
    volatile bool working_flag;                 ///< Working flag (If set, processBuffer is called repeatedly with a null input buffer)
    volatile bool producing_flag;               ///< Producing flag (deprecated; not used)
    CEvent* current_event;                      ///< Current instream event (for deferred events)
    volatile unsigned int event_working_index;  ///< Index of the current instream event (deprecated; not used)
    unsigned int pre_fetch_queue_size;          ///< Number of messages to prefetch if prefetching is enabled
    Interval pre_fetch_queue_duration;          ///< Media time to prefetch if prefetching is enabled

    /** Internal representation of an input stream.
     * This structure contains all information about in input stream of a node.
     */
    class InputStream_t {
    public:
      QueuedInputJack *jack; ///< Input jack, if one was requested, otherwise 0
      const Format *format;  ///< Connection format, if stream is connected, otherwise 0
      bool enabled;          ///< Flag which indicates whether downstream messages from this stream are processed
      bool needed;           ///< Flag which indicates that a stream is not removed during removeUnneededStreams()

      /** Create a new input stream.
       *
       * @post this->jack == 0
       * @post this->format == 0
       * @post this->enabled = true
       * @post this->needed == true
       */
      InputStream_t() : jack(0), format(0), enabled(true), needed(true) {};

      /** Destructor.
       * This destructor destroys the jack and the format object.
       */
      ~InputStream_t() { if(jack) delete jack; if(format) delete format; };
    };

    /** Internal representation of an output stream.
     * This structure contains all information about in output stream of a node.
     */
    class OutputStream_t {
    public:
      JackGroup *jack_group; ///< Output jack group, if a jack was requested, otherwise 0
      const Format *format;  ///< Connection format, if stream is connected, otherwise 0
      bool enabled;          ///< Flag which indicates whether upstream messages from this stream are processed
      bool needed;           ///< Flag which indicates that a stream is not removed during removeUnneededStreams()

      /** Create a new input stream.
       *
       * @post this->jack_group == 0
       * @post this->format == 0
       * @post this->enabled = true
       * @post this->needed == true
      */
      OutputStream_t() : jack_group(0), format(0), enabled(true), needed(true) {};

      /** Destructor.
       * This destructor destroys the jack group and the format object.
       */
      ~OutputStream_t() { if(jack_group) delete jack_group; if(format) delete format; };
    };

    /** String comparison functional.
     * This functional implements a less-than operator for character arrays.
     * It is used with certain STL algorithms.
     * This functional performs a lexicographic, case-sensitive string comparison.
     */
    struct ltstr {
      /** Invocation operator.
       *
       * @param s1 Left operand
       * @param s2 Right operand
       * @return @c true if s1 is less than s2, otherwise false
       */
      bool operator()(const char *s1, const char *s2) const {
        return strcmp(s1, s2) < 0;
      }
    };

    int queue_size;               ///< Initial maximum number of messages in all input queues (set on construction)
    StreamQueue::Mode queue_mode; ///< Initial queue mode for all input queues (set on construction)

    /** Input stream map.
     * This map is a database of input streams of a node, indexed by the jack tags of the streams.
     */
    typedef map<const char*, InputStream_t*, ltstr> InputStreamMap_t;
    mutable InputStreamMap_t input_streams;   ///< All input streams

    /** Output stream map.
     * This map is a database of input streams of a node, indexed by the jack tags of the streams.
     */
    typedef map<const char*, OutputStream_t*, ltstr> OutputStreamMap_t;
    mutable OutputStreamMap_t output_streams; ///< All output streams

    int downstr_blocked_queue_count;  ///< Number of downstream connections which are blocked
    bool output_queue_not_empty;      ///< Flag which indicates that upstream messages are in the input queue

    int upstr_blocked_queue_count;    ///< Number of upstream connections which are blocked
    bool input_queue_not_empty;       ///< Flag which indicates that downstream messages are in the input queue

    InputStreamMap_t::iterator curr_recv_in_stream;   ///< Input stream from which current mesasge was received
    OutputStreamMap_t::iterator curr_recv_out_stream; ///< Output stream from which current message was received

    /** Get the next upstream message to process.
     * This internal method examines all input queues in the upstream direction, removes a single message
     * from one of these queues and returns it.
     *
     * @return The next upstream message to process
     */
    Message *getNextUpstrMessage();

    /** Get the next downstream message to process.
     * This internal method examines all input queues in the downstream direction, removes a single message
     * from one of these queues and returns it.
     *
     * @return The next downstream message to process
     */
    Message *getNextDownstrMessage();

    /** Send a message.
     * This method is used for sending to the correct output stream or streams.
     * An Upstream messages is sent to one or more input streams,
     * and a downstream message is sent to one or more output streams.
     * A composite event or flush event is always sent to all input streams or all output streams.
     * A buffer is sent to a single, specific input or output stream if a current input or output stream
     * for sending is set, or to all input streams or all output streams if no current input or
     * output stream is set.
     *
     * @param message The message to send
     * @return Queue state of the connection (SUCCESS, FAILURE, BLOCKED)
     */
    virtual Result sendMessage(Message *message);

    JackGroup* curr_send_out_stream;    ///< Current output stream where downstream buffers are sent to (see sendMessage())
    InputJack* curr_send_in_stream;     ///< Current input stream where upstream buffers are sent to (see sendMessage())

    queue<int> signal_queue;         ///< Signal queue for asynchronous signal handling
    ThreadMutex signal_queue_mutex;  ///< Mutex for synchronizing access to the signal queue
    ThreadCondition signal_queue_cv; ///< Condition variable for waiting for signals

    bool internal_started_state;         ///< A START signal has been received and the node is running
    ThreadMutex internal_started_mutex;  ///< Mutex for synchronizing access to internal_started_state
    ThreadCondition internal_started_cv; ///< Condition variable for waiting for internal_started_state 

    Message::Direction current_msg_direction; ///< Direction of the current instream message

    ThreadMutex __internal_statechange_mutex; ///< Mutex for protecting state changes from block()/unblock()

  protected://Interface methods
    // ISwitch interface
    /** Perform switch operation.
     * This instream event is sent when outputs are switched. Plug-ins that receive this event and
     * implement this interface may perform whatever operation is required when switching outputs.
     *
     * @deprecated Output switching is deprecated
     */
    virtual void switchOutputs();

    /** Flush internal output queue.
     * This method flushes the internal output queue which is used for output switching.
     *
     * @param ts Flush timestamp
     *
     * @deprecated Output switching is deprecated
     */
    virtual void flushOutputs(const Timestamp& ts);

    /** Enable or disable output switching.
     * This method enables or disables special handling for output switching.
     * If enabled, the node sends a switchOutputs event whenever a new output jack is connected.
     *
     * @param enable @c true to enable, @c false to disable output switching
     *
     * @deprecated Output switching is deprecated
     */
    virtual void setSwitchOutputsEnabled(const nmm_bool& enable);

    /** Check if output switching is enabled.
     *
     * @return @c true if output switching is enabled, @c false if it is disabled
     *
     * @deprecated Output switching is deprecated
     */
    virtual nmm_bool getSwitchOutputsEnabled();

    bool perform_switch;                      ///< Switch operation should be performed
    bool perform_flush;                       ///< Flush operation should be performed
    Timestamp perform_flush_ts;               ///< Timestamp for the flush operation
    bool switch_enabled;                      ///< Output switching is enabled (see ISwitch::setSwitchOutputsEnabled)
    ThreadMutex internal_message_queue_mutex; ///< Mutex for protecting the internal message queue for output switching

    /** An entry in the internal message queue for output switching.
     */
    class InternalMessage_t {
    public:
      Message* switch_to_msg;         ///< ?
      Message* switch_from_msg;       ///< ?
      bool reset_switch_to;           ///< ?

      /** Create a new internal message queue entry.
       *
       * @post this->switch_to_msg == 0
       * @post this->switch_from_msg == 0
       * @post this->reset_switch_to == false
       */
      InternalMessage_t() : switch_to_msg(0), switch_from_msg(0), reset_switch_to(false) { };

      /** Create a new internal message queue entry.
       *
       * @param to_msg ?
       * @param from_msg ?
       * @param reset ?
       * @post this->switch_to_msg == to_msg
       * @post this->switch_from_msg == from_msg
       * @post this->reset_switch_to == reset
       */
      InternalMessage_t(Message* to_msg, Message* from_msg, bool reset) 
	: switch_to_msg(to_msg), switch_from_msg(from_msg), reset_switch_to(reset) { };
    };

    /** Type for internal message queue for output switching.
     */
    typedef queue<InternalMessage_t> InternalMessageQueue_t;
    InternalMessageQueue_t internal_message_queue;  ///< Internal message queue for output switching

    // IFormatChange interface
    /** The input format has changed (instream event).
     * This instream event notifies the receiver that the input format has changed at the
     * input jack from which the event was received.
     * @param format The new input format
     * @return Result of the instream event
     */
    virtual Result setFormat(const Format& format);
    
    /** Send a message.
     * This method is used for sending to the correct output stream or streams.
     * An Upstream messages is sent to one or more input streams,
     * and a downstream message is sent to one or more output streams.
     * A composite event or flush event is always sent to all input streams or all output streams.
     * A buffer is sent to a single, specific input or output stream if a current input or output stream
     * for sending is set, or to all input streams or all output streams if no current input or
     * output stream is set.
     *
     * @param message The message to send
     * @return Queue state of the connection (SUCCESS, FAILURE, BLOCKED)
     *
     * @deprecated This method provides plug-in classes access to an internal method and should not be used
     */
    inline Result protectedSendMessage(Message* message) { return sendMessage(message); };

    // IBufferManager interface
    /** Get the buffer manager of this node.
     *
     * This method returns the NMM reference of the buffer manager of this node.
     *
     * @return The NMM reference of the buffer manager of this node
     */
    virtual NMMReference getBufferManager();

    /** Set the buffer manager to use by this node.
     *
     * This method configures this node to use the given buffer manager.
     * The given buffer manager must belong to a node which is local to this node,
     * otherwise this method will fail.
     *
     * @param manager Interface to the buffer manager to use
     * @return @c SUCCESS if the method succeeded, otherwise @c FAILURE
     */
    virtual Result useBufferManager(const IBufferManager& manager);

    /** Release the buffer manager used by this node.
     */
    virtual void releaseBufferManager();

    // INode interface
    /** Request an input jack.
     * This method requests an input jack with the given format and jack tag
     * and returns an interface to the requested jack.
     * If the request fails, a null pointer is returned.
     * The caller is responsible for deleting the jack interface.
     *
     * This method may only be called in state INITIALIZED.
     *
     * This is the actual implementation of this method.
     *
     * @param format The format of the jack
     * @param jack_tag The name of an input stream
     * @return An interface to the requested jack or 0
     */
    virtual IInputJack*   getInputJack(const NMM::Format& format, const string& jack_tag);

    /** Get an interface to a connected input jack.
     * This method returns an interface to an existing and connected input jack with the
     * given jack tag. If no such jack exists, a null pointer is returned.
     * The caller is responsible for deleting the jack interface.
     *
     * This is the actual implementation of this method.
     *
     * @param jack_tag The name of an input stream
     * @return An interface to the existing and connected jack or 0
     */
    virtual IInputJack*   getInputStream(const string& jack_tag);

    /** Request an output jack.
     * This method requests an output jack with the given format and jack tag
     * and returns an interface to the requested jack.
     * If the request fails, a null pointer is returned.
     * The caller is responsible for deleting the jack interface.
     *
     * This method may only be called in state OUTPUT_INITIALIZED.
     *
     * This is the actual implementation of this method.
     *
     * @param format The format of the jack
     * @param jack_tag The name of an output stream
     * @return An interface to the requested jack or 0
     */
    virtual IOutputJack*  getOutputJack(const NMM::Format& format, const string& jack_tag);

    /** Request an output jack.
     * This method requests an output jack with the given format and jack tag
     * and returns an interface to the requested jack.
     * If the request fails, a null pointer is returned.
     * The caller is responsible for deleting the jack interface.
     *
     * Unlike getOutputJack, this method may only be called in state INITIALIZED.
     * This method bypasses format negotiation and should only be used for debugging.
     *
     * This is the actual implementation of this method.
     *
     * @param format The format of the jack
     * @param jack_tag The name of an output stream
     * @return An interface to the requested jack or 0
     */
    virtual IOutputJack*  getUnintializedOutputJack(const Format& format, const string& jack_tag);

    /** Get an interface to a connected output jack.
     * This method returns an interface to an existing and connected output jack with the
     * given jack tag. If no such jack exists, a null pointer is returned.
     * The caller is responsible for deleting the jack interface.
     *
     * This is the actual implementation of this method.
     *
     * @param jack_tag The name of an output stream
     * @return An interface to the existing and connected jack or 0
     */
    virtual IOutputJack*  getOutputStream(const string& jack_tag);

    /** Get the format of an input stream.
     * This method returns the connection format of the given stream if the stream is connected.
     * If the stream is not connected or does not exist, a null pointer is returned.
     * The caller is responsible for deleting the returned Format object.
     *
     * @param jack_tag The name of an input stream
     * @returns A pointer to a Format object describing the connection format or 0
     */
    virtual Format* getInputStreamFormat(const string& jack_tag = "default");

    /** Get the format of an output stream.
     * This method returns the connection format of the given stream if the stream is connected.
     * If the stream is not connected or does not exist, a null pointer is returned.
     * The caller is responsible for deleting the returned Format object.
     *
     * @param jack_tag The name of an output stream
     * @returns A pointer to a Format object describing the connection format or 0
     */
    virtual Format* getOutputStreamFormat(const string& jack_tag = "default");

    // IBiDirQueued
    /** Get the current size of a downstream queue.
     * This method returns the number of buffers currently queued in a downstream queue.
     *
     * @param id Input stream ID which identifies the queue
     * @return Current size of the queue (in number of buffers)
     */
    virtual nmm_uint32 getDownstreamSize(const string& id = "default") const;

    /** Get the maximum size of a downstream queue.
     * This method returns the maximum number of buffers that may be queued in a downstream queue.
     *
     * @param id Input stream ID which identifies the queue
     * @return Maximum size of the queue (in number of buffers)
     */
    virtual nmm_uint32 getDownstreamMaxSize(const string& id = "default") const;

    /** Get the mode of a downstream queue.
     * This method returns the queue mode of a downstream queue.
     *
     * @param id Input stream ID which identifies the queue
     * @return Queue mode
     */
    virtual IQueued::Mode getDownstreamMode(const string& id = "default") const;

    /** Set the maximum size of a downstream queue.
     * This method sets the maximum number of buffers that may be queued in a downstream queue.
     *
     * @param size Maximum size of the queue (in number of buffers)
     * @param id Input stream ID which identifies the queue
     */
    virtual Result setDownstreamMaxSize(const nmm_uint32& size, const string& id = "default"); /* limited(Node::CONSTRUCTED) */

    /** Set the maximum duration of a downstream queue.
     *
     * @param duration Maximum duration of the queue
     * @param id Input stream ID which identifies the queue
     */
    virtual Result setDownstreamMaxDuration(const Interval& duration, const string& id = "default"); /* limited(Node::CONSTRUCTED) */

    /** Set the mode of a downstream queue.
     * This method sets the queue mode of a downstream queue.
     *
     * @param mode Queue mode
     * @param id Input stream ID which identifies the queue
     */
    virtual Result setDownstreamMode(const IQueued::Mode& mode, const string& id = "default"); /* limited(Node::CONSTRUCTED) */

    /** Instream event set the downstream size to unlimited.
     *
     * @return SUCCESS
     */
    virtual Result unlimitDownstreamSize() /* limited(INode::STARTED) */ /* in-stream */;

    /** Instream event set the downstream size back to limited.
     *
     * @return SUCCESS
     */
    virtual Result limitDownstreamSize() /* limited(INode::STARTED) */ /* in-stream */ ;

    /** Get the current size of an upstream queue.
     * This method returns the number of buffers currently queued in an upstream queue.
     *
     * @param id Output stream ID which identifies the queue
     * @return Current size of the queue (in number of buffers)
     */
    virtual nmm_uint32 getUpstreamSize(const string& id = "default") const;

    /** Get the maximum size of an upstream queue.
     * This method returns the maximum number of buffers that may be queued in an upstream queue.
     *
     * @param id Output stream ID which identifies the queue
     * @return Maximum size of the queue (in number of buffers)
     */
    virtual nmm_uint32 getUpstreamMaxSize(const string& id = "default") const;

    /** Get the mode of an upstream queue.
     * This method returns the queue mode of an upstream queue.
     *
     * @param id Output stream ID which identifies the queue
     * @return Queue mode
     */
    virtual IQueued::Mode getUpstreamMode(const string& id = "default") const;

    /** Set the maximum size of an upstream queue.
     * This method sets the maximum number of buffers that may be queued in an upstream queue.
     *
     * @param size Maximum size of the queue (in number of buffers)
     * @param id Output stream ID which identifies the queue
     */
    virtual Result setUpstreamMaxSize(const nmm_uint32& size, const string& id = "default"); /* limited(Node::CONSTRUCTED) */

    /** Set the mode of an upstream queue.
     * This method sets the queue mode of an upstream queue.
     *
     * @param mode Queue mode
     * @param id Output stream ID which identifies the queue
     */
    virtual Result setUpstreamMode(const IQueued::Mode& mode, const string& id = "default"); /* limited(Node::CONSTRUCTED) */

    /** Set the maximum size of a downstream queue.
     * This method sets the maximum number of buffers that may be queued in a downstream queue.
     *
     * @param size Maximum size of the queue (in number of buffers)
     * @param id Input stream ID which identifies the queue
     */
    virtual Result setMaxSize(const nmm_uint32& size, const string& id = "default");

    /** Set the mode of a downstream queue.
     * This method sets the queue mode of a downstream queue.
     *
     * @param mode Queue mode
     * @param id Input stream ID which identifies the queue
     */
    virtual Result setMode(const IQueued::Mode& mode, const string& id = "default");

  protected:
    /** Handle an instream format change.
     * This method is invoked whenever the connection format of an input stream changes.
     * Subclasses may reimplement this method to implement special handling.
     *
     * The default implementation does nothing and always succeeds.
     * Subclasses are free whether or not to call the base class implementation.
     *
     * @param format The new input format
     * @return @c SUCCESS if the format change succeeded, @c FAILURE otherwise
     */
    virtual Result doSetFormat(const Format& format);

    /** Dequeue a signal.
     * This method remoevs a signal from the signal queue and returns it.
     *
     * @return The dequeued signal
     */
    int dequeueSignal();

    /** Check if signal queue is empty.
     *
     * @return @c true if the signal queue is empty, @c false otherwise
     */
    bool isSignalQueueEmpty();

    /** Wait for a signal.
     * This method waits until the signal queue is non-empty.
     *
     * @return SUCCESS
     */
    Result waitForSignal();

    /** Insert an event.
     * This method inserts an event into the current composite event
     * behind the current position.
     * Event handlers can call this method to inject new instream events into the stream.
     *
     * @param e The event to insert
     *
     * @deprecated his method is not fully implemented, so its use is discouraged
     */
    void insertEvent(const Event& e);

    /** Insert an event.
     * This method inserts an event into the current composite event
     * behind the current position.
     * Event handlers can call this method to inject new instream events into the stream.
     *
     * @param e The event to insert
     *
     * @deprecated his method is not fully implemented, so its use is discouraged
     */
    void insertEvent(Event* e);

    /** Get the direction of the current message.
     *
     * @return The direction of the current message
     */
    inline Message::Direction getCurrentMsgDirection() const { return current_msg_direction; }

    /** Handle processing of a composite event.
     * This method is invoked for every composite event which is processed by this node.
     * It can be reimplemented by plug-ins to provide special handling for instream events.
     *
     * The default implementation does nothing and returns SUCCESS.
     * Subclasses are free whether or not to call the base class implementation.
     *
     * @param ce The composite event to process
     * @return Event result (SUCCESS, FAILURE, DELETE, DEFER)
     */
    virtual Result processCEvent(CEvent* ce);

    /** Set the working flag.
     * This method sets the working flag of this node to @c true or @c false.
     * If the working flag is @c true, then processBuffer() is called repeatedly with a
     * null pointer as input buffer argument, and no new input buffers are processed.
     * If the working flag is @c false, then processBuffer() is called only with non-null
     * pointers as input buffer argument, and only if a new buffer is available for processing.
     *
     * This method can be called from within processBuffer(), instream event handlers
     * and state transition handlers. It should not be called from out-of-band event handlers.
     *
     * @param working The value to set for the working flag
     */
    void setWorkingFlag(const bool working);

    /** Set the producing flag.
     * This method sets the producing flag of this node to @c true or @c false.
     * If the producing flag is @c true, then processBuffer() is called repeatedly with a
     * null pointer as input buffer argument, and no new input buffers are processed.
     * If the working flag is @c false, then processBuffer() is called only with non-null
     * pointers as input buffer argument, and only if a new buffer is available for processing.
     *
     * This method can be called from within processBuffer(), instream event handlers
     * and state transition handlers. It should not be called from out-of-band event handlers.
     *
     * @param working The value to set for the producing flag
     */
    void setProducingFlag(const bool working);

    /** Add an input stream.
     * This method adds an input stream with the given jack tag to this node.
     * Plug-ins use this method in doInit() to create new streams in addition to the default ones.
     *
     * @param jack_tag Name of the input stream
     * @return @c SUCCESS if the stream was successfully added, @c FAILURE otherwise
     */
    Result addInputStream(const char *jack_tag);

    /** Add an output stream.
     * This method adds an output stream with the given jack tag to this node.
     * Plug-ins use this method in doInitOutput() to create new streams in addition to the default ones.
     *
     * @param jack_tag Name of the output stream
     * @return @c SUCCESS if the stream was successfully added, @c FAILURE otherwise
     */
    Result addOutputStream(const char *jack_tag);

    /** Remove an input stream.
     * This method removes the input stream with the given jack tag from this node.
     * Plug-ins use this method in doDeinit() to remove the streams created in doInit().
     *
     * This method schedules a stream for removal. The stream is not removed immediately.
     * Adding the stream again cancels the removal.
     *
     * @param jack_tag Name of the input stream
     * @return @c SUCCESS if the stream was successfully removed, @c FAILURE otherwise
     */
    Result removeInputStream(const char *jack_tag);

    /** Remove all unneeded input streams.
     * This method removes all input streams which have been scheduled for removal using removeInputStream().
     * Plug-ins do not have to call this method explicitly.
     */
    void removeUnneededInputStreams();

    /** Remove an output stream.
     * This method removes the output stream with the given jack tag from this node.
     * Plug-ins use this method in doDeinitOutput() to remove the streams created in doInitOutput().
     *
     * This method schedules a stream for removal. The stream is not removed immediately.
     * Adding the stream again cancels the removal.
     *
     * @param jack_tag Name of the output stream
     * @return @c SUCCESS if the stream was successfully removed, @c FAILURE otherwise
     */
    Result removeOutputStream(const char *jack_tag);

    /** Remove all unneeded output streams.
     * This method removes all output streams which have been scheduled for removal using removeOutputStream().
     * Plug-ins do not have to call this method explicitly.
     */
    void removeUnneededOutputStreams();

    /** Get current receiving input stream.
     * This method returns the jack tag of the input stream from which the current message was received.
     * The result is valid only if called from processBuffer() or an instream event handler.
     * Then the current message is the buffer being processed or the CEvent which contained the instream
     * event being handled.
     *
     * To determine whether to use getCurrentRecvInputStream() or getCurrentRecvOutputStream(), the
     * getCurrentMsgDirection() method can be used. Example:
     * @code
     * string jack_tag;
     * if(getCurrentMsgDirection() == Message::DOWNSTREAM) {
     *   jack_tag = getCurrentRecvInputStream();
     * }
     * else {
     *   jack_tag = getCurrentRecvOutputStream();
     * }
     * cerr << "The message was received from " << jack_tag << endl;
     * @endcode
     *
     * @return The jack tag of the stream from which the message was received
     */
    const char* getCurrentRecvInputStream() const;

    /** Get current receiving output stream.
     * This method returns the jack tag of the output stream from which the current message was received.
     * The result is valid only if called from processBuffer() or an instream event handler.
     * Then the current message is the buffer being processed or the CEvent which contained the instream
     * event being handled.
     *
     * To determine whether to use getCurrentRecvInputStream() or getCurrentRecvOutputStream(), the
     * getCurrentMsgDirection() method can be used. Example:
     * @code
     * string jack_tag;
     * if(getCurrentMsgDirection() == Message::DOWNSTREAM) {
     *   jack_tag = getCurrentRecvInputStream();
     * }
     * else {
     *   jack_tag = getCurrentRecvOutputStream();
     * }
     * cerr << "The message was received from " << jack_tag << endl;
     * @endcode
     *
     * @return The jack tag of the stream from which the message was received
     */
    const char* getCurrentRecvOutputStream() const;

    /** Enable or disable receiving input stream.
     * This method enables or disables receiving messages on an input stream. If receiving messages is
     * disabled on a stream, no composite events or buffers queued at that stream will be processed by
     * the node until receiving messages is enabled again. In this way, the node can control which
     * streams to service. It is legal to disable receiving messages on all streams simultaneously.
     * This is the preferred way of handling a situation where the node is idle and can not process messages.
     * Another use of this method is to implement nodes which reorder messages in the stream.
     *
     * If not stream with the given jack tag exists, then this method fails.
     *
     * This method may be called by plug-ins at any time. However, it is not part of the INode interface.
     * To make this interface available to applications, a plug-in must provide additional interface methods.
     *
     * @param jack_tag Name of an input stream
     * @param enable @c true to enable, @c false to disable receiving messages on the stream
     * @return @c SUCCESS if the method succeeded, @c FAILURE otherwise
     */
    Result setRecvInputStreamEnabled(const char *jack_tag, bool enable);

    /** Check if receiving input stream is enabled.
     * This method returns a flag which indicates if receiving messages is enabled on the input stream
     * with the given jack tag. If no such jack exists, this method returns @c false.
     * For more information about enabling and disabling receiving messages, see setRecvInputStreamEnabled().
     *
     * @param jack_tag Name of an input stream
     * @return @c true if receiving messages in enabled on the stream, @c false otherwise
     */
    bool getRecvInputStreamEnabled(const char *jack_tag) const;

    /** Enable or disable receiving output stream.
     * This method enables or disables receiving messages on an output stream. If receiving messages is
     * disabled on a stream, no composite events or buffers queued at that stream will be processed by
     * the node until receiving messages is enabled again. In this way, the node can control which
     * streams to service. It is legal to disable receiving messages on all streams simultaneously.
     * This is the preferred way of handling a situation where the node is idle and can not process messages.
     * Another use of this method is to implement nodes which reorder messages in the stream.
     *
     * If not stream with the given jack tag exists, then this method fails.
     *
     * This method may be called by plug-ins at any time. However, it is not part of the INode interface.
     * To make this interface available to applications, a plug-in must provide additional interface methods.
     *
     * @param jack_tag Name of an output stream
     * @param enable @c true to enable, @c false to disable receiving messages on the stream
     * @return @c SUCCESS if the method succeeded, @c FAILURE otherwise
     */
    Result setRecvOutputStreamEnabled(const char *jack_tag, bool enable);

    /** Check if receiving output stream is enabled.
     * This method returns a flag which indicates if receiving messages is enabled on the output stream
     * with the given jack tag. If no such jack exists, this method returns @c false.
     * For more information about enabling and disabling receiving messages, see setRecvOutputStreamEnabled().
     *
     * @param jack_tag Name of an output stream
     * @return @c true if receiving messages in enabled on the stream, @c false otherwise
     */
    bool getRecvOutputStreamEnabled(const char *jack_tag) const;

    /** Set the current sending output stream.
     * This method sets the output stream which is used for sending messages.
     * If no output stream is set for sending messages, then a downstream message is always sent to all output streams.
     * Otherwise, downstream buffers are sent only to the currently set output streams, and downstream composite events
     * are sent to all output streams.
     *
     * Plug-ins can call this method in processBuffer() to control where the buffer returned by processBuffer()
     * will be sent to, if it is a downstream buffer.
     *
     * If no output stream with the given jack tag exists, then this method fails.
     * To set no stream as current sending output stream, a null pointer must be given as jack tag.
     *
     * @param jack_tag Name of an output stream or 0
     * @return @c SUCCESS if the method succeeded, @c FAILURE otherwise
     */
    Result setCurrentSendOutputStream(const char *jack_tag);

    /** Get the current sending output stream.
     * This method returns the jack tag of the stream which is set as the output stream where downstream messages are
     * sent to. See setCurrentSendOutputStream() for details.
     * If no stream has been set, this method returns a null pointer.
     *
     * @return Jack tag of the current sending output stream or 0
     */
    const char* getCurrentSendOutputStream() const;

    /** Set the current sending input stream.
     * This method sets the input stream which is used for sending messages.
     * If no input stream is set for sending messages, then an upstream message is always sent to all input streams.
     * Otherwise, upstream buffers are sent only to the currently set input streams, and upstream composite events
     * are sent to all input streams.
     *
     * Plug-ins can call this method in processBuffer() to control where the buffer returned by processBuffer()
     * will be sent to, if it is an upstream buffer.
     *
     * If no output stream with the given jack tag exists, then this method fails.
     * To set no stream as current sending input stream, a null pointer must be given as jack tag.
     *
     * @param jack_tag Name of an output stream or 0
     * @return @c SUCCESS if the method succeeded, @c FAILURE otherwise
     */
    Result setCurrentSendInputStream(const char *jack_tag); 

    /** Get the current sending input stream.
     * This method returns the jack tag of the stream which is set as the input stream where downstream messages are
     * sent to. See setCurrentSendInputStream() for details.
     * If no stream has been set, this method returns a null pointer.
     *
     * @return Jack tag of the current sending input stream or 0
     */
    const char* getCurrentSendInputStream() const;

    /** Process a buffer.
     * This method is called when a buffer has been dequeued from an input queue and has to be processed by the node.
     * Plug-ins must implement this method, as it usually contains the core functionality of the node.
     *
     * Buffers are dequeued from the queues at the input and output streams of a node whenever buffers are available
     * in the queues. If a node has multiple input (or output) streams, then the queues of all input (or output)
     * streams are checked in a round-robin fashion. It is possible to disable receiving messages from a certain stream
     * using setRecvInputStreamEnabled() and setRecvOutputStreamEnabled().
     *
     * If the working flag of the node is set using setWorkingFlag(), then no buffers are dequeued, and this method is
     * called repeatedly with a null pointer as the input buffer.
     * If the producing flag of the node is set using setProducingFlag(), and the node is a source node, then this
     * method is called repeatedly with a null pointer as the input buffer whenever there are no messages waiting at
     * any queues.
     *
     * This method may return a null pointer or a pointer to a message object (a buffer or composite event). The returned
     * message may be the same object as the input buffer, a new message or a message received in a previous call to
     * processBuffer() and stored in an intermediate storage in the node.
     * If a message is returned, it will be sent to all output streams, if its direction is @c DOWNSTREAM, and to all
     * input streams, if its direction is @c UPSTREAM. This behaviour can be changed using setCurrentSendOutputStream()
     * and setCurrentSendInputStream().
     *
     * @param buffer The input buffer, if any, or 0
     * @return The returned message, if any, or 0
     */
    virtual Message* processBuffer(Buffer* buffer) = 0;

    /** Get all input stream tags.
     * This method returns a list of the jack tags of all input streams of the node.
     *
     * @return List of input streams as jack tags
     */
    virtual list<string >  getInputStreamTags() const;

    /** Get all output stream tags.
     * This method returns a list of the jack tags of all output streams of the node.
     *
     * @return List of output streams as jack tags
     */
    virtual list<string >  getOutputStreamTags() const;

    /** Clone an output jack.
     * This method requests a clone of an existing and connected output jack with the
     * given jack tag.
     * It returns an interface to the cloned jack and the connection format.
     * The cloned jack is unconnected.
     * If no matching jack exists, a null pointer is returned.
     * The caller is responsible for deleting the jack interface and the returned
     * format object.
     *
     * This method is used for connecting an output jack to multiple input jacks.
     *
     * @param jack_tag The name of an output stream
     * @param intersec_format Returns a pointer to a Format object describing the connection format or 0
     * @return An interface to the cloned jack or 0
     */
    virtual IOutputJack* cloneOutputJack(Format*& intersec_format, const string& jack_tag = "default");

    /** Set the output format of a stream.
     * This method changes the output format of the stream with the given jack tag to the given format.
     * The stream must exist, and the format must be supported by the stream (i.e. contained in the working
     * output property). Otherwise, this method fails and an exception is thrown.
     *
     * This method may only be called in states OUTPUT_INITIALIZED, ACTIVATED and STARTED.
     * It is used for instream format changes.
     *
     * @param format The format to set
     * @param jack_tag Jack tag of an output stream
     * @exception Exception if this method fails
     */
    virtual void setOutputFormat(const Format& format, const string& jack_tag = "default")
      throw (const Exception&);

  public:
    /** Create a new generic node.
     *
     * @param name Name of the node
     * @param queue_mode Initial queue mode for all input queues
     * @param size Initial maximum queue size for all input queues
     */
    GenericNode(const char *name = "GenericNode",
                const StreamQueue::Mode queue_mode = StreamQueue::DEFAULT_MODE,
                const int size = StreamQueue::DEFAULT_SIZE);

    /** Virtual destructor.
     */
    ~GenericNode();

    /** Set prefetch queue size and enable prefetching.
     * This method enables prefetching for the node. It may only be called in state CONSTRUCTED.
     * If prefetching is enabled using this method, the node will not start processing data
     * before at least the given number of messages have been queued in its input queue.
     *
     * @param pre_fetch_queue_size Number of messages to prefetch
     */
    void setPrefetchQueueSize(const unsigned int& pre_fetch_queue_size);

    /** Set prefetch queue duration and enable prefetching.
     * This method enables prefetching for the node. It may only be called in state CONSTRUCTED.
     * If prefetching is enabled using this method, the node will not start processing data
     * before messages with at least the given total duration have been queued in its input queue.
     *
     * @param pre_fetch_queue_duration Total duration to prefetch
     */
    void setPrefetchQueueDuration(const Interval& pre_fetch_queue_duration);

    /** Get the prefetch queue size.
     * This method gets the number of messages to be prefetched if prefetching was enabled
     * with setPrefetchQueueSize. Otherwise, it returns zero.
     *
     * @return Number of messages to prefetch or 0
     */
    nmm_uint32 getPrefetchQueueSize();

    /** Wait until prefetching is finished.
     * This method waits until the process of prefetching is finished, i.e. the
     * number of messages in the input queue or their total duration has reached
     * the value specified by setPrefetchQueueSize or setPrefetchQueueDuration.
     * This method may only to be called in state ACTIVATED.
     */
    void waitPrefetchFinished();

    /** Perform state transition from CONSTRUCTED to INITIALIZED.
     * This method changes the state and invokes the doInit method of the node.
     * The state transition fails if doInit fails or a required interface method for this
     * state transition has not been called successfully.
     *
     * @exception StateTransitionFailedException if doInit has failed
     * @exception UnsuccessfulRequiredCallException if a required interface method was not called successfully
     */
    void init()
      throw (const StateTransitionFailedException&, const UnsuccessfulRequiredCallException&);

    /** Perform state transition from INITIALIZED to OUTPUT_INITIALIZED.
     * This method changes the state and invokes the doInitOutput method of the node.
     * The state transition fails if doInitOutput fails or a required interface method for this
     * state transition has not been called successfully.
     *
     * @exception StateTransitionFailedException if doInitOutput has failed
     * @exception UnsuccessfulRequiredCallException if a required interface method was not called successfully
     */
    void initOutput()
      throw (const StateTransitionFailedException&, const UnsuccessfulRequiredCallException&);

    /** Perform state transition from ACTIVATED to STARTED.
     * This method changes the state and invokes the doStart method of the node.
     * The state transition fails if doStart fails or a required interface method for this
     * state transition has not been called successfully.
     *
     * @exception StateTransitionFailedException if doStart has failed
     * @exception UnsuccessfulRequiredCallException if a required interface method was not called successfully
     */
    void start()
      throw (const StateTransitionFailedException&, const UnsuccessfulRequiredCallException&);

    /** Perform state transition from STARTED to ACTIVATED.
     * This method changes the state and invokes the doStop method of the node.
     * The state transition fails if doStop fails or a required interface method for this
     * state transition has not been called successfully.
     *
     * @exception StateTransitionFailedException if doStop has failed
     * @exception UnsuccessfulRequiredCallException if a required interface method was not called successfully
     */
    void stop()
      throw (const StateTransitionFailedException&, const UnsuccessfulRequiredCallException&);

    /** Perform state transition from OUTPUT_INITIALIZED to INITIALIZED.
     * This method changes the state and invokes the doDeinitOutput method of the node.
     * The state transition fails if doDeinitOutput fails or a required interface method for this
     * state transition has not been called successfully.
     *
     * @exception StateTransitionFailedException if doDeinitOutput has failed
     * @exception UnsuccessfulRequiredCallException if a required interface method was not called successfully
     */
    void deinitOutput()
      throw (const StateTransitionFailedException&, const UnsuccessfulRequiredCallException&);

    /** Perform state transition from OUTPUT_INITIALIZED to ACTIVATED.
     * This method changes the state and invokes the doActivate method of the node.
     * The state transition fails if doActivate fails or a required interface method for this
     * state transition has not been called successfully.
     *
     * @exception StateTransitionFailedException if doActivate has failed
     * @exception UnsuccessfulRequiredCallException if a required interface method was not called successfully
     */
    void activate()
      throw (const StateTransitionFailedException&, const UnsuccessfulRequiredCallException&);

    /** Block this node.
     * This method blocks all instream message processing of this node.
     * The behaviour is the same as for stop(), except that the state of the node does not change.
     *
     * @deprecated Blocking of nodes is deprecated. Nodes should be stopped instead.
     */
    virtual void block();

    /** Unblock this node.
     * This method unblocks all instream message processing of this node if it was previously blocked.
     * The behaviour is the same as for start(), except that the state of the node does not change.
     *
     * @deprecated Blocking of nodes is deprecated. Nodes should be stopped instead.
     */
    virtual void unblock();

    /** Receive a signal.
     * This method handles signals sent from partner nodes and transport strategies, as well as internal
     * signals for controlling the operation of the node thread.
     *
     * @param signal Signal to be received
     */
    void receiveSignal(Signal signal);

    /** Main processing method.
     * This method is running in the node thread and performs all instream processing and signal handling
     * of the node. All generic message processing functionality of a node is implememted here.
     * Plug-ins should not reimplement this method directly.
     *
     * @param num_times Deprecated, must be zero
     */
    virtual void run(int num_times = 0);

    /** Request an input jack.
     * This method requests an input jack with the given format and jack tag
     * and returns an interface to the requested jack.
     * If the request fails, a null pointer is returned.
     * The caller is responsible for deleting the jack interface.
     *
     * This method may only be called in state INITIALIZED.
     *
     * @param format The format of the jack
     * @param jack_tag The name of an input stream
     * @return An interface to the requested jack or 0
     */
    virtual const InputJack* getInputJack(const Format* format, const char* jack_tag = "default") const;
    
   /** Request an output jack.
     * This method requests an output jack with the given format and jack tag
     * and returns an interface to the requested jack.
     * If the request fails, a null pointer is returned.
     * The caller is responsible for deleting the jack interface.
     *
     * This method can bypass the check whether the format is supported.
     *
     * This method may only be called in state OUTPUT_INITIALIZED.
     *
     * @param format The format of the jack
     * @param jack_tag The name of an output stream
     * @param output_initialized @c false to disable format checking, @c true for normal operation
     * @return An interface to the requested jack or 0
     */
    virtual const OutputJack* getOutputJack(const Format* format, const char* jack_tag = 0, bool output_initialized = true) const;

    /** Flush the input queue of the node.
     * This method flushes the queues of all input jacks of the node.
     * It may only be called in state ACTIVATED or STARTED.
     *
     * This implementation invokes doFlush.
     * Plug-ins should not reimplement this method. Instead, special handling for flushing the queue
     * should be implemented in doFlush.
     *
     * @return SUCCESS if the method succeeded, otherwisew FAILURE
     */
    virtual Result flush();

    /** Get the format of an input stream.
     * This method returns the connection format of the given stream if the stream is connected.
     * If the stream is not connected or does not exist, a null pointer is returned.
     *
     * This method should be used by plug-ins to request the current connection format.
     * The caller must not delete the returned object. The object stays valid until the
     * connection format changes.
     *
     * @param jack_tag The name of an input stream
     * @returns A pointer to a Format object describing the connection format or 0
     */
    virtual const Format* getInputFormat(const char * jack_tag = "default") const ;

    /** Get the format of an output stream.
     * This method returns the connection format of the given stream if the stream is connected.
     * If the stream is not connected or does not exist, a null pointer is returned.
     *
     * This method should be used by plug-ins to request the current connection format.
     * The caller must not delete the returned object. The object stays valid until the
     * connection format changes.
     *
     * @param jack_tag The name of an output stream
     * @returns A pointer to a Format object describing the connection format or 0
     */
    virtual const Format* getOutputFormat(const char * jack_tag = "default") const ;

    /** Get an input jack of this node.
     * This method returns a pointer to an existing input jack of this node
     * with the given jack tag. If no such jack exists, a null pointer is returned.
     * If the input jack is cloned, only the master of the jack group is returned.
     * The caller must not delete the returned object.
     *
     * @return A pointer to the requested input jack or 0
     */
    virtual InputJack* getOwnInputStream(const string& jack_tag = "default");

    /** Get an input jack of this node.
     * This method returns a pointer to an existing input jack of this node
     * with the given jack tag. If no such jack exists, a null pointer is returned.
     * If the input jack is cloned, only the master of the jack group is returned.
     * The caller must not delete the returned object.
     *
     * @return A pointer to the requested input jack or 0
     */
    virtual OutputJack* getOwnOutputStream(const string& jack_tag = "default");

    /** Get an input jack of this node.
     * This method returns a pointer to an existing input jack of this node
     * with the given jack tag. If no such jack exists, a null pointer is returned.
     * If the input jack is cloned, only the master of the jack group is returned.
     * The caller must not delete the returned object.
     *
     * @return A pointer to the requested input jack or 0
     */
    virtual const InputJack* getOwnInputStream(const string&  = "default") const;

    /** Get an input jack of this node.
     * This method returns a pointer to an existing input jack of this node
     * with the given jack tag. If no such jack exists, a null pointer is returned.
     * If the input jack is cloned, only the master of the jack group is returned.
     * The caller must not delete the returned object.
     *
     * @return A pointer to the requested input jack or 0
     */
    virtual const OutputJack* getOwnOutputStream(const string&  = "default") const;

    /** Send upstream composite event.
     * This method asynchronously sends a composite event in upstream direction from this node.
     * The actual CEvent sent is a copy of the given CEvent.
     *
     * If a null pointer is passed for the jack tag, the event is sent to all connected input jacks.
     *
     * @param cevent The composite event to send
     * @param jack_tag Jack tag of jack over which the event is sent, or 0 to send to all jacks
     */
    virtual Result sendEventUpstr(CEvent* cevent, const char * jack_tag = "default");

    /** Send upstream composite event.
     * This method asynchronously sends a composite event in upstream direction from this node.
     * The actual CEvent sent is a copy of the given CEvent.
     *
     * If a null pointer is passed for the jack tag, the event is sent to all connected input jacks.
     *
     * @param cevent The composite event to send
     * @param jack_tag Jack tag of jack over which the event is sent, or 0 to send to all jacks
     */
    virtual Result sendEventUpstr(CEvent& cevent, const char * jack_tag = "default");

    /** Send upstream event.
     * This method asynchronously sends a composite event in upstream direction from this node.
     * The actual CEvent sent is a composite event containing exactly the given Event.
     *
     * If a null pointer is passed for the jack tag, the event is sent to all connected input jacks.
     *
     * @param event The event to send
     * @param jack_tag Jack tag of jack over which the event is sent, or 0 to send to all jacks
     */
    virtual Result sendEventUpstr( Event&  event, const char * jack_tag = "default");

    /** Send downstream composite event.
     * This method asynchronously sends a composite event in downstream direction from this node.
     * The actual CEvent sent is a copy of the given CEvent.
     *
     * If a null pointer is passed for the jack tag, the event is sent to all connected input jacks.
     *
     * @param cevent The composite event to send
     * @param jack_tag Jack tag of jack over which the event is sent, or 0 to send to all jacks
     */
    virtual Result sendEventDownstr(CEvent* cevent, const char * jack_tag = "default");

    /** Send downstream composite event.
     * This method asynchronously sends a composite event in downstream direction from this node.
     * The actual CEvent sent is a copy of the given CEvent.
     *
     * If a null pointer is passed for the jack tag, the event is sent to all connected input jacks.
     *
     * @param cevent The composite event to send
     * @param jack_tag Jack tag of jack over which the event is sent, or 0 to send to all jacks
     */
    virtual Result sendEventDownstr(CEvent& cevent, const char * jack_tag = "default");

    /** Send downstream event.
     * This method asynchronously sends a composite event in downstream direction from this node.
     * The actual CEvent sent is a composite event containing exactly the given Event.
     *
     * If a null pointer is passed for the jack tag, the event is sent to all connected input jacks.
     *
     * @param event The event to send
     * @param jack_tag Jack tag of jack over which the event is sent, or 0 to send to all jacks
     */
    virtual Result sendEventDownstr(Event&   event, const char * jack_tag = "default");

    // IInstream interface
    /** Inject an upstream event.
     * This method injects the given instream event into the stream in upstream direction.
     *
     * @param cevent An instream event
     */
    virtual Result sendMessageUpstream(const CEvent& cevent);

    /** Inject a downstream event.
     * This method injects the given instream event into the stream in downstream direction.
     *
     * @param cevent An instream event
     */
    virtual Result sendMessageDownstream(const CEvent& cevent);

    /** Get the working flag.
     * This method gets the current value of the working flag.
     *
     * @return The working flag
     */
    bool getWorkingFlag() const;

    /** Get the producing flag.
     * This method gets the current value of the producing flag.
     *
     * @return The producing flag
     */
    bool getProducingFlag() const;

    /** Deprecated test method.
     *
     * @param m not used
     * @return not used
     *
     * @deprecated This method is included for testing and should not be used.
     */
    Result triggerSendMessage(Message *m) { return (this->sendMessage(m)); }

    /** Deprecated test method.
     *
     * @return not used
     *
     * @deprecated This method is included for testing and should not be used.
     */
    Message *triggerDequeueMessage()        { return getNextDownstrMessage(); } 

    /** Deprecated test method.
     *
     * @param buffer not used
     * @return not used
     *
     * @deprecated This method is included for testing and should not be used.
     */
    virtual Message *triggerProcessBuffer(Buffer *buffer) { return processBuffer(buffer); }

    /** Deprecated test method.
     *
     * @param jack_tag not used
     * @return not used
     *
     * @deprecated This method is included for testing and should not be used.
     */
    unsigned int getQueueSize(const char* jack_tag = "default");

    /** Deprecated test method.
     *
     * @param e not used
     * @return not used
     *
     * @deprecated This method is included for testing and should not be used.
     */
    virtual Result triggerProcessCEvent(CEvent *e) { return processCEvent(e); }

    /** @name Buffer Management
     */
    //@{
    /** Requests a new buffer.
     *
     * This method asks the buffer manager for a new
     * buffer. If no buffer manager is present, a new Allocator will be
     * instantiated and stored.
     *
     * @param size The requested size of the buffer.
     * @return the requested buffer, or NULL if no buffer was available.
     */
    Buffer *getNewBuffer(const size_t size);

    /** Requests a new buffer.
     *
     * This method does the same as getNewBuffer(const size_t). 
     * But the new buffer's header is
     * initialized with the second argument's header.
     *
     * @param size The requested size of the buffer.
     * @param buffer The buffer which header is copied into the new one.
     * @return The requested buffer, or NULL if no buffer was available.
     */
    Buffer *getNewBuffer(const size_t size, const Buffer *buffer);
    //@}

    // analyseData and helpers
  private:
    /** An entry in the analysing cache.
     */
    class AnalysingCacheEntry {
    public:
      Message& message;           ///< Cached message
      list<JackGroup*> jacks;     ///< Jacks to send message to

      /** Create a new analysing cache entry.
       *
       * @param _nessage The cached message
       * @param _jacks Jacks to send message to
       */
      AnalysingCacheEntry(Message& _message, list<JackGroup*> _jacks)
        : message(_message), jacks(_jacks) {}
    };

    list<AnalysingCacheEntry>* analysing_cache;           ///< Cached output messages during data analysis
    bool analysing_flag;                                  ///< Data analysis is running
    bool cached_analysing_flag;                           ///< Temporary flag used by data analysis
    unsigned int max_analysis_iterations;                 ///< Maximum number of iterations for data analysis
    bool analyse_producing_flag;                          ///< Special producing flag for data analysis
    bool analyse_processing_flag;                         ///< Processing flag for data analysis
    bool analysis_data_forwarded;                         ///< Requested data for analysis has been sent
    bool send_data_failed;                                ///< Failed to send requested data for analysis
    JackGroup* current_analysis_out_stream;               ///< Current output stream where data analysis is performed
    InputJack* current_analysis_in_stream;                ///< Current input stream from which data is analysed
    InputStreamMap_t::iterator next_analysis_in_stream;   ///< Cached iterator to current analysing input stream

    /** Check if the run method should exit during data analysis.
     *
     * @returns @c true iff run() should exit
     */
    bool checkAnalysis();

    /** Send an INode::sendDataFailed event downstream.
     *
     * @param jack Jack tag of the output jack to send the event to
     * @returns @c true if this method succeeded, @c false otherwise
     */
    bool transmitSendDataFailed(JackGroup& jack);

    /** Send an INode::sendData event upstream.
     *
     * @param jack Jack tag of the input jack to send the event to
     * @returns @c true if this method succeeded, @c false otherwise
     */
    bool transmitSendData(InputJack& jack);

    /** Send a cached output message to a single output jack.
     * This method picks the next message from the analysing cache which needs to be sent to the gievn output jack
     * and sends this message to that jack.
     *
     * @param jack Jack tag of the output jack to send the message to
     * @returns @c true if a mesage was found in the analysing cache and sent
     */
    bool sendAnalysingCacheEntryTo(JackGroup& jack);

    /** Analyzing oracle.
     * This method chooses the next input jack from which to request data for analysis.
     *
     * @return The jack tag and a pointer ti the input jack
     */
    pair<string, InputJack*> getNextAnalysisInput();

    /** Update the analysing flag.
     */
    void updateAnalysingFlag();

    /** Get the producing flag for data analysis.
     * This method sets the flag which indicates that this node is producing data for analysis.
     *
     * @param analyse_producing The producing flag for data analysis
     */
    void setAnalyseProducingFlag(const bool analyse_producing);

    /** Get the processing flag for data analysis.
     * This method gets the flag which indicates that this node is processing data for analysis.
     *
     * @param analyse_processing The processing flag for data analysis
     */
    void setAnalyseProcessingFlag(const bool analyse_processing); 

  protected:
    /** Perform data analysis.
     * This method analyses the data of an input stream. It can be called by plug-ins
     * in doInitOutput() to determine the actual output format of the node depending on
     * the input data.
     *
     * Data analysis requires data (NMM buffers) from predecessor nodes. This data is requested
     * internally by the predecessor nodes, which in turn requires these nodes to process data
     * and perhaps request data from their predecessors, and so on. In particular, the processBuffer()
     * method and instream event handlers of this node are invoked during data analysis.
     *
     * If no input jack with the given jack tag exists, then this method fails.
     *
     * @param in_jack Jack tag of input jack from which to analyse data.
     * @return @c true if data analysis succeeded, @c false otherwise
     */
    virtual bool analyseData(const string& in_jack = "default");

    /** Set the analysing flag.
     * This method sets the flag which indicates that data analysis is in progress.
     * The processBuffer() method must set the flag to @c false when data analysis
     * is complete.
     *
     * @param analysing The analysing flag
     */
    void setAnalysingFlag(const bool analysing);

    /** Get the analysing flag.
     * This method gets the flag which indicates that data analysis is in progress.
     *
     * @return The analysing flag
     */
    bool getAnalysingFlag() const;

    /** Set the maximum number of iterations for data analysis.
     * Data analysis only performs the given number of iterations. If the maximum number of iterations
     * is reached, data analysis fails.
     *
     * @param iterations Maximum number of iterations for data analysis
     */
    void setMaxAnalysisIterations(const unsigned int iterations);

    /** Get the maximum number of iterations for data analysis.
     * Data analysis only performs the given number of iterations. If the maximum number of iterations
     * is reached, data analysis fails.
     *
     * @return Maximum number of iterations for data analysis
     */
    unsigned int getMaxAnalysisIterations() const;

    /** Get the producing flag for data analysis.
     * This method gets the flag which indicates that this node is producing data for analysis.
     *
     * @return The producing flag for data analysis
     */
    bool getAnalyseProducingFlag() const;

    /** Get the processing flag for data analysis.
     * This method gets the flag which indicates that this node is processing data for analysis.
     *
     * @return The processing flag for data analysis
     */
    bool getAnalyseProcessingFlag() const;

    /** Check if an output stream with a given jack tag is connected.
     *
     * @param jack_tag A jack tag
     * @return @c true if an output stream with the given jack tag is connected, @c false otherwise
     */
    bool isAnyOutputStreamConnected(const string jack_tag) const;

    // INode Interface
    /** Request data from the predecessor node.
     * This is an instream event which is used by dynamic data analysis.
     * It is sent internally between nodes and should not be sent manually by applications or plug-ins.
     *
     * @return DELETE
     */
    virtual Result sendData();

    /** Data request from the predecessor node has failed.
     * This is an instream event which is used by dynamic data analysis.
     * It is sent internally between nodes and should not be sent manually by applications or plug-ins.
     *
     * @return DELETE
     */
    virtual Result sendDataFailed();

    /****************************** analyseData and helpers ******************************/

  public:
    /** @name IDelayMeasurement implementation
     */
    //@{
    /** Enable or disable a start timepoint for delay measurement.
     *
     * This method enables or disables starting delay measurement for this node at the given time point.
     *
     * @param start_timepoint The start timepoint for delay measurement
     * @param enabled @c true to enable or @c false to disable delay measurement
     */
    virtual void setDelayStartEnabled(const IDelayMeasurement::StartTimepoint& start_timepoint, const nmm_bool& enabled);

    /** Enable or disable a stop timepoint for delay measurement.
     *
     * This method enables or disables stopped delay measurement for this node at the given time point.
     *
     * @param stop_timepoint The stop timepoint for delay measurement
     * @param enabled @c true to enable or @c false to disable delay measurement
     */
    virtual void setDelayStopEnabled(const IDelayMeasurement::StopTimepoint& stop_timepoint, const nmm_bool& enabled);

    /** Enable or disable a start timepoint for delay measurement (clic interface).
     *
     * This method enables or disables starting delay measurement for this node at the given time point.
     *
     * @param start_timepoint The start timepoint for delay measurement
     * @param enabled @c true to enable or @c false to disable delay measurement
     *
     * @deprecated Helper method which becomes obsolete when clic can handle enum values
     */
    virtual void setDelayStartEnabled2(const string& start_timepoint, const nmm_bool& enabled);

    /** Enable or disable a stop timepoint for delay measurement (clic interface).
     *
     * This method enables or disables stopped delay measurement for this node at the given time point.
     *
     * @param stop_timepoint The stop timepoint for delay measurement
     * @param enabled @c true to enable or @c false to disable delay measurement
     *
     * @deprecated Helper method which becomes obsolete when clic can handle enum values
     */
    virtual void setDelayStopEnabled2(const string& stop_timepoint, const nmm_bool& enabled);

    /** Check if a start timepoint for delay measurement is enabled.
     *
     * @param start_timepoint The start timepoint for delay measurement
     * @return @c true if delay measurement is enabled, otherwise @c false
     */
    virtual nmm_bool isDelayStartEnabled(const IDelayMeasurement::StartTimepoint& start_timepoint) const;

    /** Check if a stop timepoint for delay measurement is enabled.
     *
     * @param stop_timepoint The stop timepoint for delay measurement
     * @return @c true if delay measurement is enabled, otherwise @c false
     */
    virtual nmm_bool isDelayStopEnabled(const IDelayMeasurement::StopTimepoint& stop_timepoint) const;

    /** Get average message delay.
     *
     * This method returns the current average over the last measured delays.
     * The result is valid only if the stop timepoint is not @c NO_STOP and the node has
     * already received messages.
     *
     * @param stop_timepoint The stop timepoint for delay measurement
     * @return The average message delay
     */
    virtual Interval getAverageDelay(const IDelayMeasurement::StopTimepoint& stop_timepoint);

    /** Get minimum message delay.
     *
     * This method returns the current minimum over the last measured delays.
     * The result is valid only if the stop timepoint is not @c NO_STOP and the node has
     * already received messages.
     *
     * @param stop_timepoint The stop timepoint for delay measurement
     * @return The minimum message delay
     */
    virtual Interval getMinDelay(const IDelayMeasurement::StopTimepoint& stop_timepoint);

    /** Get maximum message delay.
     *
     * This method returns the current maximum over the last measured delays.
     * The result is valid only if the stop timepoint is not @c NO_STOP and the node has
     * already received messages.
     *
     * @param stop_timepoint The stop timepoint for delay measurement
     * @return The maximum message delay
     */
    virtual Interval getMaxDelay(const IDelayMeasurement::StopTimepoint& stop_timepoint);

    /** Reset delay measurement for this node.
     *
     * This method resets all samples of measured message delays for this node.
     *
     * @param stop_timepoint The stop timepoint for delay measurement
     */
    virtual void resetDelayMeasurement(const IDelayMeasurement::StopTimepoint& stop_timepoint);

    /** Set the number of samples for average delay computation.
     *
     * This method sets the maximum number of samples for computing the average message delay.
     * The delays of the most recent messages are stored up the the given number of messages
     * and used for computing a new average for every new message.
     *
     * @param number_of_samples The number of samples
     */
    virtual void setNumberOfDelaySamples(const nmm_uint32& number_of_samples);

    /** Enable or disable delay logging.
     *
     * @param enabled @c true to enable or @c false to disable delay logging
     *
     * @deprecated LOGDELAY should be a LOGStream, but LOGStreams are defined in nmm/multimedia/sync
     */
    virtual void setDelayLoggingEnabled(const nmm_bool& enabled);

    /** Set delay logging interval.
     *
     * This method sets the number of messages after which a delay log message is displayed.
     * The default is to display a log message every 20 messages.
     *
     * @param interval The delay logging interval in number of messages
     */
    virtual void setDelayLoggingInterval(const nmm_uint32& interval);
    //@}

  public:
    /** @name Delay measurement implementation
     */
    //@{
    /** Start delay measurement.
     *
     * This method is called when delay measurement is started.
     * It checks whether delay measurement for the given timepoint is enabled.
     * (This guarantees that the mutex is locked only once and the operation is atomic)
     *
     * @param timepoint Timepoint when measurement is started
     * @param msg The message
     *
     * @note This method is public to allow QueuedInputJack to access it
     */
    void startDelayMeasurement(IDelayMeasurement::StartTimepoint timepoint, Message* msg);

    /** Stop delay measurement.
     *
     * This method is called when delay measurement is stopped.
     * It checks whether delay measurement for the given timepoint is enabled.
     * (This guarantees that the mutex is locked only once and the operation is atomic)
     *
     * @param timepoint Timepoint when measurement is stopped
     * @param msg The message
     * @param additional_delay A constant added to measured delay
     *
     * @note This method is public to allow QueuedInputJack to access it
     */
    void stopDelayMeasurement(IDelayMeasurement::StopTimepoint timepoint,
                              Message* msg, Interval additional_delay = Interval());
    //@}

  private:
    ThreadMutex delay_mutex;             ///< Mutex for synchronizing delay variables

    bool delay_logging_enabled;          ///< Delay logging is enabled
    unsigned int delay_logging_counter;  ///< Number of messages processed since last log message
    unsigned int delay_logging_interval; ///< Number of messages after which to produce a log message
    unsigned int max_delay_samples;      ///< Maximum number of delay samples

    static const size_t NUM_DELAY_START_TIMEPOINTS = 4;       ///< Number of start timepoints for delay measurement
    static const size_t NUM_DELAY_STOP_TIMEPOINTS = 4;        ///< Number of stop timepoints for delay measurement

    Time in_delay_time;                  ///< Intermediate storage for input timestamp for delay measurement

    unsigned int delay_start_enabled;                         ///< Bitmask of enabled delay measurement start timepoints
    unsigned int delay_stop_enabled;                          ///< Bitmask of enabled delay measurement stop timepoints
    std::deque< Interval > delays[NUM_DELAY_STOP_TIMEPOINTS]; ///< Delay measurement samples for all delay stop timepoints
    Interval max_delay[NUM_DELAY_STOP_TIMEPOINTS];            ///< Maximum delay for all delay stop timepoints
    Interval min_delay[NUM_DELAY_STOP_TIMEPOINTS];            ///< Minimum delay for all delay stop timepoints
    Interval average_delay[NUM_DELAY_STOP_TIMEPOINTS];        ///< Average delay for all delay stop timepoints
  };

  // IMPLEMENTATION

  inline Result GenericNode::doSetFormat(const Format&) { return SUCCESS; }

  inline Result GenericNode::processCEvent(CEvent*) { return SUCCESS; }
}

#include "nmm/nmm_suffix.hpp"

#endif

