/* 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_ITimeshifting_HPP
#define NMM_ITimeshifting_HPP

#include "nmm/nmm_prefix.hpp"

#include "nmm/comm/Interface.hpp"
#include "nmm/comm/messaging/Event.hpp"
#include "nmm/comm/messaging/IllegalStateException.hpp"
#include <memory>

/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *
 * See file LICENCE for terms and conditions of usage
 *
 */

#include "nmm/utils/time/Time.hpp"
#include "nmm/utils/time/Interval.hpp"
#include "nmm/multimedia/INode.hpp"

#include "visibility.hpp"

namespace NMM
{
  /** Timeshifting interface.
   * The interface for the timeshifting node.
   */

  class DLLEXPORT ITimeshifting : virtual public NMM::Interface { 
  public:    /** Main working mode.
     * The main working mode controls what happens to incoming data.
     */

  enum Mode {       /** Live mode.
       * Every arriving buffer is forwarded and, if enabled, backed up in memory for instant replay.
       */

    LIVE ,       /** File buffer mode.
       * Buffers and events are buffered in a file buffer.
       * This mode is divided up in different play modes.
       */

    FILEBUF  };
    /** Play mode.
     * The play mode controls what data is sent to the output if the main mode is FILEBUF.
     */

  enum PlayMode {       /** Playback mode.
       * Buffers are sent in the order of arrival, but possibly delayed.
       */

    PLAY ,       /** Pause mode.
       * No buffers are sent.
       */

    PAUSE ,       /** Fast forward mode.
       * Only I-Frames are sent, and a certain number of buffers are skipped, depending on a speed parameter.
       * If there are no more buffers, the node automatically switches to FB_LIVE mode.
       */

    FAST_FORWARD ,       /** Rewind mode.
       * Like fast-forward mode, except that buffers are sent in reverse order and
       * if there are no more buffers, the node automatically switches to PLAY mode.
       */

    REWIND ,       /** Buffered live mode.
       * Arriving buffers are sent immediately.
       */

    FB_LIVE  };

    ITimeshifting() : NMM::Interface(ITimeshifting::ITimeshifting_type, "NMM") { };

    ITimeshifting(NMM::NMMObject& parent) : NMM::Interface (parent, ITimeshifting::ITimeshifting_type, "NMM") { };

    virtual ~ITimeshifting() {};

    static const char* ITimeshifting_type;

    virtual  string getInterfaceType() const { return ITimeshifting_type; }

    NMM::Interface* copy() const { return new ITimeshifting(*this); }
    static void used(); 
    static const char* enableMemBuf_event;
    static const char* disableMemBuf_event;
    static const char* isMemBuf_event;
    static const char* keepData_event;
    static const char* getMode_event;
    static const char* getPlayMode_event;
    static const char* setModeLive_event;
    static const char* setModeFBLive_event;
    static const char* setModePlay_event;
    static const char* setModePause_event;
    static const char* setModeFastForward_event;
    static const char* setModeRewind_event;
    static const char* setFilename_event;
    static const char* setFileBufferThreshhold_event;
    static const char* setBufferSize_event;
    static const char* startRecord_event;
    static const char* stopRecord_event;
    static const char* isRecording_event;
    static const char* saveToFile_event;
    static const char* getReadPosition_event;
    static const char* getStartTime_event;
    static const char* getLivePosition_event;
    static const char* getLiveOffset_event;
    static const char* getFileLength_event;
    /** @name Memory Buffer
     */
//@{
    /** Enable memory buffer.
     * Enable a memory buffer for instant replay that holds up to @a size buffers.
     * @param size Maximum number of buffers in the memory buffer
     */

    virtual void enableMemBuf(const nmm_uint32& size = 60)
      throw(const NMM::SerializedException&);
    /** Disable memory buffer.
     * Disable and free the memory buffer. Set the @a play flag for instant replay.
     * @param play If true, start playback at the beginning of the buffer
     */

    virtual void disableMemBuf(const nmm_bool& play = false)
      throw(const NMM::SerializedException&);
    /** Is memory buffer enabled.
     * @return true if the memorybuffer is enabled
     */

    virtual nmm_bool isMemBuf()
      throw(const NMM::SerializedException&);
    /** Keep incoming data.
     * Control whether to keep incoming data (and forward no buffers) or not.
     * @param keep if true, keep data
     */

    virtual void keepData(const nmm_bool& keep)
      throw(const NMM::SerializedException&);
//@}
    /** @name Modes and Play Modes
     */
//@{
    /** Get current main mode.
     * @return Current main mode
     */

