// Any_Unknown_IDL_Type.cpp,v 1.8 2004/06/23 00:54:02 bala Exp

#include "tao/Any_Unknown_IDL_Type.h"
#include "tao/Valuetype_Adapter.h"
#include "tao/ORB_Core.h"
#include "tao/Marshal.h"
#include "tao/Typecode.h"
#include "tao/CDR.h"

#include "ace/Dynamic_Service.h"
#include "ace/OS_NS_string.h"


ACE_RCSID (tao,
           Any_Unknown_IDL_Type,
           "Any_Unknown_IDL_Type.cpp,v 1.8 2004/06/23 00:54:02 bala Exp")


TAO::Unknown_IDL_Type::Unknown_IDL_Type (
    CORBA::TypeCode_ptr tc,
    const ACE_Message_Block *mb,
    int byte_order,
    ACE_Char_Codeset_Translator *ctrans,
    ACE_WChar_Codeset_Translator *wtrans
  )
  : TAO::Any_Impl (0, tc),
    cdr_ (0),
    byte_order_ (byte_order),
    char_translator_ (ctrans),
    wchar_translator_ (wtrans)
{
  if (mb != 0)
    {
      ACE_NEW (this->cdr_,
               ACE_Message_Block (*mb,
                                  ACE_CDR::MAX_ALIGNMENT));

      // Align the base pointer assuming that the incoming stream is also
      // aligned the way we are aligned
      char *start =
        ACE_ptr_align_binary (mb->base (),
                              ACE_CDR::MAX_ALIGNMENT);

      size_t newrdpos =
        mb->rd_ptr() - start;

      size_t newwrpos =
        mb->wr_ptr() - start;

      if (newwrpos <= this->cdr_->space ())
        {
          // Notice that ACE_Message_Block::duplicate may leave the
          // wr_ptr() with a higher value than what we actually want.
          this->cdr_->rd_ptr (newrdpos);
          this->cdr_->wr_ptr (newwrpos);
        }
      else
        {
          ACE_ERROR ((LM_ERROR,
                      "TAO(%P|%t) - Unknown_IDL_Type::Unknown_IDL_Type "
                      "Couldn't align read and write pointers \n"));
        }
    }
}

TAO::Unknown_IDL_Type::~Unknown_IDL_Type (void)
{
}

CORBA::Boolean
TAO::Unknown_IDL_Type::marshal_value (TAO_OutputCDR &cdr)
{
  ACE_TRY_NEW_ENV
    {
      TAO_InputCDR input (this->cdr_,
                          this->byte_order_);

      TAO::traverse_status status =
        TAO_Marshal_Object::perform_append (this->type_,
                                            &input,
                                            &cdr
                                            ACE_ENV_ARG_PARAMETER);
      ACE_TRY_CHECK;

      if (status != TAO::TRAVERSE_CONTINUE)
        {
          return 0;
        }
    }
  ACE_CATCH (CORBA::Exception, ex)
    {
      return 0;
    }
  ACE_ENDTRY;

  return 1;
}

const void *
TAO::Unknown_IDL_Type::value (void) const
{
  return this->cdr_;
}

void
TAO::Unknown_IDL_Type::free_value (void)
{
  CORBA::release (this->type_);
  delete this->cdr_;
}

ACE_Message_Block *
TAO::Unknown_IDL_Type::_tao_get_cdr (void) const
{
  return this->cdr_;
}

int
TAO::Unknown_IDL_Type::_tao_byte_order (void) const
{
  return this->byte_order_;
}
void
TAO::Unknown_IDL_Type::_tao_decode (TAO_InputCDR &cdr
                                    ACE_ENV_ARG_DECL)
{
  // @@ (JP) The following code depends on the fact that
  //         TAO_InputCDR does not contain chained message blocks,
  //         otherwise <begin> and <end> could be part of
  //         different buffers!

  // This will be the start of a new message block.
  char *begin = cdr.rd_ptr ();

  // Skip over the next argument.
  TAO::traverse_status status =
    TAO_Marshal_Object::perform_skip (this->type_,
                                      &cdr
                                      ACE_ENV_ARG_PARAMETER);
  ACE_CHECK;

  if (status != TAO::TRAVERSE_CONTINUE)
    {
      ACE_THROW (CORBA::MARSHAL ());
    }

  // This will be the end of the new message block.
  char *end = cdr.rd_ptr ();

  // The ACE_CDR::mb_align() call can shift the rd_ptr by up to
  // ACE_CDR::MAX_ALIGNMENT - 1 bytes. Similarly, the offset adjustment
  // can move the rd_ptr by up to the same amount. We accommodate
  // this by including 2 * ACE_CDR::MAX_ALIGNMENT bytes of additional
  // space in the message block.
  size_t size = end - begin;

  ACE_Message_Block::release (this->cdr_);
  ACE_NEW (this->cdr_,
           ACE_Message_Block (size + 2 * ACE_CDR::MAX_ALIGNMENT));

  ACE_CDR::mb_align (this->cdr_);
  ptrdiff_t offset = ptrdiff_t (begin) % ACE_CDR::MAX_ALIGNMENT;

  if (offset < 0)
    offset += ACE_CDR::MAX_ALIGNMENT;

  this->cdr_->rd_ptr (offset);
  this->cdr_->wr_ptr (offset + size);

  ACE_OS::memcpy (this->cdr_->rd_ptr (),
                  begin,
                  size);

  this->byte_order_ = cdr.byte_order ();

  // Get character translators.
  this->char_translator_ = cdr.char_translator();
  this->wchar_translator_ = cdr.wchar_translator();
}

