/* 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_REFERENCE_MANAGER_HPP
#define NMM_REFERENCE_MANAGER_HPP NMM_REFERENCE_MANAGER_HPP

#include "nmm/nmm_prefix.hpp"

#include "nmm/utils/NamedObject.hpp"
#include "nmm/utils/Singleton.hpp"

#include "NMMReference.hpp"
#include "UserInfo.hpp"

#include <list>
#include <string>

#include "visibility.hpp"

namespace NMM {
  
  class NMMObject;
  class ProxyObject;
  class Interface;
  
  /** \class ReferenceManager
   * This class manages all local NMMObjects and stores their NMMReferences wich are created by
   * this class. The NMMReference is unique in this address space and is a 64-Bit integer.
   * If a NMMReference is known the corresponding NMMObject can be requested. The
   * registration of the NMMReferences is done by the NMMObjects.
   *
   * This class is also used to create the ProxyObject and can be requested from an interface.
   * If the ProxyObject for a distributed NMMObject exist, it is returned, otherwies it is allocated.
   *
   * This class can also be used as a simple Naming service. An application or an object can register
   * any NMMObject with a unique name (string) that can be used to find the Object. So the caller
   * must not know the NMMReference. For example this is used to connect to a ServerRegistry, because
   * the NMMReference is unknown after of this object. The ServerRegistry can than be used to search other objects.
   */
  class DLLEXPORT ReferenceManager : public NamedObject {
    friend class CreateUsingNew<ReferenceManager>;
	DECLARE_SINGLETON(ReferenceManager, CreateUsingNew, DefaultLifetime, MultiThreaded);
  public:
    /** @name Register and remove NMMObjects and ProxyObjects
     */
    //@{
    /** This method is used to register NMMObjects and is automatically called by each
     * NMMObject in the constructors. It returns the NMMReference of the object. This method
     * is for internal use!
     *
     * @param object The NMMObject that should be registered
     * @return NMMReference for registered NMMObject
     */
    NMMReference registerNMMObject(NMMObject* object);

    /** This method is used to unregister NMMObjects. 
     *
     * This method is automatically called by each
     * NMMObject in its destructors. This method is for internal use!
     *
     * @param ref The NMMReference of the NMMObject
     */
    void unregisterNMMObject(const NMMReference& ref);

    /** This method is used by an interfaces to request a ProxyObject.
     *
     * If the ProxyObject don't exist it is created. The needed informations are stored in the interface.
     *
     * @param _interface The Interface with the information
     * @return pointer to requested ProxyObject
     */
    ProxyObject* requestProxyObject(const Interface& _interface);

    /** This method removes an ProxyObject from the map. 
     *
     * This method is called in the destructor of the ProxyObject.
     *
     * @param po The ProxyObject that is invalid.
     * @param u_info The UserInfo of the application from the NMMObject
     */
    void unregisterProxyObject(ProxyObject& po, const UserInfo& u_info);
    //@}

    /** @name Access NMMObject's
     */
    //@{
    /** This method returns the NMMObject of the given NMMReference.
     *
     * @param ref The NMMReference of the NMMObject
     * @return pointer to requested NMMObject
     */
    NMMObject* getNMMObject(const NMMReference& ref);

    /** This method returns the NMMObject of the given NMMReference.
     *
     * @param ref The NMMReference of the NMMObject
     * @return pointer to requested NMMObject
     */
    const  NMMObject* getNMMObject(const NMMReference& ref) const;

    /** This method returns the NMMObject of the given NMMReference and performs a dynamic_cast to given type T.
     *
     * @param ref The NMMReference of the NMMObject
     * @return NMMObject casted to type T
     */
    template <class T>
    T* getObject(const NMMReference& ref)                   { return dynamic_cast<T*>(getNMMObject(ref)); }

    /** This method returns the NMMObject of the given NMMReference and performs a dynamic_cast to given type T.
     *
     * @param ref The NMMReference of the NMMObject
     * @return NMMObject casted to type T
     */
    template <class T>
    const T* getObject(const NMMReference& ref)       const { return dynamic_cast<T*>(getNMMObject(ref)); }
    //@}

    /** @name Service-methods 
     */
    //@{
    /** This method can be used to register an NMMObject with an unique service name.
     *
     * @param service_name The unique service name that should be used to access the NMMObject
     * @param object The NMMObject that should be returned for the service-name
     */
    void registerService(const string& service_name, NMMObject* object);

    /** This method removes a specific service from the internal service map. 
     *
     * The corresponding NMMObject is no longer returned.
     * @param service The name of the service that should be removed
     */
    void removeService(const string& service);

    /** This method returns a NMMObject that is previously registered with the given name.
     *
     * This method returns a NMMObject that is previously registered with the given name, or NULL if no such service-name is available.
     *
     * @param service The name of the service
     * @return NMMObject that is previously registered with the given name
     */
    NMMObject* getService(const string& service);

    /** This method returns a list of names that are registered as service.
     *
     * @return list of names that are registered as service
     */
    list<string> getServiceList() const;
    //@}

  private: 
    /** Allocates ProxyObject.
     *
     * Allocates ProxyObject with partner object given by interface and auto delete enabled.
     * 
     * @param _interface interface to partner object
     * @return pointer to allocated ProxyObject
     */
    ProxyObject* allocateProxyObject(const Interface& _interface);

  private:
    /** This stores the ProxyObjects for NMMObjects that run in a specific process. 
     * To identify this process a UserInfo-struct is used.
     */
    struct InternalProxyStruct {
      InternalProxyStruct(const UserInfo& arg_user_info) : user_info(arg_user_info) {}
      UserInfo user_info;                               // stores the UserInfo to specify a certain process
      map<NMMReference, ProxyObject*> proxy_map;        // stores the ProxyObjects for NMMObjects that run in a specific process. 
    };

  private:

    /** @name Constructor & Destructor
     */
    //@{
    /** Constructor.
     */
    ReferenceManager();

    /** Copy constructor.
     *
     * @param ref ReferenceManager to copy.
     */
    ReferenceManager(const ReferenceManager& ref);

    /** Destructor.
     */
    ~ReferenceManager();
    //@}
    typedef map<NMMReference, NMMObject*>::iterator InternalIt;
    typedef map<NMMReference, NMMObject*>::const_iterator CInternalIt;

    typedef map<string, NMMObject*>::iterator ServiceIt;
    typedef map<string, NMMObject*>::const_iterator CServiceIt;

    typedef list<InternalProxyStruct>::iterator ProxyIt;
    typedef list<InternalProxyStruct>::const_iterator CProxyIt;


    map<NMMReference, NMMObject*>     __object_map;     ///< stores the NMMObjects that run in this process
    list<InternalProxyStruct>         __proxy_map;      ///< stores the used ProxyObjects
    map<string, NMMObject*>           __service_map;    ///< stores the services available an this system
    static NMMReference               __reference_counter; ///< calculates the NMMReference that should be used.
  };

  /* Typedef to access the ReferenceManager with the method call SReferenceManager::getInstance() */
  typedef ReferenceManager::Singleton SReferenceManager;
  
}

#include "nmm/nmm_suffix.hpp"

#endif
