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

#ifndef OB_DOWNCALL_H
#define OB_DOWNCALL_H

#include <OB/Downcall_fwd.h>
#include <OB/Stream_fwd.h>
#include <OB/PIClient_fwd.h>
#include <OB/PIManager_fwd.h>
#include <OB/NamedValue_fwd.h>
#include <OB/DII_fwd.h>
#include <OB/Client_fwd.h>
#include <OB/RefCounted_fwd.h>

#include <OB/Any.h>
#include <OB/TypeCodeConst.h>
#include <OB/OCITypes.h>
#include <OB/PIArgs.h>

namespace PortableInterceptor
{

class Manager_impl;

} // End of namespace PortableInterceptor

namespace OB
{

//
// For the friend declaration
//
class MarshalStubImpl;

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

protected:

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

    //
    // The client
    //
    Client_var client_;

    //
    // The downcall emitter
    //
    DowncallEmitter_var emitter_;

    //
    // Information about the IOR profile
    //
    RefCountProfileInfo_var profileInfo_;

    //
    // The list of policies
    //
    RefCountPolicyList_var policies_;

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

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

    //
    // Whether a response is expected
    //
    bool responseExpected_;

    //
    // The marshalled headers and parameters
    //
    OutputStream_var out_;

    //
    // Holds the results of the operation
    //
    InputStream_var in_;

    //
    // The state of this invocation
    //
    enum DowncallState
    {
        DowncallStateUnsent,
        DowncallStatePending,
        DowncallStateNoException,
        DowncallStateUserException,
        DowncallStateSystemException,
        DowncallStateFailureException,
        DowncallStateForward,
        DowncallStateForwardPerm
    };
    DowncallState state_;
    JTCMonitor* stateMonitor_;
    
    //
    // Holds the exception if state_ is DowncallStateUserException,
    // DowncallStateSystemException, or DowncallStateFailureException
    //
    CORBA::Exception* ex_;

    //
    // Holds the forward IOR if state_ is DowncallStateLocationForward
    // or DowncallLocationForwardPerm
    //
    RefCountIOR_var forwardIOR_;

    //
    // The request and reply service contexts
    //
    IOP::ServiceContextList requestSCL_;
    IOP::ServiceContextList replySCL_;

    //
    // Raise an exception if necessary
    //
    virtual void checkForException()
	throw(OB::ExceptionBase);

    Downcall(ORBInstance_ptr, Client_ptr, RefCountProfileInfo_ptr,
	     RefCountPolicyList_ptr, const char*, bool);
    virtual ~Downcall();

    //
    // Only MarshalStubImpl may create Downcalls
    //
    friend class MarshalStubImpl;

public:

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

    //
    // Accessors
    //
    ORBInstance_ptr orbInstance_nodup() const
    { return orbInstance_.in(); }
    Client_ptr client_nodup() const
    { return client_.in(); }
    RefCountProfileInfo_ptr profileInfo_nodup() const
    { return profileInfo_.in(); }
    RefCountPolicyList_ptr policies_nodup() const
    { return policies_.in(); }
    CORBA::ULong requestId() const { return reqId_; }
    const char* operation() const { return op_; }
    bool responseExpected() const { return responseExpected_; }

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

    //
    // Get the request service context list
    //
    const IOP::ServiceContextList& getRequestSCL()
    { return requestSCL_; }

    //
    // Add a service context to the request service context list
    //
    void addToRequestSCL(const IOP::ServiceContext& sc)
    {
	CORBA::ULong l = requestSCL_.length();
	requestSCL_.length(l + 1);
	requestSCL_[l] = sc;
    }

    //
    // Set the reply service context list
    //
    void setReplySCL(const IOP::ServiceContextList& scl)
    { replySCL_ = scl; }

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

    //
    // Request/response operations
    //
    void locate()
	throw(OB::ExceptionBase);
    void request()
	throw(OB::ExceptionBase);
    void oneway()
	throw(OB::ExceptionBase);
    void deferred()
	throw(OB::ExceptionBase);
    void response()
	throw(OB::ExceptionBase);
    bool poll()
	throw(OB::ExceptionBase);

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

    //
    // For DowncallStateUserException
    //
    char* unmarshalExceptionId();

    //
    // Check for a specific state
    //
    bool unsent() { return state_ == DowncallStateUnsent; }
    bool pending() { return state_ == DowncallStatePending; }
    bool userException() { return state_ == DowncallStateUserException; }
    bool failureException() { return state_ == DowncallStateFailureException; }

    //
    // Set the state of the downcall
    //
    void setPending()
	throw();
    void setNoException(InputStream_ptr)
	throw();
    void setUserException(InputStream_ptr)
	throw();
    virtual void setUserException(CORBA::UserException*, const char*)
	throw();
    void setUserException(const CORBA::UserException&)
	throw();
    void setSystemException(const CORBA::SystemException&)
	throw();
    void setFailureException(const CORBA::SystemException&,
			     CORBA::CompletionStatus)
	throw();
    void setLocationForward(RefCountIOR_ptr, bool)
	throw();

    //
    // Initialize the state monitor. This operation must be called in
    // order to be able to use one of the waitUntil...() operations
    // below
    //
    void initStateMonitor();

    //
    // This operation tries to wait for a specific state, using the
    // timeout from this downcall's policies. If the timeout expires,
    // a NO_RESPONSE exception is raised.
    //
    // - If the first parameter is set to false, the operations return
    //   immediately with false if the desired state cannot be
    //   reached.
    //
    // - If the return value is true, it's safe to access or modify
    //   the downcall object. If the return value if false, accessing
    //   or modifying the downcall object is not allowed, for thread
    //   safety reasons. (Because the downcall object is not thread
    //   safe.)
    //
    bool waitUntilCompleted(bool);
};

} // End of namespace OB

