// **********************************************************************
//
// Copyright (c) 2002
// IONA Technologies, Inc.
// Waltham, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#ifndef OB_PI_MANAGER_H
#define OB_PI_MANAGER_H

#include <OB/TypeCodeConst.h>
#include <OB/PIManager_fwd.h>
#include <OB/PIClient_fwd.h>
#include <OB/PIServer_fwd.h>
#include <OB/POAServantBase_fwd.h>
#include <OB/POAInterface_fwd.h>
#include <OB/ORBInstance_fwd.h>
#include <OB/RefCounted_fwd.h>
#include <OB/PIIOR_fwd.h>
#include <OB/PIORT_fwd.h>

#include <OB/Object.h>
#include <OB/ValueBase.h>
#include <OB/PIArgs.h>
#include <OB/Any.h> // gcc needs this
#include <OB/PIORT.h>
#include <OB/Except.h>
#include <OB/PIORBInit.h>
#include <OB/Hashtable.h>
#include <OB/StubImpl.h> // For OB::LocationForward
#include <OB/IOP.h>

namespace OB
{

//
// The component table
//
typedef Hashtable< CORBA::ULong, IOP::TaggedComponentSeq*, 
    ULongHasher> ComponentTable;

} // End namespace OB

//
// Forward declarations
//
namespace PortableInterceptor
{

class Current_impl;

//
// Sequence types for client request and server request interceptors
//
class OBUnique_ClientRequestInterceptorSeq;
typedef OB::ObjSeq< ClientRequestInterceptor,
    OBUnique_ClientRequestInterceptorSeq > ClientRequestInterceptorSeq;

class OBUnique_ServerRequestInterceptorSeq;
typedef OB::ObjSeq< ServerRequestInterceptor,
    OBUnique_ServerRequestInterceptorSeq > ServerRequestInterceptorSeq;

class OBUnique_IORInterceptorSeq;
typedef OB::ObjSeq< IORInterceptor,
    OBUnique_IORInterceptorSeq > IORInterceptorSeq;

} // End namespace PortableInterceptor

namespace OB
{

class PIManager : public RefCount
{
    //
    // Hide copy constructor/assignment operator
    //
    PIManager(const PIManager&);
    void operator=(const PIManager&);

    //
    // The ORB Instance
    //
    OB::ORBInstance_var orbInstance_;

    //
    // The next interceptor id and mutex
    //
    CORBA::ULong id_;
    JTCMutex idMut_;

    //
    // The PortableInterceptor::Current implementation
    //
    PortableInterceptor::Current_impl* current_;

    //
    // The registered ClientRequestInterceptors
    //
    PortableInterceptor::ClientRequestInterceptorSeq clientReqInterceptors_;

    //
    // The registered ServerRequestInterceptors
    //
    PortableInterceptor::ServerRequestInterceptorSeq serverReqInterceptors_;

    //
    // The registered IORInterceptors
    //
    PortableInterceptor::IORInterceptorSeq iorInterceptors_;

    //
    // The number of state slots
    //
    CORBA::ULong maxSlots_;

    //
    // Have all ORB initializers been invoked?
    //
    bool complete_;

    CORBA::ULong nextID();
    
public:

    PIManager();
    ~PIManager();

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

    //
    // Destroy the PI manager
    //
    void destroy();

    //
    // Registration methods
    //
    void addIORInterceptor(PortableInterceptor::IORInterceptor_ptr,
    			   bool insertAtHead = false)
	throw(PortableInterceptor::ORBInitInfo::DuplicateName,
	      CORBA::SystemException);

    void addClientRequestInterceptor(
        PortableInterceptor::ClientRequestInterceptor_ptr)
	throw(PortableInterceptor::ORBInitInfo::DuplicateName,
	      CORBA::SystemException);

    void addServerRequestInterceptor(
        PortableInterceptor::ServerRequestInterceptor_ptr)
	throw(PortableInterceptor::ORBInitInfo::DuplicateName,
	      CORBA::SystemException);

    CORBA::ULong allocateSlotId();

    void registerPolicyFactory(CORBA::PolicyType,
                               PortableInterceptor::PolicyFactory_ptr);

    //
    // Set the ORBInstance object
    //
    void setORBInstance(OB::ORBInstance_ptr);

    //
    // Setup is complete
    //
    void setupComplete();

    //
    // For client request interceptors
    //

    //
    // No argument information available
    //
    PortableInterceptor::ClientRequestInfo_ptr
    clientSendRequest(
        const char*, bool,
	RefCountIOR_ptr,
	RefCountIOR_ptr,
	RefCountProfileInfo_ptr,
	OB::RefCountPolicyList_ptr,
        IOP::ServiceContextList&, IOP::ServiceContextList&)
	throw(OB::LocationForward, CORBA::SystemException);

    //
    // DII style argument information available
    //
    PortableInterceptor::ClientRequestInfo_ptr
    clientSendRequest(
        const char*, bool,
	RefCountIOR_ptr,
	RefCountIOR_ptr,
	RefCountProfileInfo_ptr,
	OB::RefCountPolicyList_ptr,
        IOP::ServiceContextList&, IOP::ServiceContextList&,
        CORBA::NVList_ptr, CORBA::NamedValue_ptr, CORBA::ExceptionList_ptr)
	throw(OB::LocationForward, CORBA::SystemException);