    virtual ITimeshifting::Mode getMode() const
      throw(const NMM::SerializedException&);
    /** Get current play mode.
     * @return Current play mode, if the main mode is FILEBUF
     */

    virtual ITimeshifting::PlayMode getPlayMode() const
      throw(const NMM::SerializedException&);
    /** Switch to LIVE mode.
     * Switch the node into the LIVE main mode. This completely disables timeshifting and file buffering.
     * @param enable_memory_buffer Enable memory buffering
     * @param send_sync_reset Send a ISyncReset::syncReset event after the mode change
     */

    virtual void setModeLive(const nmm_bool& enable_memory_buffer = true, const nmm_bool& send_sync_event = true)
      throw(const NMM::SerializedException&);
    /** Set play mode to FB_LIVE.
     * Switch the node into FILEBUF mode and set the play mode to FB_LIVE.
     * @param send_sync_reset Send a ISyncReset::syncReset event after the mode change
     */

    virtual void setModeFBLive(const nmm_bool& send_sync_event = true)
      throw(const NMM::SerializedException&);
    /** Set play mode to PLAY.
     * Set the play mode to PLAY. The main mode must be FILEBUF!
     * @param send_sync_reset Send a ISyncReset::syncReset event after the mode change
     */

    virtual void setModePlay(const nmm_bool& send_sync_reset = true)
      throw(const NMM::SerializedException&);
    /** Set play mode to PAUSE.
     * Switch the node into FILEBUF mode and set the play mode to PAUSE.
     */

    virtual void setModePause()
      throw(const NMM::SerializedException&);
    /** Set play mode to FAST_FORWARD.
     * Set the play mode to FAST_FORWARD. The main mode must be FILEBUF!
     * Also set the playback speed, which is the number of I-Frames to skip, to @a speed.
     * If there are no more buffers to send, the play mode is imediately set to FB_LIVE.
     * @param speed Playback speed (number of I-Frames to skip for every I-frame sent)
     */

    virtual void setModeFastForward(const nmm_int32& speed = 5)
      throw(const NMM::SerializedException&);
    /**
     * Set play mode to REWIND.
     * Set the play mode to REWIND. The main mode must be FILEBUF!
     * Also set the playback speed, which is the number of I-Frames to skip, to @a speed.
     * If there are no more buffers to send, the play mode is imediately set to PLAY.
     * @param speed Playback speed (number of I-Frames to skip for every I-frame sent)
     */

    virtual void setModeRewind(const nmm_int32& speed = -5)
      throw(const NMM::SerializedException&);
//@}
    /** @name File Buffer
     */
//@{
    /** Set file buffer filename.
     * Use another file for buffering if current file is empty.
     * @param filename Buffer filename
     */

    virtual void setFilename(const string& filename)
      throw(const NMM::SerializedException&);
    /** Set file buffer threshold.
     * Sets the file buffer threshold, which is the maximum number of GOPs to store in the file buffer.
     * If the buffer is full, the oldest recorded GOPs is overwritten.
     * @param threshold File buffer threshold
     */

    virtual void setFileBufferThreshhold(const nmm_uint32& threshold) /* limited(INode::CONSTRUCTED) */
      throw(const NMM::SerializedException&, const NMM::IllegalStateException&);
    /** Set maximum buffer size.
     * Sets the maximum size of arriving buffers to @a size. This should be the maximum size of a GOP.
     * @param size Maximum buffer size
     * @return SUCCESS if the method succeeded, otherwise FAILURE
     */

    virtual Result setBufferSize(const nmm_uint32& size)
      throw(const NMM::SerializedException&);
    /** Enter recording mode.
     * Switch the file buffer into recording mode. In this mode, no blocks are overwritten and the file buffer
     * is not deleted after the node is destroyed.
     */

    virtual void startRecord()
      throw(const NMM::SerializedException&);
    /** Leave recording mode.
     * Switch the file buffer back from recording mode into to mode.
     */

    virtual void stopRecord()
      throw(const NMM::SerializedException&);
    /** Is file buffer in recording mode.
     * @return True if the file buffer is in recording mode
     */

    virtual nmm_bool isRecording()
      throw(const NMM::SerializedException&);
    /** Save file buffer to file.
     * Saves the file buffer (without events) to a file.
     * @param filename Save filename
     */

    virtual Result saveToFile(const string& filename)
      throw(const NMM::SerializedException&);
//@}
    /** @name Timing
     */
//@{
    /** Get read position.
     * This method returns the current read position in the timeshifting buffer.
     * In FILEBUF mode, this value is between the values returned by getStartTime and getLivePosition.
     * In LIVE mode, this returns the same value as getLivePosition.
     * @return Current read position
     */

