/*  -*- mode: c++; c-basic-offset: 4; -*-
 *
 *  MICO --- a free CORBA implementation
 *  Copyright (C) 1997-98 Kay Roemer & Arno Puder
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Send comments and/or bug reports to:
 *                 mico@informatik.uni-frankfurt.de
 */

#ifndef __mico_orb_h__
#define __mico_orb_h__

#ifndef HAVE_THREADS
#  define USE_ORB_CACHE
#endif

class ObjectAdapter;
//class ORBRequest;
struct ORBCallback;

typedef ORBRequest *ORBRequest_ptr;
typedef ObjVar<ORBRequest> ORBRequest_var;


class ORBInvokeRec {
public:
    typedef ULong MsgId;
private:
    MsgId _myid;
    RequestType _type;
    Boolean _have_result;
    Boolean _active;
    Object_ptr _obj;
    Object_ptr _target;
    Principal_ptr _principal;
    Boolean _response_expected;
    string _repoid;
    InvokeStatus _invoke_stat;
    LocateStatus _locate_stat;
    ObjectAdapter *_adapter;
    ORBRequest *_req;
    ORBCallback *_cb;
    ORB_ptr _orb;
    OctetSeq _objtag;
    Address *_addr;

    void *_reqHint;
    void *_invHint;
 
public:
    ORBInvokeRec (MsgId);
    virtual ~ORBInvokeRec ();

    void free();

    void init_invoke (ORB_ptr, Object_ptr target,
		      ORBRequest *, Principal_ptr pr,
		      Boolean response_expected,
		      ORBCallback *, ObjectAdapter * = 0);

    void init_locate (ORB_ptr, Object_ptr,
		      ORBCallback *, ObjectAdapter * = 0);

    void init_bind (ORB_ptr, const char *,
		    const OctetSeq &tag, Address *, ORBCallback *,
		    ObjectAdapter * = 0);

    void set_RequestHint(void *hint) 
    { _reqHint = hint; }
    void *get_RequestHint()
    { return _reqHint; }

    void set_InvokeHint(void *hint) 
    { _invHint = hint; }
    void *get_InvokeHint()
    { return _invHint; }

    Boolean active() const
    { return _active; };
    void deactivate()
    { _active = FALSE; };

    MsgId id () const
    { return _myid; }

    Boolean completed () const
    { return _have_result; }

    ObjectAdapter *oa ()
    { return _adapter; }

    void oa (ObjectAdapter *oa)
    { _adapter = oa; }
    
    RequestType request_type ()
    { return _type; }

    ORBRequest *request ()
    { return _req; }

    const OctetSeq &tag () const
    { return _objtag; }

    ORBCallback *callback ()
    { return _cb; }

    void redo ();

    void set_answer_invoke (InvokeStatus, Object_ptr, ORBRequest *);
    void set_answer_bind (LocateStatus, Object_ptr);
    void set_answer_locate (LocateStatus, Object_ptr);

    Boolean get_answer_invoke (InvokeStatus &, Object_ptr &, ORBRequest *&);
    Boolean get_answer_bind (LocateStatus &, Object_ptr &);
    Boolean get_answer_locate (LocateStatus &, Object_ptr &);
};



class ORB : public ServerlessObject {
public:
    typedef CORBA::ULong MsgId;
private:

    class ResourceManager {
      ULong _connection_limit;
      ULong _request_limit;
      ULong _connection_count;
      MICOMT::Mutex conn_cnt_mutex;
    public:
      ResourceManager () {
        _connection_limit = 10;
        _request_limit = 10;
        _connection_count = 0;
      }
      inline ULong connectionLimit (ULong NewLimit) {
        MICOMT::auto_Lock l(conn_cnt_mutex);
        return _connection_limit = NewLimit;
      }
      inline ULong connectionLimit (void) const {
        return _connection_limit;
      }
      inline ULong requestLimit (ULong NewLimit) {
        return _request_limit = NewLimit;
      }
      inline ULong requestLimit (void) const {
        return _request_limit;
      }

      //
      // Not used, but might be handy if we ever want to know how many con-
      // nections are open at any point in time...
      //
      inline ULong connectionCount (void) {
        return _connection_count;
      }

