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

#ifndef OB_UPCALL_H
#define OB_UPCALL_H

#include <OB/Upcall_fwd.h>
#include <OB/ORBInstance_fwd.h>
#include <OB/Stream_fwd.h>
#include <OB/OCIClient_fwd.h>
#include <OB/Dispatch_fwd.h>
#include <OB/RefCounted_fwd.h>

#include <OB/TypeCode.h>
#include <OB/Any.h>
#include <OB/OCITypes.h>

#include <OB/PIManager_fwd.h> // For PI
#include <OB/PIServer_fwd.h>
#include <OB/PIArgs.h>
#include <OB/NamedValue_fwd.h>

#include <OB/POAServantBase_fwd.h>
#include <OB/POAInterface_fwd.h>

namespace OBPortableServer
{

class POA_impl; // For the friend declaration

}

namespace OB
{

class POAOAInterface_impl; // For the friend declaration

//
// The Upcall class
//
class Upcall : public RefCount
{
    //
    // Hide copy-constructor and assignment operator
    //
    Upcall(const Upcall&);
    void operator=(const Upcall&);

protected:

    //
    // The ORBInstance object
    //
    ORBInstance_var orbInstance_;

    //
    // Upcall delegates to UpcallReturn upon return from the
    // upcall. If this is nil, then no response is expected (i.e.,
    // this is a oneway call).
    //
    UpcallReturn_var upcallReturn_;

    //
    // Information about the IOR profile
    //
    OCI::ProfileInfo_var profileInfo_;

    //
    // The OCI transport info object
    //
    OCI::TransportInfo_var transportInfo_;

    //
    // The unique request ID
    //
    CORBA::ULong reqId_;

    //
    // The name of the operation
    //
    CORBA::String_var op_;

    //
    // Holds the inout/out parameters and return value
    //
    OutputStream_var out_;

    //
    // Holds the in/inout parameters
    //
    InputStream_var in_;

    //
    // The request service context list
    //
    IOP::ServiceContextList_var requestSCL_;

    //
    // The reply service context list
    //
    IOP::ServiceContextList replySCL_;

    //
    // The dispatch request
    //
    DispatchRequest_var dispatchRequest_;

    //
    // Dispatch strategy
    //
    DispatchStrategy_var dispatchStrategy_;

    //
    // The servant and POA
    //
    PortableServer::ServantBase* servant_;
    OBPortableServer::POA_impl* poa_;

    //
    // Whether postinvoke() has been called
    //
    bool postinvokeCalled_;

    //
    // Only POAOAInterface_impl and POA_impl may create Upcalls
    //
    friend class POAOAInterface_impl;
    friend class OBPortableServer::POA_impl;

    //
    // Note: For efficiency reasons, the Upcall constructor assumes
    // ownership for all parameter marked with "/**/"
    //
    Upcall(ORBInstance_ptr,
	   UpcallReturn_ptr,
	   /**/ OCI::ProfileInfo*,
	   /**/ OCI::TransportInfo_ptr,
	   CORBA::ULong,
	   /**/ char*,
	   /**/ InputStream_ptr,
           /**/ IOP::ServiceContextList*);

public:

    virtual ~Upcall();

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

    //
    // Accessors
    //
    ORBInstance_ptr orbInstance_nodup() const
    { return orbInstance_.in(); }
    const OCI::ProfileInfo& profileInfo() const
    { return profileInfo_.in(); }
    OCI::TransportInfo_ptr transportInfo_nodup()
    { return transportInfo_.in(); }
    CORBA::ULong requestId() const { return reqId_; }
    const char* operation() const { return op_; }
    bool responseExpected() const;
    bool postinvokeCalled() const { return postinvokeCalled_; }

    //
    // Obtain the input and output stream
    //
    OutputStream_ptr output_nodup() { return out_; }
    InputStream_ptr input_nodup() { return in_; }

    //
    // Create the output stream for marshalling replies
    //
    void createOutputStream(CORBA::ULong);

    //
    // Unmarshalling interception points
    //
    InputStream_ptr preUnmarshal()
	throw(CORBA::SystemException, OB::LocationForward);
    void unmarshalEx(const CORBA::SystemException&)
	throw(CORBA::SystemException, OB::LocationForward);
    virtual void postUnmarshal()
	throw(CORBA::SystemException, OB::LocationForward);

    //
    // Marshalling interception points
    //
    void postinvoke()
	throw(CORBA::SystemException, OB::LocationForward);
    virtual OutputStream_ptr preMarshal()
	throw(CORBA::SystemException, OB::LocationForward);
    void marshalEx(const CORBA::SystemException&)
	throw(CORBA::SystemException, OB::LocationForward);
    void postMarshal()
	throw(CORBA::SystemException, OB::LocationForward);

    //
    // Set the state of the Upcall
    //
    virtual void setUserException(const CORBA::UserException&)
	throw();
    virtual void setUserException(const CORBA::Any&)
	throw();
    virtual void setSystemException(const CORBA::SystemException&)
	throw();
    virtual void setLocationForward(RefCountIOR_ptr, bool)
	throw();