namespace CORBA
{

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

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

} // End of namespace CORBA

namespace OB
{

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

protected:

    //
    // The IOR and the original IOR
    //
    RefCountIOR_var IOR_;
    RefCountIOR_var origIOR_;

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

    //
    // Holds the exception ID if state_ is DowncallStateUserException,
    // DowncallStateSystemException, or DowncallStateFailureException
    //
    CORBA::String_var exId_;

    //
    // The ClientRequestInfo object provided by the interceptors
    //
    PortableInterceptor::ClientRequestInfo_var requestInfo_;

    //
    // Raise an exception if necessary
    //
    virtual void checkForException()
	throw(OB::ExceptionBase);

    //
    // Note: For efficiency reasons, the Downcall constructor assumes
    // ownership for all parameter marked with "/**/"
    //
    PIDowncall(ORBInstance_ptr, 
	       Client_ptr, 
	       RefCountProfileInfo_ptr,
	       RefCountPolicyList_ptr, 
	       const char*, 
	       bool,
	       RefCountIOR_ptr, 
	       RefCountIOR_ptr,
	       /**/ PIManager_ptr);

    virtual ~PIDowncall();

    //
    // Only MarshalStubImpl may create PIDowncalls
    //
    friend class MarshalStubImpl;

public:

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

    //
    // Marshalling interception points
    //
    virtual OutputStream_ptr preMarshal()
	throw(OB::ExceptionBase);

    //
    // Unmarshalling interception points
    //
    virtual void postUnmarshal()
	throw(OB::ExceptionBase);

    //
    // Set the state of the downcall
    //
    virtual void setUserException(CORBA::UserException*, const char*)
	throw();
};

} // End of namespace OB

namespace CORBA
{

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

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

} // End of namespace CORBA

namespace OB
{

//
// Declare ParameterDesc
//
struct ParameterDesc;

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

protected:

    //
    // Argument, result and exception list description provided by the
    // static stubs
    //
    OB::ParameterDesc* argDesc_;
    CORBA::ULong nargDesc_;
    OB::ParameterDesc* retDesc_;
    CORBA::TypeCode_ptr* exceptionTC_;
    CORBA::ULong nexceptionTC_;

    //
    // Note: For efficiency reasons, the Downcall constructor assumes
    // ownership for all parameter marked with "/**/"
    //
    PIArgsDowncall(ORBInstance_ptr, Client_ptr, RefCountProfileInfo_ptr,
		   RefCountPolicyList_ptr, const char*, bool,
		   RefCountIOR_ptr, RefCountIOR_ptr, /**/ PIManager_ptr,
		   ParameterDesc[], CORBA::ULong, ParameterDesc*,
		   CORBA::TypeCode_ptr*, CORBA::ULong);
    virtual ~PIArgsDowncall();

    //
    // Only MarshalStubImpl may create PIArgsDowncalls
    //
    friend class MarshalStubImpl;

public:

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

    //
    // Marshalling interception points
    //
    virtual OutputStream_ptr preMarshal()
	throw(OB::ExceptionBase);
};

} // End of namespace OB

namespace CORBA
{

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

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

} // End of namespace CORBA

namespace OB
{

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

protected:

    //
    // Argument, result and exception list description provided by the
    // DII
    //
    CORBA::NVList_var args_;
    CORBA::NamedValue_var result_;
    CORBA::ExceptionList_var exceptionList_;

    //
    // Note: For efficiency reasons, the Downcall constructor assumes
    // ownership for all parameter marked with "/**/"
    //
    PIDIIDowncall(ORBInstance_ptr, Client_ptr, RefCountProfileInfo_ptr,
		   RefCountPolicyList_ptr, const char*, bool,
		   RefCountIOR_ptr, RefCountIOR_ptr, /**/ PIManager_ptr,
		  CORBA::NVList_ptr, CORBA::NamedValue_ptr,
		  CORBA::ExceptionList_ptr);
    virtual ~PIDIIDowncall();

    //
    // Only MarshalStubImpl may create PIDIIDowncalls
    //
    friend class MarshalStubImpl;

public:

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

    //
    // Marshalling interception points
    //
    virtual OutputStream_ptr preMarshal()
	throw(OB::ExceptionBase);
};

} // End of namespace OB

namespace CORBA
{

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

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

} // End of namespace CORBA

namespace OB
{

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

    virtual ~DowncallEmitter() { };

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

    //
    // Send and receive downcalls
    //
    // - The first parameter is the downcall to send/receive
    //
    // - If the second parameter is set to false, send/receive will be
    //   done non-blocking.
    //
    // - If the return value is true, it's safe to access or modify
    //   the downcall object. If the return value if false, accessing
    //   or modifying the downcall object is not allowed, for thread
    //   safety reasons. (Because the downcall object is not thread
    //   safe.)
    //     
    virtual bool send(Downcall_ptr, bool)
        throw() = 0;
    virtual bool receive(Downcall_ptr, bool)
        throw() = 0;

    //
    // Send and receive downcalls with one operation (for efficiency
    // reasons)
    //
    virtual bool sendReceive(Downcall_ptr)
        throw() = 0;
};

} // End of namespace OB

namespace CORBA
{

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

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

} // End of namespace CORBA

#endif
