/* 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_MESSAGE_HPP
#define NMM_MESSAGE_HPP NMM_MESSAGE_HPP

#include "nmm/nmm_prefix.hpp"

#include "Timestamp.hpp"

#include "nmm/utils/thread/ThreadMutex.hpp"

#include "nmm/comm/serialize/SerializedValue.hpp"
#include "nmm/comm/serialize/TValue.hpp"
#include "nmm/nmm_types.hpp"

#include "nmm/comm/serialize/SerializeUtilsTime.hpp"

#include "visibility.hpp"

namespace NMM {

  /** Superclass for objects that get transfered between nodes.
   */  
  class DLLEXPORT Message : public SerializedValue {
  public:
    enum Type{EVENT = 0, BUFFER, FLUSH_EVENT} ;

    enum Direction{DOWNSTREAM = 0, UPSTREAM, OUTOFBAND, UNDEF };

    /** Constructor.
     *
     * @param t message type
     */
    Message(Type t);

    /** CopyConstructor.
     *
     * @param message message to copy
     */
    Message(const Message& message);

    /** Destructor.
     */
    virtual ~Message();

    /** Sets a label for this buffer.
     * 
     * Labels can be used in muxed streams (e.g. a stream
     * of audio and video data running over a single jack)
     * to distinguish between the different buffer types
     * (e.g. between video and audio buffers)
     * Use an instream event to set the labels to meaningful
     * values (e.g. "video" is 0, "audio" is 1)
     */
    inline void setLabel(unsigned int l) { label = l; }

    /** Returns the label of a buffer.
     */
    inline unsigned int getLabel() const { return label.getValue(); }

    /** Sets mandatory bit.
     *
     * Set this to true, if you do not want this Buffer object
     * to get lost. Currently, this only prevents the Buffer object
     * to be discarded from a StreamQueue object. In the future,
     * network transmission, etc. will take care of this bit as well.
     *
     * @param m value of mandatory bit to set
     */
    inline void setMandatoryBit(bool m)                  { mandatory.setValue(m); }

    /** Sets direction.
     *
     * Sets the direction where this message should will sent to
     * UPSTREAM or DOWNSTREAM.
     *
     * @param d direction to set
     */
    inline void setDirection(Direction d) { direction.setValue(d); }

    /** Sets the buffers timestamp.
     * 
     * @param t timestamp to set
     */
    inline void setTimestamp(const Timestamp& t)          { time.setValue(t); time.getRValue().is_set = true; }

    /** Set the start time for delay measurement.
     *
     * @param t start time for delay measurement 
     */
    inline void setDelayStartTime(const Time& t)          { delay_time.setValue(t); }

    /** Unset the start time for delay measurement.
     */
    inline void unsetDelayStartTime()                     { delay_time.setValue(Time()); }

    /** Releases a buffer.
     *
     * The method to release a buffer. The Buffer automatically
     * informs its own Allocator (stored in allocator) that
     * it can be reused as soon as the reference count reaches 0.
     * This method reduces the reference count by one. If the
     * reference count reaches 0, the Buffer should also reset its
     * used byte count.
     */
    void release()                          const;

    /** Increases reference count.
     *
     * Increase the reference count. Used when the buffer is
     * multicast.
     *
     * @param by The number to increase the count by.
     */
    void increaseCount (unsigned int by = 1) const;

    /** Returns the status of the mandatory bit.
     *
     * @return status of the mandatory bit
     */
    inline bool getMandatoryBit()                  const { return mandatory.getRValue(); }

    /** Returns the direction.
     * 
     * Returns the direction, i.e. UPSTREAM or DOWNSTREAM.
     *
     * @return message direction
     */
    inline Direction getDirection()                const { return direction.getRValue(); }

    /** Gets current reference count.
     *
     * Gets the current reference count. 0 means that this buffer
     * is currently not in use.
     *
     * @return current reference count
     */
    inline int getCount ()                         const { return ref_count; }

    /** Returns the Message type.
     *
     * @return Message type
     */
    inline Type getType()                          const { return type.getRValue(); }

    /** Returns the Messages's timestamp.
     *
     * @return Messages's timestamp
     */
    inline const Timestamp& getTimestamp ()        const {return time.getRValue();}

    /** Get the start time for delay measurement.
     *
     * @return The start time for delay measurement if it is set
     */
    inline const Time& getDelayStartTime()         const { return delay_time.getRValue(); }

    /** Check if the start time for delay measurement is set.
     * 
     * @return @c true if the start time for delay measurement is set, otherwise @c false
     */
    inline bool isDelayStartTimeSet()              const { return delay_time.getRValue() != Time(); }

    /** Compares the synchronization time of two events.
     *
     * @param e Event to compare synchronization time of this event with
     * @return true if this Event's synchronization time is smaller, false otherwise.
     */
    bool operator<(const Message& e)               const { return getTimestamp().sync_time < e.getTimestamp().sync_time;}

    /** Assignment operator.
     *
     * Assigns direction, timestamp, mandatory bit and delay start time and returns reference to this Message.
     *
     * @param m Message to take named values from
     * @return reference to this message
     */
     Message& operator=(const Message& m);

    /** Copy this Message.
     *
     * @return pointer to copy of this message
     */
    Message* copy() const = 0;

  protected:
    /** Erase this message.
     *
     * This method is called, if ref_count is 0.
     */
    virtual void erase() const = 0;

    /** Set Message type.
     *
     * @param new_type type to set
     */
    void setType(Type new_type);

  private:

    /** Register member variables for serialization/deserialization.
     */
    void registerValues();

  private:
    TValue<Direction> direction;  ///< direction this message is sent
    TValue<Type> type;            ///< type of this message
    TValue<Timestamp> time;       ///< Timestamp.
    TValue<nmm_bool> mandatory;   ///< Mandatory bit.
    TValue<nmm_uint32> label;     ///< Label of the message for muxed streams
    TValue<Time> delay_time;      ///< Timestamp for measuring delays in the flowgraph

    mutable int ref_count;
    ThreadMutex mutex;
  };

  /** Write Message type to OValueStream.
   *
   * @param ov OValueStream to write to
   * @param t message type 
   * @return reference to used OValueStream
   */
  OValueStream& operator<<(OValueStream& ov, const Message::Type& t);

  /** Read Message type from IValueStream.
   *
   * @param iv IValueStream to read from
   * @param t message type 
   * @return reference to used IValueStream
   */
  IValueStream& operator>>(IValueStream& iv, Message::Type& t);

  /** Write Message direction to OValueStream.
   *
   * @param ov OValueStream to write to
   * @param t message direction
   * @return reference to used OValueStream
   */
  OValueStream& operator<<(OValueStream& ov, const Message::Direction& t);

  /** Write Message direction to IValueStream.
   *
   * @param iv IValueStream to write to
   * @param t message direction
   * @return reference to used IValueStream
   */
  IValueStream& operator>>(IValueStream& iv, Message::Direction& t);

}
#include "nmm/nmm_suffix.hpp"

#endif
