// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#ifndef OB_BOA_H
#define OB_BOA_H

//
// The OCI object key type
//
typedef OBFixSeq< CORBA_Octet > OCI_ObjectKey;

//
// The OBServer class
//
class OBServer;
typedef OBServer* OBServer_ptr;
typedef OBServer_ptr OBServerRef;
void OBDuplicate(OBServer_ptr);
void OBRelease(OBServer_ptr);
typedef OBObjVar< OBServer > OBServer_var;
typedef OBObjSeq< OBServer > OBServerSeq;
typedef OBSeqVar< OBObjSeq< OBServer > > OBServerSeq_var;

class OBServer : public OBRefCount
{
    //
    // Hide copy-constructor and asignment operator
    //
    OBServer(const OBServer&);
    void operator=(const OBServer&);

protected:

    OBServer() { }
    virtual ~OBServer() { }

public:

    static inline OBServer_ptr _duplicate(OBServer_ptr p)
    { if(p) p -> _OB_incRef(); return p; }
    static inline OBServer_ptr _nil()
    { return 0; }

    //
    // Destroy the server
    //
    virtual void destroy() = 0;

    //
    // Get the OCI acceptor
    //
    virtual OCI_Acceptor_ptr acceptor() = 0;

    //
    // Run the server. This may block or not block, depending on the
    // server kind, The return value is a "done" flag, meaning that
    // the server blocked to do it's work and can now be released.
    //
    virtual bool run() = 0;

    //
    // Disable the server
    //
    virtual void disable() = 0;
};

inline void
CORBA_release(OBServer_ptr p)
{
    if(p)
	p -> _OB_decRef();
}

inline CORBA_Boolean
CORBA_is_nil(OBServer_ptr p)
{
    return p == 0;
}

//
// The BOA class
//
class CORBA_BOA;
typedef CORBA_BOA* CORBA_BOA_ptr;
typedef CORBA_BOA_ptr CORBA_BOARef;
void OBDuplicate(CORBA_BOA_ptr);
void OBRelease(CORBA_BOA_ptr);
typedef OBObjVar< CORBA_BOA > CORBA_BOA_var;

class OBLockStrategyNull;
template <class K, class T, class LS> class OBHashtable;

class CORBA_BOA : public OBRefCount
{
    //
    // Hide copy-constructor and asignment operator
    //
    CORBA_BOA(const CORBA_BOA&);
    void operator=(const CORBA_BOA&);

    //
    // The BOA singleton
    //
    static CORBA_BOA_ptr boa_;

    //
    // The OCI acceptor registry
    //
    OCI_AccRegistry_var registry_;

    //
    // The OCI current
    //
    OCI_Current_var current_;

    //
    // All servers
    //
    OBServerSeq servers_;
#ifdef HAVE_JTC
    JTCMonitor serversMonitor_;
#endif

private:
public: // This has to be public for some compilers

    typedef OBFixSeq< CORBA_Octet > ServantKey;
    typedef OBHashtable< ServantKey, CORBA_Object_var, OBLockStrategyNull >
        ObjectHash;
    typedef OBVarSeq< ServantKey > ServantKeySeq;
    typedef OBHashtable< CORBA_Object_ptr, ServantKeySeq, OBLockStrategyNull >
        ServantKeyHash;

private:
    //
    // All connected implementations. This maps key -> object
    //
    ObjectHash* impls_;
#ifdef HAVE_JTC
    JTCMutex implsMutex_;
#endif

    //
    // All connected objects. This maps object -> keys
    //
    ServantKeyHash* keys_;
    
    //
    // Used for generating unnamed object keys
    //
    CORBA_ULong num_;

    //
    // The concurrency model
    //
public:
    enum ConcModel
    {
	//
	// Server uses blocking send/receive
	//
	ConcModelBlocking,
	
	//
	// Server uses the reactor and non-blocking send/receive
	//
	ConcModelReactive

#ifdef HAVE_JTC
	,
	ConcModelThreaded,
	ConcModelThreadPerClient,
	ConcModelThreadPerRequest,
	ConcModelThreadPool
#endif
    };

    //
    // Sequence of objects
    //
    typedef OBObjSeq< CORBA_Object > ObjectSeq;
    typedef OBSeqVar< ObjectSeq > ObjectSeq_var;
    
private:
    static ConcModel concModel_;
    static CORBA_ULong concModelThreadPool_;

    CORBA_BOA();
    ~CORBA_BOA();
    friend CORBA_BOA_ptr CORBA_ORB::BOA_init(int&, char**, const char*);
    // ORB::BOA_init may create BOAs

public:

    //
    // Standard IDL to C++ Mapping
    //
    static inline CORBA_BOA_ptr _duplicate(CORBA_BOA_ptr p)
    { if(p) p -> _OB_incRef(); return p; }
    static inline CORBA_BOA_ptr _nil()
    { return 0; }

    CORBA_Object_ptr create(const CORBA_ReferenceData&,
			    CORBA_InterfaceDef_ptr,
			    CORBA_ImplementationDef_ptr);

    void dispose(CORBA_Object_ptr);

    CORBA_ReferenceData* get_id(CORBA_Object_ptr);

    void change_implementation(CORBA_Object_ptr, CORBA_ImplementationDef_ptr);

    CORBA_Principal_ptr get_principal(CORBA_Object_ptr, CORBA_Environment_ptr);

    void impl_is_ready(CORBA_ImplementationDef_ptr);
    void deactivate_impl(CORBA_ImplementationDef_ptr);
    void obj_is_ready(CORBA_Object_ptr, CORBA_ImplementationDef_ptr);
    void deactivate_obj(CORBA_Object_ptr);

    //
    // Additional ORBacus specific functions
    //
    OCI_AccRegistry_ptr get_acc_registry(); 
    static ConcModel conc_model();
    static void conc_model(ConcModel);
    static CORBA_ULong conc_model_thread_pool();
    static void conc_model_thread_pool(CORBA_ULong);
    void create_new_servers();
    bool init_servers();
    void destroy_servers();
    void connect(CORBA_Object_ptr);
    void connect(CORBA_Object_ptr, const char*);
    void disconnect(CORBA_Object_ptr);
    void disable_acceptor(CORBA_ULong tag);
    void display_stats(OB_STD(ostream)&);
    ObjectSeq get_connected_servants();
    static void set_iiop_port_from_object(CORBA_Object_ptr);
    void oci_current(OCI_Current_ptr);
    OCI_Current_ptr oci_current();
    
    //
    // ORBacus internal functions
    // Application programs must not use these functions directly
    //
    static CORBA_BOA_ptr _OB_instance();
    static CORBA_BOA_ptr _OB_instance_nodup();

    CORBA_Object_ptr _OB_find(const OCI_ObjectKey&);
    CORBA_Object_ptr _OB_find(const IOP_IOR&);
    OBDispatchStatus _OB_dispatch(OCI_TransportInfo_ptr,
                                  const OCI_ObjectKey&, const char*,
				  OBBuffer&, bool, CORBA_ULong, CORBA_ULong);

    void _OB_startServers();
    void _OB_stopServers();
};

inline void
CORBA_release(CORBA_BOA_ptr p)
{
    if(p)
	p -> _OB_decRef();
}

inline CORBA_Boolean
CORBA_is_nil(CORBA_BOA_ptr p)
{
    return p == 0;
}

#endif
