/* 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_NODE_DESCRIPTION_HPP
#define NMM_NODE_DESCRIPTION_HPP NMM_NODE_DESCRIPTION_HPP

#include "nmm/nmm_prefix.hpp"

#include "nmm/comm/ObjectDescription.hpp"

#include "nmm/comm/format/Format.hpp"
#include "nmm/comm/format/Property.hpp"

#include "INode.hpp"

#include "visibility.hpp"

namespace NMM {

  /** Description of a node.
   * This class describes an NMM node. It is used for requesting nodes from a registry
   * and for identifying an existing node in a graph description.
   *
   * The node description contains information such as the node name, node type, supported formats,
   * supported interfaces and location (host name) of the node. All of these parameters are optional
   * and unspecified by default.
   *
   * Configuration events can be attached to the node description to describe which out-of-band events
   * are to be sent to the node in which states. These configuration events are automatically sent
   * to the node when the graph is realized by the registry.
   *
   * Session sharing support is incorporated into this class by sharing types. For a node, the sharing
   * type specifies whether the node may be shared by other applications and what strategy should be
   * used for requesting the node (e.g. request a new node or try to share an existing sharable node?)
   *
   * @see INode, GraphDescription
   */
  class DLLEXPORT NodeDescription : public ObjectDescription {
  public:
    /** Sharing type of a node.
     */
    enum SharingType {
      UNDEFINED = -1,                   ///< Undefined sharing type
      EXCLUSIVE = 0,                    ///< Always request a new node, disallow sharing
      EXCLUSIVE_THEN_SHARED,            ///< Always request a new node, allow sharing
      EXCLUSIVE_OR_SHARED,              ///< First try EXCLUSIVE. If this fails, try SHARED.
      SHARED_OR_EXCLUSIVE,              ///< First try SHARED. If this fails, try EXCLUSIVE.
      SHARED,                           ///< Always request a shared node
      EXCLUSIVE_THEN_SHARED_OR_SHARED,  ///< First try EXCLUSIVE_THEN_SHARED. If this fails, try SHARED
      SHARED_OR_EXCLUSIVE_THEN_SHARED   ///< First try SHARED. If this fails, try EXCLUSIVE_THEN_SHARED
    };

    /** @name Constructor & Destructor
     */
    //@{
    /** Create a new node description with default attributes.
     * All optional parameters are unspecified. The sharing type is @c EXCLUSIVE_THEN_SHARED.
     */
    NodeDescription();

    /** Create a new node description from a node name.
     * This constructor conveniently creates a new node description from a node name.
     * All other optional parameters are unspecified. The sharing type is @c EXCLUSIVE_THEN_SHARED.
     * @param node_name Name of the node
     */
    explicit NodeDescription(const string& node_name);

    /** Destructor.
     */
    ~NodeDescription();

    /** Copy constructor.
     *
     * @param nd An existing node description
     */
    NodeDescription(const NodeDescription& nd);

    /** Virtual copy constructor.
     *
     * @return A pointer to a dynamically allocated copy of this object, which has the same type
     */
    virtual NodeDescription* copy() const { return new NodeDescription(*this); }
    //@}

    /** @name Attribute Access
     */
    //@{
    /** Set the node name.
     * This method sets the node name. An empty string means that the node name is not specified.
     *
     * @param node_name The node name (e.g. "DevNullNode") or an empty string
     */
    inline void setNodeName       (const string& node_name)     { setObjectName(node_name); }

    /** Set the node type.
     * This method sets the node type. A value of INode::UNKNOWN means that the node type is not specified.
     *
     * @param node_type The node type (e.g. INode::CONVERTER) or INode::UNKNOWN
     */
    inline void setNodeType       (INode::Type node_type)       { __node_type.setValue(node_type); }

    /** Set the URL.
     * This method sets the URL. This attribute is used by the GraphBuilder.
     * An empty string means that the URL is not specified.
     *
     * @param url The URL or an empty string
     */
    inline void setURL            (const string& url)           { __url.setValue(url); }

    /** Set the input property.
     * This method sets the set of input formats that the node supports, represented as a Property object.
     * A property which contains no formats indicates that the input property is not specified.
     *
     * @param property The input property
     */
    inline void setInputProperty  (const Property& property)    { __input_property.setValue(property); }

    /** Set the output property.
     * This method sets the set of output formats that the node supports, represented as a Property object.
     * A property which contains no formats indicates that the output property is not specified.
     *
     * @param property The output property
     */
    inline void setOutputProperty (const Property& property)    { __output_property.setValue(property); }

    /** Add an input format to the input property.
     * This method adds an input format which is supported by the node to the input property.
     *
     * @param format An input format that is supported by the node
     */
    inline void addInputFormat    (const Format& format)        { __input_property.getRValue().addNewFormat(new Format(format)); }

    /** Add an input format to the input property.
     * This method adds an input format which is supported by the node to the input property.
     *
     * @param format An output format that is supported by the node
     */
    inline void addOutputFormat   (const Format& format )       { __output_property.getRValue().addNewFormat(new Format(format));}

    /** Add an input format and an output format.
     * This method is equivalent to the following sequence of method calls:
     * @code
     * addInputFormat(input_format);
     * addOutputFormat(output_format);
     * @endcode
     *
     * @param input_format An input format that is supported by the node
     * @param output_format An output format that is supported by the node
     */
    inline void addFormat(const Format& input_format, const Format& output_format) { addInputFormat(input_format); addOutputFormat(output_format); }

    /** Get the node name.
     * This method gets the node name. An empty string means that the node name is not specified.
     *
     * return The node name (e.g. "DevNullNode") or an empty string
     */
    inline const string& getNodeName()          const   { return getObjectName(); }

    /** Get the node type.
     * This method gets the node type. A value of INode::UNKNOWN means that the node type is not specified.
     *
     * @return The node type (e.g. INode::CONVERTER) or INode::UNKNOWN
     */
    inline INode::Type getNodeType()            const   { return __node_type.getRValue(); }

    /** Get the URL.
     * This method gets the URL. This attribute is used by the GraphBuilder.
     * An empty string means that the URL is not specified.
     *
     * @return The URL or an empty string
     */
    inline string getURL()            const   { return __url.getRValue(); }

    /** Check for inclusion.
     * This method checks if this given node description is a subset of the given node description, i.e.
     * if all nodes matching this description also match the other description.
     *
     * @param nd A node description
     * @return @c true of this node description is a subset of @c nd, @c false otherwise
     */
    bool isSubset(const NodeDescription& nd)    const;

    /** Set the distribution flag.
     * This method sets the flag which indicates that this node can be distributed.
     *
     * @param flag @c true if the node can be distributed, @c false otherwise
     *
     * @deprecated The distribution flag is no longer used and has no meaning
     */
    void setDistributionFlag(bool flag);

    /** Get the distribution flag.
     * This method gets the flag which indicates that this node can be distributed.
     *
     * @return @c true if the node can be distributed, @c false otherwise
     *
     * @deprecated The distribution flag is no longer used and has no meaning
     */
    bool getDistributionFlag();

    /** Get the input property.
     * This method gets the set of input formats that the node supports, represented as a Property object.
     * A property which contains no formats indicates that the input property is not specified.
     *
     * @return The input property
     */
    inline const Property& getInputProperty()   const   { return __input_property.getRValue(); }

    /** Get the input property.
     * This method gets the set of input formats that the node supports, represented as a Property object.
     * A property which contains no formats indicates that the input property is not specified.
     *
     * @return The input property
     */
    inline       Property& getInputProperty()           { return __input_property.getRValue(); }

    /** Set the output property.
     * This method sets the set of output formats that the node supports, represented as a Property object.
     * A property which contains no formats indicates that the output property is not specified.
     *
     * @return The output property
     */
    inline const Property& getOutputProperty()  const   { return __output_property.getRValue(); }

    /** Set the output property.
     * This method sets the set of output formats that the node supports, represented as a Property object.
     * A property which contains no formats indicates that the output property is not specified.
     *
     * @return The output property
     */
    inline       Property& getOutputProperty()          { return __output_property.getRValue(); }
    //@}

    /** Set the internal ID of this node description.
     *
     * @param id The internal ID
     */
    void setInternalID(int id)                          { __internal_id.setValue(id); }

    /** Get the internal ID of this node description.
     *
     * @return The internal ID
     */
    int getInternalID()                           const { return __internal_id.getRValue(); }

    /** Get the sharing type of this node description.
     *
     * @return Sharing type of this node description
     */
    NodeDescription::SharingType getSharingType() const { return __sharing_type.getValue(); }

    /** Set the sharing type of this node description.
     *
     * @param s_type Sharing type of this node description
     */
    void setSharingType(NodeDescription::SharingType s_type) { __sharing_type.setValue(s_type); }

    /** Check if this node allows sharing.
     * This method is a convenience method which checks whether the node allows sharing, based on its sharing type.
     * The following sharing types allow sharing:
     *
     * - EXCLUSIVE_OR_SHARED
     * - SHARED_OR_EXCLUSIVE
     * - SHARED
     * - EXCLUSIVE_THEN_SHARED
     *
     * @return @c true if the node allows sharing, @c false otherwise
     */
    bool allowsSharing() const;

    /** Add a configuration event to the node description.
     * This method adds an event to the configuration of the node in a specific state.
     * The event is sent to the node automatically when the graph is realized by the registry.
     *
     * For example,
     * @code
     * NodeDescription nd;
     * Event e(IFileHandler::setFilename, new TInValue<string>("file.wav"));
     * nd.addNodeEvent(event, INode::INITIALIZED);
     * @endcode
     * causes the event IFileHandler::setFilename("file.wav") to be sent to the node
     * in state INITIALIZED.
     *
     * This feature can only be used if the registry realizes the graph automatically.
     * If the graph is realized manually by the application, then configuration events
     * in the node description have no meaning.
     *
     * @param event Configuration event to add
     * @param state State of the node
     */
    void addNodeEvent(const Event& event, INode::State state);

    /** Get the configuration of the node in the given state.
     * This method returns a composite event which contains all configuration events
     * to be sent to the node in the given state.
     * The returned CEvent object must not be deleted. The pointer stays valid until
     * this node description object is destroyed.
     *
     * @param state State of the node
     * @return The configuration of the node in the given state
     */
    CEvent* getNodeEvent(INode::State state);

    /** Get the configuration of the node in the given state.
     * This method returns a composite event which contains all configuration events
     * to be sent to the node in the given state.
     * The returned CEvent object must not be deleted. The pointer stays valid until
     * this node description object is destroyed.
     *
     * @param state State of the node
     * @return The configuration of the node in the given state
     */
    const CEvent* getNodeEvent(INode::State state) const;

  private:
    /** register all attibutes by the base class SerializedValue
     */
    void registerAttributes();

    /** checks if the second property is a subset of the first
     */
    bool isPropertySubset(const Property&, const Property&) const;

  private:
    TValue<string>                  __url;                  ///< URL used by GraphBuilder
    TValue<INode::Type>             __node_type;            ///< Node type
    TValue<Property>                __input_property;       ///< Supported input formats
    TValue<Property>                __output_property;      ///< Supported output formats
    TValue<nmm_int32>               __internal_id;          ///< Internal ID in the GraphDescription
    TValue<SharingType>             __sharing_type;         ///< Sharing type
    SequenceValue< CEvent >         __config_events;        ///< Configuration events for all states
    TValue<nmm_bool>                __distribution_flag;    ///< Distribution flag (unused)
  };

  /** Output serialization operator for NodeDescription::SharingType.
   * This object serializes an object using an output value stream.
   *
   * @param ov Output value stream
   * @param desc The object to serialize
   * @return ov
   */
  DLLEXPORT NMM::OValueStream& operator<<(NMM::OValueStream& ov, const NMM::NodeDescription::SharingType& desc);

  /** Input serialization operator for NodeDescription::SharingType.
   * This object deserializes an object using an input value stream.
   *
   * @param iv Input value stream
   * @param desc The object to deserialize
   * @return iv
   */
  DLLEXPORT NMM::IValueStream& operator>>(NMM::IValueStream& iv, NMM::NodeDescription::SharingType& desc);

  /** Output serialization operator for list<NodeDescription>.
   * This object serializes an object using an output value stream.
   *
   * @param ov Output value stream
   * @param desc The object to serialize
   * @return ov
   */
  DLLEXPORT NMM::OValueStream& operator<<(NMM::OValueStream& ov, const list<NMM::NodeDescription>& desc);

  /** Input serialization operator for NodeDescription::SharingType.
   * This object deserializes an object using an input value stream.
   *
   * @param iv Input value stream
   * @param desc The object to deserialize
   * @return iv
   */
  DLLEXPORT NMM::IValueStream& operator>>(NMM::IValueStream& iv, list<NMM::NodeDescription>& desc);

  /** Scoped pointer to a NodeDescription object.
   */
  typedef auto_ptr<NodeDescription> NodeDescription_var;
}

/** Output serialization operator for list<NodeDescription>.
 * This object serializes an object using an output value stream.
 *
 * @param ov Output value stream
 * @param desc The object to serialize
 * @return ov
 */
DLLEXPORT NMM::OValueStream& operator<<(NMM::OValueStream& ov, const vector<NMM::NodeDescription>& desc);

/** Input serialization operator for NodeDescription::SharingType.
 * This object deserializes an object using an input value stream.
 *
 * @param iv Input value stream
 * @param desc The object to deserialize
 * @return iv
 */
DLLEXPORT NMM::IValueStream& operator>>(NMM::IValueStream& iv, vector<NMM::NodeDescription>& desc);


#include "nmm/nmm_suffix.hpp"

#endif