    //
    // Notify the Upcall about a potential change in the thread
    // context
    //
    virtual void contextSwitch() { /* Do nothing */ }

    //
    // Note: For efficiency reasons, this method assumes ownership for
    // all parameter marked with "/**/"
    //
    // Set the dispatch information
    //
    void setDispatchInfo(/**/DispatchRequest_ptr, DispatchStrategy_ptr)
	throw();

    //
    // Set the servant and POA
    //
    virtual void setServantAndPOA(PortableServer::ServantBase*,
                                  OBPortableServer::POA_impl*)
        throw();

    //
    // Do the invocation
    //
    void invoke()
	throw();
};

} // End of namespace OB

namespace CORBA
{

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

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

} // End of namespace CORBA

namespace PortableInterceptor
{

class ObjectReferenceTemplate; // Forward declaration

}

namespace OB
{

class PIUpcall : public Upcall
{
    //
    // Hide copy-constructor and assignment operator
    //
    PIUpcall(const PIUpcall&);
    void operator=(const PIUpcall&);

protected:

    //
    // The PortableInterceptor manager
    //
    PIManager_var piManager_;

    //
    // The ServerRequestInfo object provided by the interceptors
    //
    PortableInterceptor::ServerRequestInfo_var requestInfo_;

    //
    // Only POA_impl may create PIUpcall
    //
    friend class OBPortableServer::POA_impl;

    //
    // Note: For efficiency reasons, the Upcall constructor assumes
    // ownership for all parameter marked with "/**/"
    //
    PIUpcall(ORBInstance_ptr,
	     UpcallReturn_ptr,
	     /**/ OCI::ProfileInfo*,
	     /**/ OCI::TransportInfo_ptr,
	     CORBA::ULong,
	     /**/ char*,
	     /**/ InputStream_ptr,
	     /**/ IOP::ServiceContextList*,
	     /**/ PIManager_ptr);

public:

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

    //
    // Set the interceptor argument information
    //
    void setArgDesc(ParameterDesc*, CORBA::ULong,
		    ParameterDesc*,
		    CORBA::TypeCode_ptr*, CORBA::ULong)
	throw();

    //
    // Set the DSI argument information and result
    //
    void setArguments(CORBA::NVList_ptr)
	throw();
    void setResult(const CORBA::Any&)
	throw();

    //
    // Notify the PIUpcall about a potential change in the thread
    // context
    //
    virtual void contextSwitch()
        throw();

    //
    // receive_request_service_contexts interception point.
    //
    void receiveRequestServiceContexts(
   	  RefCountPolicyList_ptr,
	  const CORBA::OctetSeq&,
	  const CORBA::OctetSeq&,
  	  PortableInterceptor::ObjectReferenceTemplate*)
	throw(CORBA::SystemException, OB::LocationForward);

    //
    // Unmarshalling interception points
    //
    virtual void postUnmarshal()
	throw(CORBA::SystemException, OB::LocationForward);

    //
    // Marshalling interception points
    //
    virtual OutputStream_ptr preMarshal()
	throw(CORBA::SystemException, OB::LocationForward);

    //
    // Set the state of the Upcall
    //
    virtual void setUserException(const CORBA::UserException&)
	throw();
    virtual void setUserException(const CORBA::Any&)
	throw();
    virtual void setSystemException(const CORBA::SystemException&)
	throw();
    virtual void setLocationForward(OB::RefCountIOR_ptr, bool)
	throw();

    //
    // Set the servant and POA
    //
    virtual void setServantAndPOA(PortableServer::ServantBase*,
                                  OBPortableServer::POA_impl*)
        throw();
};

}

namespace CORBA
{

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

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

} // End of namespace CORBA

namespace OB
{

//
// The UpcallReturn class
//
class UpcallReturn : virtual public RefCount
{
public:

    virtual ~UpcallReturn() { };

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

    //
    // Called upon return from an upcall
    //
    virtual void upcallBeginReply(Upcall_ptr,
				  const IOP::ServiceContextList&) = 0;
    virtual void upcallEndReply(Upcall_ptr) = 0;
    virtual void upcallBeginUserException(Upcall_ptr,
					  const IOP::ServiceContextList&) = 0;
    virtual void upcallEndUserException(Upcall_ptr) = 0;
    virtual void upcallUserException(Upcall_ptr,
				     const CORBA::UserException&,
				     const IOP::ServiceContextList&) = 0;
    virtual void upcallSystemException(Upcall_ptr,
				       const CORBA::SystemException&,
				       const IOP::ServiceContextList&) = 0;
    virtual void upcallForward(Upcall_ptr, RefCountIOR_ptr, bool,
			       const IOP::ServiceContextList&) = 0;
};

} // End of namespace OB

namespace CORBA
{

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

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

} // End of namespace CORBA

#endif