    //
    // SII argument information available
    //
    PortableInterceptor::ClientRequestInfo_ptr
    clientSendRequest(
        const char*, bool,
	RefCountIOR_ptr,
	RefCountIOR_ptr,
	RefCountProfileInfo_ptr,
	OB::RefCountPolicyList_ptr,
        IOP::ServiceContextList&, IOP::ServiceContextList&,
        OB::ParameterDesc*, int,
        OB::ParameterDesc*,
        CORBA::TypeCode_ptr*, int)
	throw(OB::LocationForward, CORBA::SystemException);
    
    void clientReceiveReply(PortableInterceptor::ClientRequestInfo_ptr)
	throw(OB::LocationForward, CORBA::SystemException);
    void clientReceiveException(PortableInterceptor::ClientRequestInfo_ptr,
                                bool, const CORBA::Exception&, const char*)
	throw(OB::LocationForward, CORBA::SystemException);
    void clientReceiveLocationForward(
        PortableInterceptor::ClientRequestInfo_ptr, RefCountIOR_ptr)
	throw(OB::LocationForward, CORBA::SystemException);

    //
    // For server request interceptors
    //
    PortableInterceptor::ServerRequestInfo_ptr
        serverCreateRequestInfo(const char*, bool,
				OB::RefCountPolicyList_ptr,
        		        const CORBA::OctetSeq&,
				const CORBA::OctetSeq&,
				PortableInterceptor::ObjectReferenceTemplate*,
        			IOP::ServiceContextList&,
				IOP::ServiceContextList&)
	throw(OB::LocationForward, CORBA::SystemException);

    //
    // Setup the servant and POA information
    //
    void serverSetupServant(PortableInterceptor::ServerRequestInfo_ptr,
                            PortableServer::ServantBase*,
                            PortableServer::POA_ptr)
	throw();

    //
    // Notify the ServerRequestInfo about a context switch
    //
    void serverContextSwitch(PortableInterceptor::ServerRequestInfo_ptr)
	throw();

    //
    // Set the parameter information (SII case)
    //
    void serverParameterDesc(PortableInterceptor::ServerRequestInfo_ptr,
                             OB::ParameterDesc*, CORBA::ULong,
                             OB::ParameterDesc*,
                             CORBA::TypeCode_ptr*, CORBA::ULong)
	throw();

    //
    // Set the arguments (DSI case)
    //
    void serverArguments(PortableInterceptor::ServerRequestInfo_ptr,
                         CORBA::NVList_ptr)
	throw();

    //
    // Set the result (DSI case)
    //
    void serverResult(PortableInterceptor::ServerRequestInfo_ptr,
                      const CORBA::Any&)
	throw();

    //
    // Call the receive_request_service_contexts interception point
    //
    void
    serverReceiveRequestServiceContexts(
	PortableInterceptor::ServerRequestInfo_ptr)
	throw(OB::LocationForward, CORBA::SystemException);

    //
    // Call the receive_request interception point
    //
    void serverReceiveRequest(PortableInterceptor::ServerRequestInfo_ptr)
	throw(OB::LocationForward, CORBA::SystemException);

    //
    // Call the send_reply interception point
    //
    void serverSendReply(PortableInterceptor::ServerRequestInfo_ptr)
	throw(CORBA::SystemException);

    //
    // Call the send_location_forward interception point
    //
    void serverSendLocationForward(PortableInterceptor::ServerRequestInfo_ptr,
                                   RefCountIOR_ptr)
	throw(OB::LocationForward, CORBA::SystemException);

    //
    // Call the send_exception interception point
    //
    void serverSendException(PortableInterceptor::ServerRequestInfo_ptr,
                             bool, const CORBA::Exception&)
	throw(OB::LocationForward, CORBA::SystemException);

    //
    // For IORInterceptors
    //
    void establishComponents(PortableInterceptor::IORInfo_ptr)
	throw(CORBA::SystemException);

    void componentsEstablished(PortableInterceptor::IORInfo_ptr)
	throw(CORBA::SystemException);

    void adapterStateChange(
        const PortableInterceptor::ObjectReferenceTemplateSeq&,
	PortableInterceptor::AdapterState)
	throw();

    void adapterManagerStateChange(
	PortableInterceptor::AdapterManagerId,
	PortableInterceptor::AdapterState)
	throw();

    //
    // Do we have client request interceptors?
    //
    bool haveClientInterceptors() const
	throw();

    //
    // Do we have server request interceptors?
    //
    bool haveServerInterceptors() const
	throw();
};

} // End namespace OB

namespace CORBA
{

inline void
release(OB::PIManager_ptr p)
{
    if(p)
	p -> _OB_decRef();
}

inline Boolean
is_nil(OB::PIManager_ptr p)
{
    return p == 0;
}

} // End of namespace CORBA

#endif