    virtual Time getReadPosition() const /* limited(INode::STARTED) */
      throw(const NMM::SerializedException&, const NMM::IllegalStateException&);
    /** Get starting time.
     * This method returns the (absolute) time when the tiomeshifting buffer begins.
     * In LIVE mode, this is the same value as returned by getLivePosition.
     * @return The start time of the timeshifting buffer
     */

    virtual Time getStartTime() const /* limited(INode::STARTED) */
      throw(const NMM::SerializedException&, const NMM::IllegalStateException&);
    /** Get live position.
     * This method returns the (absolute) time when the timeshifting buffer ends.
     * In LIVE mode, this is the same value as returned by getStartTime.
     * @return The end time of the timeshifting buffer
     */

    virtual Time getLivePosition() const /* limited(INode::STARTED) */
      throw(const NMM::SerializedException&, const NMM::IllegalStateException&);
    /** Get live offset.
     * This method computes the "live" offset when timeshifting. This is the offset between the
     * live position and the current read position.
     * In LIVE mode, the live offset is always zero
     * @return The live offset
     */

    virtual Interval getLiveOffset() const /* limited(INode::STARTED) */
      throw(const NMM::SerializedException&, const NMM::IllegalStateException&);
    /** Get file size.
     * This method computes the length of the timeshifting buffer as a time interval.
     * In LIVE mode, the file length is always zero
     * @return The length of the timeshifting buffer
     */

    virtual Interval getFileLength() const /* limited(INode::STARTED) */
      throw(const NMM::SerializedException&, const NMM::IllegalStateException&);

//@}

  };

  typedef auto_ptr<ITimeshifting> ITimeshifting_var;

  DLLEXPORT NMM::OValueStream& operator<<(NMM::OValueStream& ov, const ITimeshifting::Mode& t);
  DLLEXPORT NMM::IValueStream& operator>>(NMM::IValueStream& iv, ITimeshifting::Mode& t);
  DLLEXPORT NMM::OValueStream& operator<<(NMM::OValueStream& ov, const ITimeshifting::PlayMode& t);
  DLLEXPORT NMM::IValueStream& operator>>(NMM::IValueStream& iv, ITimeshifting::PlayMode& t);

  /** Time shifting listener.
   * Provides methods for Events sent by TimeshiftingNodes.
   */

  class DLLEXPORT ITimeshiftingListener : virtual public NMM::Interface { 
  public:
    ITimeshiftingListener() : NMM::Interface(ITimeshiftingListener::ITimeshiftingListener_type, "NMM") { };

    ITimeshiftingListener(NMM::NMMObject& parent) : NMM::Interface (parent, ITimeshiftingListener::ITimeshiftingListener_type, "NMM") { };

    virtual ~ITimeshiftingListener() {};

    static const char* ITimeshiftingListener_type;

    virtual  string getInterfaceType() const { return ITimeshiftingListener_type; }

    NMM::Interface* copy() const { return new ITimeshiftingListener(*this); }
    static void used(); 
    static const char* timeshiftingBegin_event;
    static NMM::Event* create_timeshiftingBegin();
    static const char* live_event;
    static NMM::Event* create_live();
    static const char* setProgress_event;
    static NMM::Event* create_setProgress(const Time& ts_time);
  /* instream (protected) and non-instream (public) */        /** Time shifting begins.
     * This method signals, that rewinding the timeshifting buffer has
     * reached its beginning and so, the TimeshiftingNode has changed
     * its internal state from ITimeshifting::REWIND to
     * ITimeshifting::PLAY.
     */

    virtual Result timeshiftingBegin() /* in-stream */ 
      throw(const NMM::SerializedException&);
        /** Live mode.
     * this method signals, that fast-forwarding the timeshifting
     * buffer has reached its end and so, the TimeshiftingNode has
     * changed its internal state from ITimeshfting::FAST_FORWARD to
     * ITimeshifting::FB_LIVE.
     */

    virtual Result live() /* in-stream */ 
      throw(const NMM::SerializedException&);
        /** Set progress.
     * TimeshiftingNodes send progress as a absolute Time, which is
     * the Time of the stream currently displayed.
     * @param ts_time The current Time of the (timeshifted) stream.
     */

    virtual Result setProgress(const Time& ts_time) /* in-stream */ 
      throw(const NMM::SerializedException&);


  };

  typedef auto_ptr<ITimeshiftingListener> ITimeshiftingListener_var;




}

#include "nmm/nmm_suffix.hpp"

#endif