      inline CORBA::Boolean acquireConnection (void) {
        MICOMT::auto_Lock l(conn_cnt_mutex);
        if (_connection_count < _connection_limit)
        {
          _connection_count++;
          return TRUE;
        }
        return FALSE;
      }
      inline void releaseConnection (void) {
        MICOMT::auto_Lock l(conn_cnt_mutex);
        _connection_count--;
      }
    } resourceManager;

    typedef vector<ObjectAdapter *> OAVec;
    typedef map<MsgId, ORBInvokeRec *, less<MsgId> > InvokeMap;
    typedef map<string, Object_var, less<string> > InitialRefMap;
    typedef map<string, ValueFactoryBase_var, less<string> > ValueFactoryMap;
    typedef list<string> isa_cacheList;

    string _default_init_ref;
    MICOMT::Locked<isa_cacheList> _isa_cache;
    vector<string> _bindaddrs;
    MICOMT::rwLocked<OAVec> _adapters;
    MICOMT::rwLocked<InvokeMap> _invokes;
    Dispatcher *_disp;
    IOR *_tmpl;

    MsgId _theid;
    MICOMT::Mutex _theid_lock;

    InitialRefMap _init_refs;
    MICOMT::Mutex _init_refs_lock;

    string _rcfile;
    DomainManager_var _def_manager;
    MICOMT::Locked<ValueFactoryMap> _value_facs;
    Boolean _is_running;
    Boolean _is_shutdown;
    Boolean _is_stopped;
    Boolean _wait_for_completion;
    OAVec _shutting_down_adapters;

#ifdef USE_ORB_CACHE
    ORBInvokeRec *_cache_rec;
    CORBA::Boolean _cache_used;
#endif

    ObjectAdapter *get_oa (Object_ptr);
    ORBInvokeRec *create_invoke (MsgId);
    void add_invoke (ORBInvokeRec *);
    ORBInvokeRec *get_invoke (MsgId);
    void del_invoke (MsgId);

    // internal ORBMsgId handling
    ORBInvokeRec *get_invoke (ORBMsgId id) { return id; };

    void do_shutdown ();

    CORBA::Object_ptr iioploc_to_object (const char *);
    CORBA::Object_ptr iiopname_to_object (const char *);
    CORBA::Object_ptr file_to_object (const char *);
    CORBA::Object_ptr http_to_object (const char *);

    Boolean builtin_invoke (ORBMsgId, Object_ptr,
                            ORBRequest *, Principal_ptr);
    Object_ptr _resolve_initial_references (const char *id);
public:
    inline ResourceManager &resource_manager (void) {
      return resourceManager;
    }
    typedef IfaceSequenceTmpl<Request_var,Request_ptr> RequestSeq;
    typedef TSeqVar<RequestSeq> RequestSeq_var;

    typedef OctetSeq ObjectTag;
    typedef TSeqVar<ObjectTag> ObjectTag_var;

    ORB (int &argc, char **argv, const char *rcfile);
    virtual ~ORB ();

    char *object_to_string (Object_ptr);
    Object_ptr string_to_object (const char *);
    Object_ptr ior_to_object (IOR *);
    Boolean is_impl (Object_ptr);
    const char *rcfile();

    static char *tag_to_string (const ObjectTag &);
    static ObjectTag *string_to_tag (const char *);

    void create_list (Long, NVList_ptr &);
    void create_operation_list (OperationDef *,
				NVList_ptr &);
    void create_named_value (NamedValue_ptr &);
    void create_exception_list (ExceptionList_ptr &);
    void create_context_list (ContextList_ptr &);
    void create_environment (Environment_ptr &);
    
    void get_default_context (Context_ptr &);

    // mico-extension
    void get_default_domain_manager (DomainManager_ptr &);

    CORBA::Boolean get_service_information (
	CORBA::ServiceType service_type,
	CORBA::ServiceInformation *& service_information);

    void send_multiple_requests_oneway (const RequestSeq &);
    void send_multiple_requests_deferred (const RequestSeq &);
    Boolean poll_next_response ();
    void get_next_response (Request_ptr &);

    Boolean work_pending ();
    void perform_work ();
    void run ();
    void shutdown (Boolean wait_for_completion);
    void destroy ();

    typedef char *OAid;
    BOA_ptr BOA_init (int &argc, char **argv, const char *boa_id = "");
    BOA_ptr BOA_instance (const char *id, Boolean create=TRUE);

