/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * The iWear Framework 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 as in version 2 of the License.

 * 
 * The iWear Framework 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
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWREMOTE_ROOTARTANO_H
#define __IWREMOTE_ROOTARTANO_H

#include <iwremote/remoteconnection.h>
// we need to include remoteconnection first otherwise boost::multi_index would
// not work
#include <iwear/uid.h>
#include <iwremote/protocol.h>
#include <iwremote/remotedescriptor.h>
#include <iwremote/remote_enums.h>
#include <boost/intrusive_ptr.hpp>

#include <vector>
#include <map>

using std::multimap;

namespace iwear {
class ServiceManager;
class EventDispatcher;
    namespace net {

// some forward declarations
class CallManager;
class RemoteObject;
class RemoteConnection;
class RPCStream;
class HostConnector;

/**
 * The RootArtano is the key class from which people will ask for
 * remoteobjects, and pass their remoteobjects or references to it to be
 * destroyed. They should never need to interface with the CallManager or
 * CallProviders or similar classes.
 * It will setup the CallManager as well as everything thats needed for the
 * others, like SSL configuration locations etc. The configuration itself will
 * of course be read by the classes themselves.
 * @note We *need* some myth in the core of a program. Artano is elvish for
 * "highest blacksmith", so this class can be seen as the one and only master
 * class that will create objects.
 */
class RootArtano : public ThreadLocked
{
friend class CallManager;
private:
protected:
    /**
     * This is the CallManager we do all our calls on.
     */
    CallManager* cman;

    /**
     * This is the cache of currently existing and associated remote objects we
     * have.
     */
    typedef map<const oid_t*,RemoteObject*,deref_less<const oid_t*> > ro_cache_t;

    ro_cache_t rmos;

    RemoteObject* ro_cache_search( const oid_t& oid );
    RemoteObject* ro_cache_search( const oid_t& oid, cid_t cid );
    RemoteObject* ro_cache_search( const oid_t& oid, const hostid_t& hid );


    /**
     * Here we look for the local-server RemoteObjects which are created in the
     * system, and might be offered to outside systems.
     * @todo TODO wenn adding to this, check if the uid is not duped.
     */
    map<const oid_t*, RemoteObject*,deref_less<const oid_t*> > oid_object;

    /**
     * Here we cache local-server objects implementing a certain service. First
     * one added here will be served on request.
     */
    set<pair<cid_t,RemoteObject*> > cid_local_object;

    /**
     * We save here the pairs of objects we have associated according to their
     * class ids, i.e. what they implement.
     */
    map<pair<cid_t,RemoteObject*>,RemoteDescriptor > cid_object;

    /**
     * This map is a lookup for the client side so that we can ask for a cid,
     * and if its associatable we get the proper client class returned.
     */
    static map<cid_t,RemoteObject*(*)(CallManager*)>* cid_inst;

    /**
     * This is a multimap storing multiple possible instantiations for
     * dynamically allocatable servers
     */
    static multimap<cid_t,RemoteObject*(*)()>* server_inst;

    void send_deassociate( RemoteConnectionPtr rc, RemoteObject* ro );

    /**
     * @warning After a call to this the RemoteConnection might not be the
     * responsible for this object any more, or maybe even completely invalid
     * or deleted !  So, don't use it any more then.
     */
    RemoteObject* associate_on_connection( RemoteConnectionPtr rc, const oid_t& id, resolve_behaviour rb );

    RemoteObject* associate_on_connection( RemoteConnectionPtr rc, uint32_t cid, resolve_behaviour rb );
    /**
     * This one does the real work.
     */
    RemoteObject* associate_on_connection( RemoteConnectionPtr rc, const oid_t&
	    id, uint32_t cid, resolve_behaviour rb, RemoteObject* ro = 0 );

    RemoteObject* associate_on_connection( RemoteConnectionPtr rc, RemoteObject* ro );

    RPCStream& get_search_packet( const uid& id, uint32_t hopcount );
    RPCStream& get_search_packet( uint32_t crcid, uint32_t hopcount );

    RemoteObject* create_instance( cid_t cid );
public:

    /**
     * searches in oid_object for a given object locally, with this uid
     * (shortcutting)
     */
    RemoteObject* get_object( const oid_t& oid);