void
TAO::Unknown_IDL_Type::assign_translator (CORBA::TypeCode_ptr tc,
                                          TAO_InputCDR *cdr
                                          ACE_ENV_ARG_DECL)
{
  CORBA::TCKind kind = tc->kind (ACE_ENV_SINGLE_ARG_PARAMETER);
  ACE_CHECK;

  switch (kind)
    {
      case CORBA::tk_string:
      case CORBA::tk_char:
        cdr->char_translator (this->char_translator_);
        break;
      case CORBA::tk_wstring:
      case CORBA::tk_wchar:
        cdr->wchar_translator(this->wchar_translator_);
        break;
      default:
        break;
    }
}

CORBA::Boolean
TAO::Unknown_IDL_Type::to_object (CORBA::Object_ptr &obj) const
{
  ACE_TRY_NEW_ENV
    {
      CORBA::ULong kind =
        this->type_->kind (ACE_ENV_SINGLE_ARG_PARAMETER);
      ACE_TRY_CHECK;

      CORBA::TypeCode_var tcvar =
        CORBA::TypeCode::_duplicate (this->type_);

      while (kind == CORBA::tk_alias)
        {
          tcvar = tcvar->content_type (ACE_ENV_SINGLE_ARG_PARAMETER);
          ACE_TRY_CHECK;

          kind = tcvar->kind (ACE_ENV_SINGLE_ARG_PARAMETER);
          ACE_TRY_CHECK;
        }

      if (kind != CORBA::tk_objref)
        {
          return 0;
        }

      TAO_InputCDR stream (this->cdr_,
                           this->byte_order_,
                           TAO_DEF_GIOP_MAJOR,
                           TAO_DEF_GIOP_MINOR,
                           0);

      return stream >> obj;
    }
  ACE_CATCH (CORBA::Exception, ex)
    {
    }
  ACE_ENDTRY;

  return 0;
}

CORBA::Boolean
TAO::Unknown_IDL_Type::to_value (CORBA::ValueBase *&val) const
{
  ACE_TRY_NEW_ENV
    {
      CORBA::ULong kind =
        this->type_->kind (ACE_ENV_SINGLE_ARG_PARAMETER);
      ACE_TRY_CHECK;

      CORBA::TypeCode_var tcvar =
        CORBA::TypeCode::_duplicate (this->type_);

      while (kind == CORBA::tk_alias)
        {
          tcvar = tcvar->content_type (ACE_ENV_SINGLE_ARG_PARAMETER);
          ACE_TRY_CHECK;

          kind = tcvar->kind (ACE_ENV_SINGLE_ARG_PARAMETER);
          ACE_TRY_CHECK;
        }

      if (kind != CORBA::tk_value)
        {
          return 0;
        }

      TAO_InputCDR stream (this->cdr_,
                           this->byte_order_,
                           TAO_DEF_GIOP_MAJOR,
                           TAO_DEF_GIOP_MINOR,
                           0);

      TAO_Valuetype_Adapter *adapter =
        ACE_Dynamic_Service<TAO_Valuetype_Adapter>::instance (
            TAO_ORB_Core::valuetype_adapter_name ()
          );

      if (adapter == 0)
        {
          ACE_THROW_RETURN (CORBA::INTERNAL (),
                            0);
        }

      return adapter->stream_to_value (stream,
                                       val);
    }
  ACE_CATCH (CORBA::Exception, ex)
    {
    }
  ACE_ENDTRY;

  return 0;
}

CORBA::Boolean
TAO::Unknown_IDL_Type::to_abstract_base (CORBA::AbstractBase_ptr &obj) const
{
  ACE_TRY_NEW_ENV
    {
      CORBA::ULong kind =
        this->type_->kind (ACE_ENV_SINGLE_ARG_PARAMETER);
      ACE_TRY_CHECK;

      CORBA::TypeCode_var tcvar =
        CORBA::TypeCode::_duplicate (this->type_);

      while (kind == CORBA::tk_alias)
        {
          tcvar = tcvar->content_type (ACE_ENV_SINGLE_ARG_PARAMETER);
          ACE_TRY_CHECK;

          kind = tcvar->kind (ACE_ENV_SINGLE_ARG_PARAMETER);
          ACE_TRY_CHECK;
        }

      if (kind != CORBA::tk_value)
        {
          return 0;
        }

      TAO_InputCDR stream (this->cdr_,
                           this->byte_order_,
                           TAO_DEF_GIOP_MAJOR,
                           TAO_DEF_GIOP_MINOR,
                           TAO_ORB_Core_instance ());

      TAO_Valuetype_Adapter *adapter =
        ACE_Dynamic_Service<TAO_Valuetype_Adapter>::instance (
            TAO_ORB_Core::valuetype_adapter_name ()
          );

      if (adapter == 0)
        {
          ACE_THROW_RETURN (CORBA::INTERNAL (),
                            0);
        }

      return adapter->stream_to_abstract_base (stream,
                                               obj);
    }
  ACE_CATCH (CORBA::Exception, ex)
    {
    }
  ACE_ENDTRY;

  return 0;
}