    typedef CORBA::ORB_InvalidName InvalidName;
    typedef CORBA::ORB_InvalidName_catch InvalidName_catch;

    typedef char *ObjectId;
    typedef StringSequenceTmpl<String_var> ObjectIdList;
    typedef ObjectIdList *ObjectIdList_ptr;
    typedef TSeqVar<ObjectIdList> ObjectIdList_var;

    ObjectIdList_ptr list_initial_services ();
    Object_ptr resolve_initial_references (const char *id)
	{
	    MICOMT::auto_Lock l(_init_refs_lock);
	    return _resolve_initial_references(id);
	}
    Boolean set_initial_reference (const char *id, Object_ptr obj);
    Boolean set_default_initial_reference (const char *);

    virtual TypeCode_ptr create_struct_tc (const char *rep_id,
					   const char *name,
					   const StructMemberSeq &);
    virtual TypeCode_ptr create_union_tc (const char *rep_id,
					  const char *name,
					  TypeCode_ptr disc_type,
					  const UnionMemberSeq &);
    virtual TypeCode_ptr create_enum_tc (const char *rep_id,
					 const char *name,
					 const EnumMemberSeq &);
    virtual TypeCode_ptr create_exception_tc (const char *rep_id,
					      const char *name,
					      const StructMemberSeq &);
    virtual TypeCode_ptr create_alias_tc (const char *rep_id,
					  const char *name,
					  TypeCode_ptr orig_type);
    virtual TypeCode_ptr create_interface_tc (const char *rep_id,
					      const char *name);
    virtual TypeCode_ptr create_string_tc (ULong bound);
    virtual TypeCode_ptr create_wstring_tc (ULong bound);
    virtual TypeCode_ptr create_fixed_tc (UShort digits, Short scale);
    virtual TypeCode_ptr create_sequence_tc (ULong bound,
					     TypeCode_ptr el_type);
    virtual TypeCode_ptr create_recursive_sequence_tc (ULong bound,
						       ULong offset);
    virtual TypeCode_ptr create_array_tc (ULong length,
					  TypeCode_ptr el_type);
    virtual TypeCode_ptr create_value_tc (const char *rep_id, const char *name,
					  ValueModifier mod,
					  TypeCode_ptr concrete_base,
					  const ValueMemberSeq &members);
    virtual TypeCode_ptr create_value_box_tc (const char *rep_id,
					      const char *name,
					      TypeCode_ptr boxed_type);
    virtual TypeCode_ptr create_native_tc (const char *rep_id,
					   const char *name);
    virtual TypeCode_ptr create_recursive_tc (const char *rep_id);
    virtual TypeCode_ptr create_abstract_interface_tc (const char *rep_id,
						       const char *name);

    // begin-value
    ValueFactory register_value_factory (const char *repoid,
					 ValueFactory factory);
    void unregister_value_factory (const char *repoid);
    ValueFactory lookup_value_factory (const char *repoid);
    // end-value

    CORBA::Policy_ptr create_policy (CORBA::PolicyType type,
				     const CORBA::Any &any);

    // begin-mico-extension
    const vector<string> &bindaddrs () const
    {
	return _bindaddrs;
    }
    void bindaddrs (const vector<string> &b)
    {
	_bindaddrs = b;
    }

    void dispatcher (Dispatcher *);
    Dispatcher *dispatcher ()
    {
	return _disp;
    }
    IOR *ior_template ()
    {
	return _tmpl;
    }

    void register_oa (ObjectAdapter *, CORBA::UShort prio);
    void unregister_oa (ObjectAdapter *);

    MsgId new_msgid ();
    ORBMsgId new_orbid (MsgId msgid = 0);
    static MsgId get_msgid (ORBMsgId rec) { return rec->id(); };
    ORBMsgId get_orbid (MsgId id) { return get_invoke(id); };

    //FIXME: hopefully nobody is going to shot me for this :-)
    void set_RequestHint(ORBMsgId id, void *hint) {
	ORBInvokeRec *rec = get_invoke (id);
	assert( rec );
	rec->set_RequestHint( hint );
    };
    void *get_RequestHint(ORBMsgId id) {
	ORBInvokeRec *rec = get_invoke (id);
	assert( rec );
	return rec->get_RequestHint();
    };
    void set_InvokeHint(ORBMsgId id, void *hint) {
	ORBInvokeRec *rec = get_invoke (id);
	assert( rec );
	rec->set_InvokeHint( hint );
    };
    void *get_InvokeHint(ORBMsgId id) {
	ORBInvokeRec *rec = get_invoke (id);
	assert( rec );
	return rec->get_InvokeHint();
    };

