/* 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_SYNC_SINK_NODE_HPP_
#define _NMM_GENERIC_SYNC_SINK_NODE_HPP_

#include "nmm/nmm_prefix.hpp"

#include "nmm/multimedia/GenericSinkNode.hpp"
#include "nmm/multimedia/interfaces/ITimeProgressImpl.hpp"
#include "nmm/multimedia/interfaces/IProgressListenerImpl.hpp"
#include "nmm/multimedia/interfaces/ITrackDurationImpl.hpp"
#include "nmm/utils/time/UserTime.hpp"
#include "nmm/multimedia/sync/interfaces/ISynchronizedSinkImpl.hpp"

#include "visibility.hpp"
namespace NMM {

  class SinkController;

  /** A generic synchronized sink node.
   * This is the base class of all node classes which represent sink nodes with synchronization support.
   * A synchronized sink node has a sink controller which performs intra-stream synchronization and may be
   * connected to a synchronizer that performs intra-stream synchronization with other sink nodes.
   *
   * Note that synchronized sink nodes provide a different interface to the plug-in developer.
   */
  class DLLEXPORT GenericSyncSinkNode :
    public GenericSinkNode,
    public ISynchronizedSinkImpl,
    public ITimeProgressImpl,
    public IProgressListenerImpl,
    public ITrackDurationImpl	   
  {
  protected:
    bool synchronized;           ///< Intra-stream synchronization is enabled
    bool have_prepare_method;    ///< This class reimplements the prepareBuffer method
    UserTime media_time;         ///< Current media time

    Time first_time;             ///< Presentation time of first buffer, used for progress information
    Interval last_time;          ///< Presentation time of last buffer, used for progress information
    bool first_buffer;           ///< Next buffer is the first buffer
    bool send_progress_update;   ///< Progress update events are enabled
    Buffer* saved_buffer;        ///< Temporary storage for a buffer
    Interval progress_interval;  ///< Time interval for progress update events
    u_int64_t track_duration;    ///< Total track duration
    bool update_seek_time;       ///< Special update needed after seeking
    u_int64_t seek_time;         ///< Seek time
    
    SinkController* controller;  ///< Sink controller of this sink node

    // sync statistics
    int total_buffers;           ///< Total buffers passed
    int total_buffers_discarded; ///< Total buffers discarded
    Interval total_wait_time;    ///< Total time waited for presentation
    Time start_time;             ///< Start time of synchronization
    
  public:
    /** Create a synchronized sink 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
     */
    GenericSyncSinkNode(const char* name,
                        const StreamQueue::Mode queue_mode = StreamQueue::DEFAULT_MODE,
                        const int size = StreamQueue::DEFAULT_SIZE);

    /** Virtual destructor.
     */
    virtual ~GenericSyncSinkNode();

    /** Enable or disable intra-stream synchronization.
     *
     * @return @c true to enable, @c false to disable intra-stream synchronization
     * @return SUCCESS
     */
    virtual Result setSynchronized(const nmm_bool& toggle);

    /** Check if intra-stream synchronization is enabled.
     *
     * @return @c true if intra-stream synchronization is enabled, @c false otherwise
     */
    virtual nmm_bool isSynchronized();

    /** Get the sink controller of this sink node.
     *
     * @return Sink controller interface of sink controller
     */
    ISinkController* getController();
    
    /** Prepare a buffer for presentation.
     * This method performs all the necessary steps to prepare a buffer for presentation.
     * Some of these steps may take too much time to guarantee timely presentation of the bufer.
     * Therefore, the buffer is first prepared for presentation using this method and then presented
     * using the presentBuffer() method.
     *
     * Typical tasks that should be performed in prepareBuffer() are copying, resampling, scaling
     * and other costly processing of the data to present.
     *
     * This method must be implemented in a subclass.
     * It may modify the incoming buffer or allocate a new buffer. It must return the buffer
     * containing the prepared data. If this method allocates a new buffer, it must release the incoming
     * buffer.
     *
     * If a subclass remiplements processBuffer(), this method is not called unless the base class version
     * of processBuffer() is invoked or this method is called explicitly.
     *
     * @param buffer The buffer to be presented
     */
    virtual void prepareBuffer(Buffer* buffer);

    /** Present a buffer.
     * This method presents a prepared buffer. It is scheduled by the synchronization algorithm to present
     * the buffer at the current system time, so presentation must not take too long. Any costly processing
     * steps must be carried out in prepareBuffer().
     *
     * This method must be implemented in a subclass.
     *
     * If a subclass remiplements processBuffer(), this method is not called unless the base class version
     * of processBuffer() is invoked or this method is called explicitly.
     *
     * @param in_buffer The buffer to be presented
     * @return The message that will be returned by processBuffer()
     */
    virtual Message* presentBuffer(Buffer* in_buffer) = 0;

    /** 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.
     *
     * This implementation invokes the synchronization algorithm and invokes prepareBuffer() and presentBuffer().
     *
     * @param in_buffer The input buffer, if any, or 0
     * @return The returned message, if any, or 0
     */
    virtual Message* processBuffer(Buffer *in_buffer);

    /** Get current media time.
     *
     * @return Current media time
     */
    inline UserTime getMediaTime() 
    {
      return media_time;
    };
    
    /** Set synchronization offset.
     * This method does nothing.
     *
     * @param velocity Not used
     * @param offset Reserved for future use
     * @return Usually SUCCESS
     *
     * @deprecated Ancient debugging method, not used any more
     */
    inline Result setSyncOffset(const Rational& velocity, const Interval& offset) {
      return SUCCESS;
    }

    
    /** Synchronization reset (instream event).
     * This implementation notifies the controller that a syncReset event has been received.
     *
     * @return Result of the instream event
     */
    virtual Result syncReset();

    /** Sets the update interval.
     *
     * This method sets the time interval after which progress events are sent.
     * The default interval is 1 second.
     *
     * @param update_interval The update time interval
     */
    virtual void setProgressUpdateInterval(const Interval& update_interval);

    /** Enable or disable progress updates.
     *
     * This method enables or disables sending of progress information.
     *
     * @param enable_update @c true to enable or @c false to disable progress updates
     */
    virtual void setProgressUpdateEnabled(const nmm_bool& enable_update);

    /** Progress information (instream event).
     * Instream event containing the current progress state in the form
     * @c numerator / @c denominator.
     *
     * @param numerator Progress numerator (current progress)
     * @param denominator Progress denominator (maximum progress)
     */
    void setProgress(const nmm_uint64& numerator, const nmm_uint64& denominator);

    /** Track duration (instream event).
     *
     * This instream event is sent by a source node whenever the duration of a new
     * track is known.
     *
     * @param track_duration Duration of the current track
     * @return Result of the instream event
     */
    virtual Result trackDuration(const Interval& track_duration);
    
    /** Pause this node.
     * This method is invoked by the controller to notify the node that the controller is paused.
     *
     * @return SUCCESS
     */
    Result pause();

    /** Wake up this node after pazse.
     * This method is invoked by the controller to notify the node that the controller is wakes up after pause.
     *
     * @return SUCCESS
     */
    Result wakeup();
    
  };
}

#include "nmm/nmm_suffix.hpp"

#endif // _NMM_GENERIC_SYNC_SINK_NODE_HPP_
