/* 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_SINK_CONTROLLER_HPP_
#define _NMM_SINK_CONTROLLER_HPP_

#include "nmm/nmm_prefix.hpp"

#include "Controller.hpp"
#include "nmm/multimedia/sync/interfaces/ISinkControllerImpl.hpp"
#include "nmm/utils/time/Interval.hpp"

namespace NMM {
  class GenericSyncSinkNode;
  
  /** A synchronization controller for sink node.
   * This class implements latency-based synchronization for synchronized sink nodes.
   */
  class SinkController : public Controller,
                         virtual public ISinkControllerImpl {
  protected:
    bool     block_for_sync_reset;    ///< Blocking is enabled
    bool     notify_in_time;          ///< First-buffer-in-time notification is enabled
    bool     first_buffer_in_time;    ///< First buffer did not arrive in time
    bool     wait_to_present;         ///< Waiting for presentation is enabled
    Interval max_average_delay;       ///< Maximum deviation of average latency from theoretical latency
    Interval max_delay;               ///< Maximum acceptable delay
    Interval theo_latency;            ///< Theoretical latency
    Interval default_theo_latency;    ///< Default theoretical latency
    Interval average_latency;         ///< Average latency of this sink
    Interval min_wait_interv;         ///< Minimum wait interval
    int average_count;                ///< Number of samples for computing average latency
    int n;                            ///< Number of samples for average latency already available
    int max_disc;                     ///< Maximum number of buffers to discard before presenting at least one buffer
    int disc_count;                   ///< Number of buffers discarded since last presented buffer
    bool first_buffer;                ///< Next buffer is first buffer in stream
    int sink_id;                      ///< Sink ID of this controller
    Timestamp old_ts;                 ///< Timestamp of most recent buffer
    GenericSyncSinkNode* parent_node; ///< Pointer to parent node
    ISinkSynchronizer* synchronizer;  ///< Interface to synchronizer

    /** States of the sink controller
     */
    enum State {
      WORKING,           ///< Controller is working normally
      WAIT_FOR_RESET,    ///< Controller is waiting for a syncReset event
      WAIT_FOR_RESTART,  ///< Controller is waiting for synchronizer to restart
      PAUSE              ///< Controller is paused and waiting for wakeup from synchronizer
    };
    
    State state;                ///< Current state of the controller
    ThreadMutex state_mutex;    ///< Mutex for synchronizing state changes

    /** Initialization.
     * This method is invoked by both constructors to initialize variables.
     */
    void init_variables();

    /** Reset variables of average latency computation.
     */
    virtual void reset_average();
    
  public:
    /** Create a controller.
     *
     * @param mode Initial mode of the controller
     */
    SinkController(const IController::Mode mode);

    /** Create a controller for a node.
     *
     * @param parent The synchronized node to which this controller belongs
     * @param mode Initial mode of the controller
     */
    SinkController(GenericSyncSinkNode* parent = 0,
                   const IController::Mode mode = Controller::DEFAULT_MODE);

    /** Virtual destructor.
     */
    virtual ~SinkController();
    
    /** Check if a timestamp is valid.
     * If the controller is in ONLINE mode, this method is called to determine if a
     * buffer with given timestamp can be presented in time. In this case, the buffer is "valid",
     * otherwise it is "invalid".
     * The implementation must be provided by a subclass.
     *
     * @param timestamp The timestamp of the buffer to check.
     * @return @c true if the buffer is valid, @c false if the buffer is invalid
     */
     virtual bool checkBufferValid(const Timestamp timestamp);
     
    /** Determine the time to wait for presenting a buffer.
     * This method determines and returns the amount of time that the node should wait before presenting
     * the buffer.
     * If the waiting interval would be smaller than the minimum waiting interval, this method returns zero.
     * The waiting interval is capped by the maximum waiting interval.
     *
     * @param timestamp Buffer timestamp
     * @return Time to wait for presenting this buffer
     */
     virtual Interval waitToPresent(const Timestamp timestamp); 

    /** Reset this controller.
     * This method is called by the node when it has received a syncReset event.
     * The controller notifies the synchronizer about the reset and changes its internal state.
     *
     * @return SUCCESS
     */
    virtual Result reset();

    /** Pause this controller.
     * This method is called by the synchronizer to pause this controller.
     *
     * @return SUCCESS
     */
    virtual Result pause();

    /** Wake up this controller after pause.
     * This method is called by the synchronizer to wake up this controller after pause.
     *
     * @return SUCCESS
     */
    virtual Result wakeup();

  protected:
    // BEGIN> ISinkController interface
    /** Set the maximum acceptable delay of a buffer.
     * This method sets the maximum delay of a buffer that should be tolerated.
     * If the difference between the arrival time of the buffer and its presentation system time
     * is greater than this value, then the buffer is too late to be presented in time.
     * The delay must be positive, otherwise this method fails.
     *
     * @param max_delay Maximum acceptable delay
     * @return @c SUCCESS if this method succeeded, @c FAILURE otherwise
     */
    virtual Result setMaxDelay(const Interval& max_delay);

    /** Set the maximum acceptable deviation from the theoretical latency.
     * This method sets the maximum deviation of the average latency measured by a node from the
     * theoretical latency computed by the synchronizer. If the difference between these values
     * exceeds the maximum acceptable deviation, the new average measured latency is propagated
     * to the synchronizer which in turn propagates the new theoretical latency to all sinks.
     *
     * @param value The maximum acceptable deviation from the theoretical latency
     * @return SUCCESS
     */
    virtual Result setMaxAverageDelay(const Interval& value);

    /** Set the theoretical latency.
     * This method is called by the synchronizer to tell the controllers the
     * theoretical latency of the pipeline.
     *
     * @param value Theoretical latency
     * @return SUCCESS
     */
    virtual Result setTheoLatency(const Interval& value);

/** Set the default theoretical latency.
     * This method is called by the synchronizer to tell the controllers the
     * default theoretical latency of the pipeline. This value is used as the
     * theoretical latency if no value for the theoretical latency is available
     * from the synchronizer.
     *
     * @param value Default theoretical latency
     * @return SUCCESS
     */
    virtual Result setDefaultTheoLatency(const Interval& value);

    /** Sets the synchronizer which synchronizes this controller.
     * This method is called by the synchronizer when a controller has been added.
     * The synchronizer passes an interface to itself and the sink ID to the controller.
     * The controller needs to specify the sink ID when communicating with the synchronizer
     * using the synchronizer interface.
     *
     * @param sync An interface of the synchronizer
     * @param sink_id ID of this controller at the synchronizer
     * @return SUCCESS
     */
    virtual Result setSinkSynchronizer(const ISinkSynchronizer* sync, const nmm_int32& sink_id);

     /** Set the number of samples for computing the average latency.
     * This method sets the number of buffers used to compute the average latency.
     * A small number means that the synchronizer is told the
     * average latency of incoming buffers very often. That means the
     * synchronization reacts very quickly (and this reaction may be visible)
     * For this reason higher values (such as 50 for audio and 20 for video)
     * are recommended.
     *
     * @param value Number of samples for computing the average latency
     */
    virtual Result setAverageCount(const nmm_int32& value);

    /** Set the maximum number of buffers to discard.
     * This method sets the maximal number of buffers that can be discarded before at least
     * one buffer is presented.
     *
     * @param value Maximum number of buffers to discard
     * @return SUCCESS
     */
    virtual Result setMaxDiscardingCount(const nmm_int32& value);

    /** Wait for a syncReset event.
     * This method is called by the synchronizer during a synchronization reset.
     * The controller has to wait until the node has received a syncReset event.
     * During this time, the node continues processing buffers.
     *
     * If the node has already received a syncReset event, the controller does nothing.
     *
     * @return SUCCESS
     */
    virtual Result waitForReset();

    /** Restart the controller.
     * This method is called by the synchronizer during a reset when all controllers
     * have received a syncReset event. The controller continues processing buffers.
     *
     * @return SUCCESS
     */
    virtual Result restart();

    /**
     * Tells the node to wait for the time stamp to become valid
     * or to simply present the buffer when it arrives
     * @param enable true to enable waiting, false to disable
     */
    virtual Result enableWaitToPresent(const nmm_bool& enable);

    /** Set the minimum time to wait.
     * This method sets the minimum time a controller should delay a buffer which arrives too early.
     * If the time for which a buffer would be delayed is smaller than this interval, the buffer
     * is presented immediately.
     *
     * @param min_wait Minimum time to wait
     */
    virtual Result setMinWaitInterval(const Interval& min_wait);

    /** Enable or disable first-buffer-in-time notification.
     * This method sets whether the sink controller should notify its
     * sink synchronizer if the first buffer after a syncReset
     * arrived in time (in regard to the theoretical latency).
     * The notification is done by calling
     * ISinkSynchronizer::setFirstBufferInTimeAverageLatency.
     *
     * @param enable @c true to enable, @c false to disable first-buffer-in-time notification
     */
    virtual void enableFirstBufferInTime(const nmm_bool& enable);

    /** Enable or disable blocking.
     * This method enables or disables the blocking of the controller (and
     * therefore the blocking of its parent node) when waiting for restart()
     * after having received a syncReset.
     * Blocking is enabled by default.
     *
     * @param enable @c true to enable, @c false to disable blocking
     */
    virtual void enableBlocking(const nmm_bool& enable);
    // END< ISinkController interface
  };

}

#include "nmm/nmm_suffix.hpp"

#endif // _NMM_SINK_CONTROLLER_HPP_



