#include "LocateRequest_Invocation.h"
#include "Profile_Transport_Resolver.h"
#include "operation_details.h"
#include "Stub.h"
#include "Bind_Dispatcher_Guard.h"
#include "Pluggable_Messaging.h"
#include "Transport.h"
#include "Synch_Reply_Dispatcher.h"
#include "GIOP_Utils.h"
#include "Profile.h"
#include "ORB_Constants.h"

ACE_RCSID (tao,
           LocateRequest_Invocation,
           "LocateRequest_Invocation.cpp,v 1.2 2003/10/28 18:29:15 bala Exp")

namespace TAO
{
  LocateRequest_Invocation::LocateRequest_Invocation (
      CORBA::Object_ptr otarget,
      Profile_Transport_Resolver &resolver,
      TAO_Operation_Details &detail
    )
    : Synch_Twoway_Invocation (otarget,
                               resolver,
                               detail)
  {
  }

  Invocation_Status
  LocateRequest_Invocation::invoke (ACE_Time_Value *max_wait_time
                                    ACE_ENV_ARG_DECL)
    ACE_THROW_SPEC ((CORBA::Exception))
  {
    ACE_Countdown_Time countdown (max_wait_time);

    TAO_Synch_Reply_Dispatcher rd (this->resolver_.stub ()->orb_core (),
                                   this->details_.reply_service_info ());

    // Register a reply dispatcher for this invocation. Use the
    // preallocated reply dispatcher.
    TAO_Bind_Dispatcher_Guard dispatch_guard (this->details_.request_id (),
                                              &rd,
                                              this->resolver_.transport ()->tms ());

    if (dispatch_guard.status () != 0)
      {
        // @@ What is the right way to handle this error? Do we need
        // to call the interceptors in this case?
        this->resolver_.transport ()->close_connection ();

        ACE_THROW_RETURN (CORBA::INTERNAL (TAO_DEFAULT_MINOR_CODE,
                                           CORBA::COMPLETED_NO),
                          TAO_INVOKE_FAILURE);
      }

    TAO_Target_Specification tspec;
    this->init_target_spec (tspec ACE_ENV_ARG_PARAMETER);
    ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);

    TAO_Transport *transport =
      this->resolver_.transport ();

    TAO_OutputCDR &cdr =
      transport->messaging_object ()->out_stream ();

    int retval =
      transport->generate_locate_request (tspec,
                                          this->details_,
                                          cdr);
    ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);

    if (retval == -1)
      return TAO_INVOKE_FAILURE;

    countdown.update ();

    Invocation_Status s =
      this->send_message (cdr,
                          TAO_Transport::TAO_TWOWAY_REQUEST,
                          max_wait_time
                          ACE_ENV_ARG_PARAMETER);
    ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);

    if (s != TAO_INVOKE_SUCCESS)
      return s;

    countdown.update ();

    // For some strategies one may want to release the transport
    // back to  cache. If the idling is successfull let the
    // resolver about that.
    if (this->resolver_.transport ()->idle_after_send ())
      this->resolver_.transport_released ();

    s =
      this->wait_for_reply (max_wait_time,
                            rd,
                            dispatch_guard
                            ACE_ENV_ARG_PARAMETER);
    ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);

    s = this->check_reply (rd
                           ACE_ENV_ARG_PARAMETER);
    ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);

    // For some strategies one may want to release the transport
    // back to  cache after receiving the reply. If the idling is
    // successfull let the resolver about that.
    if (this->resolver_.transport ()->idle_after_reply ())
      this->resolver_.transport_released ();

    return s;
  }

  Invocation_Status
  LocateRequest_Invocation::check_reply (TAO_Synch_Reply_Dispatcher &rd
                                         ACE_ENV_ARG_DECL)
  {
    TAO_InputCDR &cdr =
      rd.reply_cdr ();

    // Set the translators
    this->resolver_.transport ()->assign_translators (&cdr, 0);

    switch (rd.reply_status ())
      {
      case TAO_GIOP_OBJECT_HERE:
        break;
      case TAO_GIOP_UNKNOWN_OBJECT:
        ACE_THROW_RETURN (CORBA::OBJECT_NOT_EXIST (TAO_DEFAULT_MINOR_CODE,
                                                   CORBA::COMPLETED_YES),
                          TAO_INVOKE_FAILURE);
      case TAO_GIOP_OBJECT_FORWARD:
      case TAO_GIOP_OBJECT_FORWARD_PERM:
        return this->location_forward (cdr
                                       ACE_ENV_ARG_PARAMETER);

      case TAO_GIOP_LOC_SYSTEM_EXCEPTION:
        {
          // Pull the exception from the stream.
          CORBA::String_var buf;

          if ((cdr >> buf.inout ()) == 0)
            {
              // Could not demarshal the exception id, raise a local
              // CORBA::MARSHAL exception.
              ACE_THROW_RETURN (CORBA::MARSHAL (TAO_DEFAULT_MINOR_CODE,
                                                CORBA::COMPLETED_MAYBE),
                                TAO_INVOKE_SYSTEM_EXCEPTION);
            }

          // This kind of exception shouldn't happen with locate requests,
          // but if it does, we turn it into a CORBA::UNKNOWN exception.
          ACE_THROW_RETURN (CORBA::UNKNOWN (TAO_DEFAULT_MINOR_CODE,
                                            CORBA::COMPLETED_YES),
                            TAO_INVOKE_SYSTEM_EXCEPTION);
        }
      case TAO_GIOP_LOC_NEEDS_ADDRESSING_MODE:
        {
          // We have received an exception with a request to change the
          // addressing mode. First let us read the mode that the
          // server/agent asks for.
          CORBA::Short addr_mode = 0;

          if (cdr.read_short (addr_mode) == 0)
            {
              // Could not demarshal the addressing disposition, raise a local
              // CORBA::MARSHAL exception.
              ACE_THROW_RETURN (CORBA::MARSHAL (TAO_DEFAULT_MINOR_CODE,
                                                CORBA::COMPLETED_MAYBE),
                                TAO_INVOKE_SUCCESS);
            }

          // Now set this addressing mode in the profile, so that
          // the next invocation need not go through this.
          this->resolver_.profile ()->addressing_mode (addr_mode
                                                       ACE_ENV_ARG_PARAMETER);
          ACE_CHECK_RETURN (TAO_INVOKE_SUCCESS);

          // Restart the invocation.
          return TAO_INVOKE_RESTART;
        }
      }

    return TAO_INVOKE_SUCCESS;
  }
}
