/* 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_GRAPHDESCRIPTION_HPP
#define NMM_GRAPHDESCRIPTION_HPP

#include "nmm/nmm_prefix.hpp"

#include "EdgeDescription.hpp"
#include "nmm/multimedia/NodeDescription.hpp"

#include "nmm/Exception.hpp"

#include "ISession.hpp"

#include "visibility.hpp"

namespace NMM {

  class ISession;
  class InstreamChannel;
  class INode;
  class BindingException;

  /** A GraphDescription describes a flow graph with nodes that should be connected.
   */
  class DLLEXPORT GraphDescription  : public SerializedValue {
  public:
    /*! @name Constructor & Destructor
     */
    //@{
    /** Constructor.
     */ 
    GraphDescription();

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

    /** Copy constructor.
     *
     * @param gd GraphDescription to copy
     */
    GraphDescription(const GraphDescription& gd);

    /** Virtual copy constructor.
     *
     * @return pointer to copy of this GraphDescription
     */
    virtual GraphDescription* copy() const { return new GraphDescription(*this); }
    //@}

    /** Creates a new GraphDescription specfifies the same flow graph but does not include the Nodes of the original GraphDescription. 
     * This new GraphDescription can be used to request a flow graph using the same nodes as the originial GraphDescription. 
     *
     * @return GraphDescription that specfifies the same flow graph
     */
    GraphDescription getSkeleton() const;

    /** Tests edges of GraphDescriptions have the same overlap type.
     *
     * Returns true if all edges of this GraphDescription has the same overlap type as the given GraphDescription, otherwise false. 
     *
     * @param gd GraphDescription to compare with this one
     * @return true if all edges of this GraphDescription has the same overlap type as the given GraphDescription, otherwise false. 
     */
    bool isIdenticalOverlap(const GraphDescription& gd) const;

    /** Clears all entries of the graph description. 
     *
     * Allocated Nodes are not released by this method!
     */
    void clear();


    /** Adds a Node specified by a NodeDescription. 
     *
     * @param nd NodeDescription that specifies the Node. 
     */
    void addNode(NodeDescription& nd);

    /** Assigns a NodeDescription the INode interface to the corresponding Node. 
     *
     * @param nd NodeDescription that describes the Node. 
     * @param inode INode interface to the corresponding Node.
     */
    void setINode(const NodeDescription& nd, const INode& inode);

    /** Returns the INode interface for a given NodeDescription. 
     *
     * @param nd NodeDescription of the Node whose INode interface should be returned.
     * @return INode interface of corresponding Node, or NULL if no INode interface exists. 
     */
    INode* getINode(const NodeDescription& nd);

    /** Returns the INode interface for a given NodeDescription. 
     *
     * @param nd NodeDescription of the Node whose INode interface should be returned.
     * @return INode interface of corresponding Node, or NULL if no INode interface exists. 
     */
    const INode* getINode(const NodeDescription& nd) const;

    /** Returns the INode interface for a given NodeDescription. 
     *
     * @param nd NodeDescription of the Node whose INode interface should be returned.
     * @return INode interface of corresponding Node, or NULL if no INode interface exists. 
     */
    INode* getINode(const NodeDescription* nd);

    /** Returns the INode interface for a given NodeDescription. 
     *
     * @param nd NodeDescription of the Node whose INode interface should be returned.
     * @return INode interface of corresponding Node, or NULL if no INode interface exists. 
     */
    const INode* getINode(const NodeDescription* nd) const;

    /** Returns the NodeDescription that was added first to a GraphDescription. 
     *
     * @return the NodeDescription that was added first to a GraphDescription. 
     */
    const NodeDescription& getFirstNodeDescription()          const;

    /** Returns a list of ID's of outgoing edges of a certain edge. 
     * 
     * @param i specifies the ID of the edge. 
     * @return list of ID's of outgoing edges.
     */
    const list<int>& getOutEdgeList(int i)            const; 

    /** Returns a list of ID's of incoming edges of a certain edge. 
     * 
     * @param i specifies the ID of the edge. 
     * @return list of ID's of incoming edges.
     */
    const list<int>& getInEdgeList(int i)            const; 

    /** Returns the i'th NodeDescription stored in the internal vector of this class. 
     * 
     * @param i specfies the position of the NodeDescription in the internal vector of this class. 
     * @return the NodeDescription 
     */
    const NodeDescription& getNodeDescription(unsigned int i)                   const;

    /** Returns the i'th NodeDescription stored in the internal vector of this class. 
     * 
     * @param i specfies the position of the NodeDescription in the internal vector of this class. 
     * @return the NodeDescription 
     */
    NodeDescription& getNodeDescription(unsigned int i);

    /** Returns the i'th EdgeDescription stored in the internal vector of this class. 
     * 
     * @param i specfies the position of the EdgeDescription in the internal vector of this class. 
     * @return the EdgeDescription
     */
    const EdgeDescription& getEdge(int i) const { return __edges.getRValue()[i]; }
  
    /** Returns the i'th EdgeDescription stored in the internal vector of this class. 
     * 
     * @param i specfies the position of the EdgeDescription in the internal vector of this class. 
     * @return the EdgeDescription
     */
    EdgeDescription& getEdge(int i)  { return __edges.getRValue()[i]; }

    /** Returns the amount of edges for this flow graph. 
     *  
     * @return the amount of edges for this flow graph. 
     */
    size_t getNumEdges() const { return __edges.getRValue().size(); }

    /** Adds edge specified by ID's of the NodeDescription
     *
     * @param nd_from ID of the NodeDescription of the Upstream node
     * @param out_tag Output jack tag
     * @param nd_to ID of the NodeDescription of Downstream node
     * @param in_tag Input jack tag
     * @param format connection format of the two nodes
     * @return The edge ID of the added edge
     */
    unsigned int addEdgeFromIDs(int nd_from, const string& out_tag, 
                                int nd_to  , const string& in_tag,
                                const Format* format);
  
    /** Adds edge specified by NodeDescriptions
     *
     * @param nd_from NodeDescription of the Upstream node
     * @param out_tag Output jack tag
     * @param nd_to NodeDescription of Downstream node
     * @param in_tag Input jack tag
     * @param format connection format of the two nodes
     * @return The edge ID of the added edge
     */
    unsigned int addEdge(const NodeDescription& nd_from, const string& out_tag, 
                         const NodeDescription& nd_to  , const string& in_tag, 
                         const Format* format = 0);
  
    /** Adds edge specified by NodeDescriptions
     *
     * @param nd_from NodeDescription of the Upstream node and default output jack tag
     * @param nd_to NodeDescription of Downstream node
     * @param in_tag Input jack tag
     * @param format connection format of the two nodes
     * @return The edge ID of the added edge
     */
    unsigned int addEdge(const NodeDescription& nd_from,  
                         const NodeDescription& nd_to  , const string& in_tag, 
                         const Format* format = 0);
  
    /** Adds edge specified by NodeDescriptions
     *
     * @param nd_from NodeDescription of the Upstream node
     * @param out_tag Output jack tag
     * @param nd_to NodeDescription of Downstream node using default input jack tag
     * @param format connection format of the two nodes
     * @return The edge ID of the added edge
     */
    unsigned int addEdge(const NodeDescription& nd_from, const string& out_tag, 
                         const NodeDescription& nd_to  ,  
                         const Format* format = 0);
  
    /** Adds edge specified by NodeDescriptions
     *
     * @param nd_from NodeDescription of the Upstream node using default input jack tag
     * @param nd_to NodeDescription of Downstream node using default input jack tag
     * @param format connection format of the two nodes
     * @return The edge ID of the added edge
     */
    unsigned int addEdge(const NodeDescription& nd_from, 
                         const NodeDescription& nd_to  ,  
                         const Format* format = 0);
  
    /** Adds edges using several NodeDescriptions. The nodes are connected in the specified order using default input and output jacks.
     *  
     * @param n1 1st NodeDescription of the flow graph 
     * @param n2 2nd NodeDescription of the flow graph 
     * @param n3 third NodeDescription of the flow graph 
     * @param n4 4th NodeDescription of the flow graph 
     * @param n5 5th NodeDescription of the flow graph 
     * @param n6 6th NodeDescription of the flow graph 
     * @param n7 7th NodeDescription of the flow graph 
     * @param n8 8th NodeDescription of the flow graph 
     * @param n9 9th NodeDescription of the flow graph 
     * @param n10 10th NodeDescription of the flow graph 
     * @param n11 11th NodeDescription of the flow graph 
     * @param n12 12th NodeDescription of the flow graph 
     */
    void addEdges(const NodeDescription* n1    , const NodeDescription* n2    , const NodeDescription* n3    , 
                  const NodeDescription* n4 = 0, const NodeDescription* n5 = 0, const NodeDescription* n6 = 0,
                  const NodeDescription* n7 = 0, const NodeDescription* n8 = 0, const NodeDescription* n9 = 0,
                  const NodeDescription* n10 = 0,const NodeDescription* n11 = 0,const NodeDescription* n12 = 0);


    /** Adds incoming edges of a Node that are not connected (e.g. for multiplexer nodes)
     * 
     * @param nd_to NodeDescription of the node
     * @param in_tag jack tag of the unconnected input jack of the Node
     * @param format Format of the Node on the corresponding input
     */
    unsigned int addInEdge(const NodeDescription& nd_to, const string& in_tag, 
                           const Format* format = 0);

    /** Adds incoming edges for an input of a Node that is not connected (e.g. for multiplexer nodes)
     * 
     * @param nd_to NodeDescription of the node
     * @param format Format of the Node on the default input jack
     */
    unsigned int addInEdge(const NodeDescription& nd_to,  
                           const Format* format = 0);

    /** Adds outgoing edge for an output of a Node that is not connected so far(e.g. for demultiplexer nodes)
     * 
     * @param nd_from NodeDescription of the node
     * @param out_tag jack tag of the unconnected output jack of the Node
     * @param format Format of the Node on the corresponding output
     */
    unsigned int addOutEdge(const NodeDescription& nd_from, const string& out_tag, 
                            const Format* format = 0);

    /** Adds outgoing edges of a Node that are not connected (e.g. for demultiplexer nodes)
     * 
     * @param nd_from NodeDescription of the node
     * @param format Format of the Node on the default output
     */
    unsigned int addOutEdge(const NodeDescription& nd_from,  
                            const Format* format = 0);
  
    /** Adds outgoing helper edge of a Node that are not connected (e.g. for demultiplexer nodes)
     * 
     * @param nd_from NodeDescription of the node
     * @param out_tag jack tag of the unconnected output jack of the Node
     * @param format Format of the Node on the corresponding output
     */
    unsigned int addOutHelpEdge(const NodeDescription& nd_from, const string& out_tag, 
                                const Format* format = 0);
  
    /** Searches the given edge and return its ID.
     *
     * @param nd_from Upstream node
     * @param out_tag Output jack tag
     * @param nd_to Downstream node
     * @param in_tag Input jack tag
     * @return The edge ID of the edge, if it exists, otherwise -1
     */
    int getEdgeID(const NodeDescription& nd_from, const string& out_tag,
                  const NodeDescription& nd_to  , const string&  in_tag) const;

    /** Searches the given edge and return its ID.
     * @param nd_from Upstream node
     * @param nd_to Downstream node
     * @param in_tag Input jack tag
     * @return The edge ID of the edge, if it exists, otherwise -1
     */
    int getEdgeID(const NodeDescription& nd_from,
                  const NodeDescription& nd_to  , const string&  in_tag) const;
  
    /** Searches the given edge and return its ID.
     * @param nd_from Upstream node
     * @param out_tag Output jack tag
     * @param nd_to Downstream node
     * @return The edge ID of the edge, if it exists, otherwise -1
     */
    int getEdgeID(const NodeDescription& nd_from, const string& out_tag,
                  const NodeDescription& nd_to) const;
  
    /** Searches the given edge and return its ID.
     * @param nd_from Upstream node
     * @param nd_to Downstream node
     * @return The edge ID of the edge, if it exists, otherwise -1
     */
    int getEdgeID(const NodeDescription& nd_from,
                  const NodeDescription& nd_to) const;
  
    /** Returns instream communication channel of the given graph edge.
     *
     * @param edge_id The edge ID
     * @return InstreamChannel for a specific edge. 
     */
    InstreamChannel* getInstreamChannel(unsigned int edge_id) const;
  
    /** Removes Edge from GraphDescription.
     *
     * @param edge_num number of edge to remove
     */
    void removeEdge(const int edge_num);

    /** Removes edge and all edges on subgraph following this edge from GraphDescription.
     *
     * @param edge_num number of edge to remove
     */
    void removeSubgraph(const int edge_num);

    /** Writes complete GraphDescription to debug output.
     */
    void showAll();

    /** Writes all edges to debug output.
     */
    void showEdges();

    /** Returns list of all NodeDescriptions.
     *
     * @return list of all NodeDescriptions
     */
    const SequenceValue<NodeDescription>& getNodeDescriptionList()   const { return __node_list; }
   
    /** Returns list of all NodeDescriptions.
     *
     * @return list of all NodeDescriptions
     */
    SequenceValue<NodeDescription>& getNodeDescriptionList()         { return __node_list; }


    static const char* GraphDescription_type_name; ///< type name of GraphDescription
    static const char* GraphDescription_type_ns;   ///< type namespace of GraphDescription

    /** Sets comment.
     *
     * @param comment comment related to GraphDescription
     */
    void   setComment(const string& comment) { __comment = comment; }

    /** Returns comment.
     *
     * @return comment comment related to GraphDescription
     */
    string getComment() { return __comment; }

    /** Builds up graph and brings all nodes to state INITIALIZED.
     */
    void realizeGraph() throw(const BindingException&, const SerializedException&);

    /** Builds up graph and brings all nodes to state INITIALIZED.
     *
     * Works analogue to realizeGraph, but takes connection formats from GraphDescription given by argument.
     *
     * @param master_gd GraphDescription to read connection formats from
     */
    void realizeHandoverGraph(const GraphDescription& master_gd) throw(const BindingException&, const SerializedException&);
    
    /** Builds up graph and brings all nodes to state INITIALIZED.
     *
     * Works analogue to realizeGraph, but tries to establish shared connection between nodes.
     */
    void realizeSharedGraph() throw(const BindingException&, const SerializedException&);

    /** Brings all nodes to state ACTIVATED.
     */
    void activateGraph();

    /** Brings all nodes to state STARTED.
     */
    void startGraph();

    /** Brings all nodes to state ACTIVATED.
     */
    void stopGraph();
    
    /** Brings all nodes to state CONSTRUCTED.
     */
    void unrealizeGraph();
                   
    /** Returns list of all node interfaces.
     *
     * @return list of all node interfaces
     */
    const SequenceValue<INode >& getINodeList() const { return __interface_list; }
                  
    /** Returns list of all node interfaces.
     *
     * @return list of all node interfaces
     */
    SequenceValue<INode >& getINodeList()       { return __interface_list; }

    /* Tests if all NodeDescriptions have given sharing type.
     *
     * @param s SharingType
     * @return true if all NodeDescriptions have given sharing type, false otherwise
     */
    bool isSharingType(NodeDescription::SharingType s);

    /** Returns request flag for given NodeDescription.
     *
     * @param nd NodeDescription to return its flag  
     */
    bool getRequestFlag(const NodeDescription& nd) const;

    /** Sets request flag for given NodeDescription.
     *
     * @param nd NodeDescription to return its flag  
     * @param flag value of flag to set
     */
    void setRequestFlag(const NodeDescription& nd, bool flag);

    /** Returns release flag for given NodeDescription.
     *
     * @param nd NodeDescription to return its flag  
     */
    bool getReleaseFlag(const NodeDescription& nd) const;

    /** Sets release flag for given NodeDescription.
     *
     * @param nd NodeDescription to return its flag  
     * @param flag value of flag to set
     */
    void setReleaseFlag(const NodeDescription& nd, bool flag);

    /** Sets ISession.
     *
     * @param session ISession to set
     */
    void setISession(const ISession& session);

    /** Returns ISession.
     *
     * @return ISession 
     */
    const ISession* getISession()  const  { return __i_session.getValue(); }
    
    /** Returns ISession.
     *
     * @return ISession 
     */
    ISession* getISession()         { return __i_session.getValue(); }

    /** Sets SinkSynchronizerDescription.
     *
     * @param od ObjectDescription of SinkSynchronizer
     */
    void setSinkSynchronizerDescription(const ObjectDescription& od);

    /** Returns SinkSynchronizerDescription.
     *
     * @return ObjectDescription of SinkSynchronizer
     */
    ObjectDescription getSinkSynchronizerDescription() { return __sink_synch_descr.getValue(); }

    /** Configures connection between a ProxyObject and it's NMMObject.
     *
     * This method configures the connection between a ProxyObject and it's NMMObject,
     * if a specific transport strategy is stored in the corresponding NodeDescription.
     */
    void configureOutOfBandBindings();

    /** This method connects all sink nodes nodes with the SinkSynchronizer provided by the Session.
     */
    void connectSinkSynchronizer();
  
    /** Enable/disable prefetching for sink nodes.
     *
     * @param enable true if prefetching should be enabled, false otherwise
     */
    void setPrefetchingEnabled(bool enable);
  
    /** Enable/disable intra-stream synchronization for sink nodes.
     *
     * @param enable true if intra-stream synchronization should be enabled, false otherwise
     */
    void setSinkSynchronizationEnabled(bool enable);
  
    /** This method removes all sink nodes nodes from the SinkSynchronizer provided by the Session.
     */
    void disconnectSinkSynchronizer();

    /** Returns list of partial overlapped nodes.
     *
     * @return list of partial overlapped nodes
     */
    const list<INode*> getPartialOverlappedNodes();

    /** Assigns given GraphDescription to this one.
     *
     * @param gd GraphDescription to assign to this one
     * @return reference to this GraphDescription
     */
    const GraphDescription& operator=(const GraphDescription& gd);


  private:
    /** Register member variables for serialization/deserialization.
     */
    void registerAttributes();

    /** Adds and configures TransportStrategies to InstreamChannel like described in EdgeDescription.
     *
     * @param c_channel communication channel to realize
     * @param edge EdgeDescription which describes configuration of communication channel
    */
    void realizeCommunicationChannel(InstreamChannel* c_channel, EdgeDescription& edge);
  
    /** Sends events contained in CEvent to given node.
     *
     * @param node Node to send events to
     * @param cevent CEvent with events to be sent
     */
    void sendNodeEvent(INode* node, CEvent& cevent);
  
  private:
    list<int> __empty_list;

    typedef map<int, list<int> >::iterator InternalIt ;
    typedef map<int, list<int> >::const_iterator CInternalIt ;

    TValue< vector<EdgeDescription> > __edges;  ///< vector of all EdgeDescriptions
    TValue< map<nmm_int32, list<nmm_int32> > > __out_edges; ///< map of pairs of node ids and outgoing edges
    TValue< map<nmm_int32, list<nmm_int32> > > __in_edges;  ///< map of paris of node ids and incoming edges

    SequenceValue< NodeDescription >      __node_list;        ///< stores the NodeDescription of the nodes
    SequenceValue< INode  >               __interface_list;   ///< stores the INode's of allocated nodes
    TValue< vector<nmm_bool> >                __request_flags;    ///< the requests flags specify wheter a node should be requested by server registry or not
    TValue< vector<nmm_bool> >                __release_flags;    ///< the release flags specify wheter a node should be releases or not. If this flag is false, no reference counting is performed.

    unsigned int                          __internal_counter; ///< internal counter which is set inside the NodeConfiguration
    TValue<ISession*>                     __i_session;        ///< stores the interface to the session object
    string                                __comment;          ///< stores the comment
    TValue<ObjectDescription>             __sink_synch_descr;///< object description of the sink synchronizer object to use

  };

  typedef auto_ptr<GraphDescription> GraphDescription_var;


} //end namespace
#include "nmm/nmm_suffix.hpp"

#endif