    RemoteObject* get_object( cid_t cid );
    

    /**
     * Remove the server instantiator by its function...
     */
    static void remove_server( RemoteObject* fp() );

    static void remove_client( cid_t cid );


    
    static void provide_client( cid_t cid, RemoteObject* (*fp)( CallManager* ) );

    /**
     * Provide a dynamically allocatable server which might implement multiple
     * cids (e.g. due to inheritance)
     */
    static void provide_server( vector<cid_t>& cids, RemoteObject* fp() );
    

    /**
     * Registers an remote object under a certain uid for remote exposal. Why
     * is not done autmatically ? Well, because the user might want to use such
     * an object locally without exposing it. So if you choose to always do it
     * automatically, do it in your named constructor.
     */
    void register_object( const oid_t& oid, RemoteObject* ro );

    RootArtano( ServiceManager* sm, EventDispatcher*  );
    virtual ~RootArtano();
    CallManager& get_callmanager( void );
    const CallManager& get_callmanager( void ) const;

    /**
     * Should never be called multiple times
     */
    void Init( void );

    /**
     * FIXME: This information might be outdated (Phase 3)
     * Here we search for and establish a connection with an object on a
     * possibly remote host, with the uid of this object. Everytime an object
     * is resolved, for destruction it must be issued to the recycle()
     * function. If an object has be resolved twice, it must be recycled twice
     * for final destruction etc.
     * @note This will use a caching mechanism for the search, so a subsequent
     * resolve for the same uid will prbably not cause any traffic other than
     * the association.
     * @note the resolve_behaviour is only a strict-hint. Means that if you
     * want a deferred object, and the object is already associated within the
     * system, it will be as if direct_associate was specified
     *
     * The search behaviour is the same as the search since, esentially we
     * might need to do a search the same way (but for other parameters)
     * Per default we try really hard to find the object, but use the first
     * available found.
     */
    RemoteObject* resolve( const oid_t&, resolve_behaviour rb = direct_associate,
	    search_behaviour sb = search_known );

    RemoteObject* resolve( cid_t&, resolve_behaviour rb = direct_associate,
	    search_behaviour sb = search_known );

    RemoteObject* resolve( const oid_t&, cid_t, resolve_behaviour rb = direct_associate,
	    search_behaviour sb = search_known );

    /**
     * This also resolves some object. The difference here is that you don't
     * request a specific object, but you ask for a specific functionality only.
     */
    RemoteObject* resolve( const oid_t& oid, const hostid_t&, resolve_behaviour rb = direct_associate,
	    search_behaviour sb = search_known );

    RemoteObject* resolve( cid_t cid, const hostid_t&, resolve_behaviour rb = direct_associate,
	    search_behaviour sb = search_known );

    RemoteObject* resolve( const oid_t& oid, cid_t, const hostid_t&, resolve_behaviour rb = direct_associate,
	    search_behaviour sb = search_known );


    /**
     * This is not an official interface to be used by the used, but internally
     * to resolve directly as soon as the hosts location is really known.
     * Since this does not involve a search we do not have search behaviour parameters ;)
     */
    RemoteObject* resolve_internal( uint32_t cid, HostConnector&, resolve_behaviour rb = direct_associate );

   /**
     * Search for a class ID and return a list of pairs with the objects and a
     * short description that offer this service.
     */
    set<RemoteDescriptor* > search( uint32_t cid, search_behaviour = search_cache );

    set<RemoteDescriptor* > search( const oid_t& oid, search_behaviour = search_cache );

    set<RemoteDescriptor* > search( const oid_t& oid, cid_t cid, search_behaviour = search_cache );

    /**
     * The same as the search above, with the difference that we only search on
     * a specific host, which we specify by its uid.
     * @note Therefore we dont have to specify the behaviour since we know whom
     * to ask
     */
    set<RemoteDescriptor* > search( cid_t cid, const hostid_t& );

    set<RemoteDescriptor* > search( const oid_t& oid, const hostid_t& );

    set<RemoteDescriptor* > search( const oid_t& oid, cid_t cid, const hostid_t& );




    /**
     * This destroys the object. It checks a reference count on the object
     */
    void recycle( RemoteObject* );
};

}
}
#endif