    ORBMsgId invoke_async (Object_ptr, ORBRequest *, Principal_ptr,
			   Boolean rply = TRUE, ORBCallback * = 0,
			   ORBMsgId = 0);
    ORBMsgId locate_async (Object_ptr, ORBCallback * = 0, ORBMsgId = 0);
    ORBMsgId bind_async (const char *repoid, const ObjectTag &,
			 Address *, ORBCallback * = 0, ORBMsgId = 0);

    InvokeStatus invoke (Object_ptr &, ORBRequest *, Principal_ptr,
			 Boolean rply = TRUE);
    LocateStatus locate (Object_ptr &);
    LocateStatus bind (const char *repoid, const ObjectTag &,
		       Address *, Object_ptr &);
    Object_ptr bind (const char *repoid, const char *addr = 0);
    Object_ptr bind (const char *repoid, const ObjectTag &,
		     const char *addr = 0);

    void    cancel (ORBMsgId);
    void    cancel (MsgId);
    Boolean wait (ORBMsgId, Long tmout = -1);
    //    Long    wait (vector<MsgId> &, Long tmout = -1);

    void answer_invoke (ORBMsgId, InvokeStatus, Object_ptr, ORBRequest *);
    void answer_bind   (ORBMsgId, LocateStatus, Object_ptr);
    void answer_locate (ORBMsgId, LocateStatus, Object_ptr);
    void answer_shutdown (ObjectAdapter *);

    InvokeStatus get_invoke_reply (ORBMsgId, Object_ptr &, ORBRequest *&);
    LocateStatus get_locate_reply (ORBMsgId, Object_ptr &);
    LocateStatus get_bind_reply   (ORBMsgId, Object_ptr &);

    RequestType request_type (ORBMsgId);
    void redo_request (ORBMsgId);

    ImplementationDef    *get_impl (Object_ptr);
    InterfaceDef         *get_iface (Object_ptr);
    Boolean               is_a (Object_ptr, const char *repoid);
    Boolean               non_existent (Object_ptr);
    // end-mico-extension

    static ORB_ptr _duplicate (ORB_ptr o)
    {
	if (o)
	    o->_ref();
	return o;
    }
    static ORB_ptr _nil ()
    {
	return 0;
    }
};

typedef ObjVar<ORB> ORB_var;


// ORB initialization
typedef char *ORBid;
MICO_EXPORT_FCT_DECL ORB_ptr ORB_init (int &argc, char **argv,
				       const char *id = "");
MICO_EXPORT_FCT_DECL ORB_ptr ORB_instance (const char *id, Boolean create=TRUE);


class ObjectAdapter {
    CORBA::UShort _prio;
public:
    typedef CORBA::ULong MsgId;

    enum {
	OAPriorityMin = 0,
	OAPriorityRemote = 100,
	OAPriorityLocal = 200,
	OAPriorityMax = 0xffff
    };

    CORBA::UShort priority () const
    {
	return _prio;
    }
    void priority (CORBA::UShort p)
    {
	_prio = p;
    }

    virtual const char *get_oaid () const = 0;
    virtual Boolean has_object (Object_ptr) = 0;
    virtual Boolean is_local () const = 0;

    virtual Boolean invoke (ORBMsgId, Object_ptr, ORBRequest *,
			    Principal_ptr, Boolean response_exp = TRUE) = 0;
    virtual Boolean bind (ORBMsgId, const char *repoid,
			  const ORB::ObjectTag &,
			  Address *addr) = 0;
    virtual Boolean locate (ORBMsgId, Object_ptr) = 0;
    virtual Object_ptr skeleton (Object_ptr) = 0;
    virtual void cancel (ORBMsgId) = 0;
    virtual void shutdown (Boolean wait_for_completion) = 0;

    virtual void answer_invoke (ORBMsgId, CORBA::Object_ptr,
				CORBA::ORBRequest *,
				CORBA::InvokeStatus) = 0;

    virtual ~ObjectAdapter ();
};

#endif // __mico_orb_h__
