// $Id: PICurrent_Impl.cpp 91628 2010-09-07 11:11:12Z johnnyw $

#include "tao/PI/PICurrent_Impl.h"

#if TAO_HAS_INTERCEPTORS == 1

#include "tao/ORB_Core.h"

#if !defined (__ACE_INLINE__)
# include "tao/PI/PICurrent_Impl.inl"
#endif /* __ACE_INLINE__ */

#include "tao/TAO_Server_Request.h"
#include "ace/Log_Msg.h"
#include "tao/debug.h"
#include "ace/CORBA_macros.h"

TAO_BEGIN_VERSIONED_NAMESPACE_DECL

CORBA::Any *
TAO::PICurrent_Impl::get_slot (PortableInterceptor::SlotId identifier)
{
  // No need to check validity of SlotId.  It is validated before this
  // method is invoked.

  // The active slot table should never be a lazy copy of itself!
  if ( (0 != this->lazy_copy_)
      && (&this->lazy_copy_->current_slot_table () == &this->slot_table_))
  {
    if (TAO_debug_level > 0)
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("TAO (%P|%t) Lazy copy of self detected at %N,%l\n")));
    throw ::CORBA::INTERNAL ();
  }

  // Get the slot table that is currently active
  PICurrent_Impl::Table & table = this->current_slot_table ();
  CORBA::Any * any = 0;

  if (identifier < table.size ())
    {
      ACE_NEW_THROW_EX (any,
                        CORBA::Any (table[identifier]), // Make a copy.
                        CORBA::NO_MEMORY (
                          CORBA::SystemException::_tao_minor_code (
                            0,
                            ENOMEM),
                          CORBA::COMPLETED_NO));
    }
  else
    {
      // In accordance with the Portable Interceptor specification,
      // return an Any with a TCKind of tk_null.  A default
      // constructed Any has that TCKind.
      ACE_NEW_THROW_EX (any,
                        CORBA::Any,
                        CORBA::NO_MEMORY (
                          CORBA::SystemException::_tao_minor_code (
                            0,
                            ENOMEM),
                          CORBA::COMPLETED_NO));
    }

  return any;
}

void
TAO::PICurrent_Impl::set_slot (PortableInterceptor::SlotId identifier,
                               const CORBA::Any & data)
{
  // No need to check validity of SlotId.  It is validated before this
  // method is invoked.

  // Break any existing ties that another PICurrent has with our table
  // since our table is changing.
  if (0 != this->impending_change_callback_)
    this->impending_change_callback_->convert_from_lazy_to_real_copy ();

  // Ensure that we have a real physical copy of the table before
  // making any changes to it.
  this->convert_from_lazy_to_real_copy ();

  // If the slot table array isn't large enough, then increase its
  // size.  We're guaranteed not to exceed the number of allocated
  // slots for the reason stated above.
  if (identifier >= this->slot_table_.size ()
      && this->slot_table_.size (identifier + 1) != 0)
    throw ::CORBA::INTERNAL ();

  this->slot_table_[identifier] = CORBA::Any (data);
}

void
TAO::PICurrent_Impl::take_lazy_copy (
  TAO::PICurrent_Impl * p)
{
  // Check that we are being told to actually change which table we are
  // copying from. (If it is the same as before OR it would ultimately be
  // the same table, we are already correctly setup and we do nothing.)
  if ( (p != this->lazy_copy_)
      && ((0 == p) || (&p->current_slot_table () != &this->current_slot_table ()))
      )
    {
      // Break any existing ties that another PICurrent has with our table
      // since our table is changing.
      if (0 != this->impending_change_callback_)
        this->impending_change_callback_->convert_from_lazy_to_real_copy ();

      // If we have previously logically copied another table, ensure it is
      // told that we are no longer interested in it so that it will not
      // call our conver_from_lazy_to_real_copy() when it changes/destructs.
      if (0 != this->lazy_copy_)
        this->lazy_copy_->set_callback_for_impending_change (0);

      // Are we being asked to copy ourself (or nothing)
      if ((0 == p) || (this == p))
        {
          this->lazy_copy_ = 0; // Use our own physical slot_table_
        }
      else
        {
          this->lazy_copy_ = p;

          // Must tell the newly copied PICurrent_Impl that we want to
          // be told when/if it is going to be changed or destroyed.
          this->lazy_copy_->set_callback_for_impending_change (this);
        }
    }
}

TAO::PICurrent_Impl::~PICurrent_Impl ()
{
  if (this->push_)
    {
      // We have YOUNGER stack members to REMOVE as well. HOWEVER we
      // don't want the entry above us coming back down and trying
      // to delete us again. (As we are currently doing just that.)
      this->push_->pop_= 0;

      delete this->push_;
    }
  else if (this->orb_core_)
    {
      // Since there are no entries above us, we must be the top of
      // the stack and since all are being deleted, the stack will
      // be empty.
      this->orb_core_->set_tss_resource (this->tss_slot_, 0);
    }

  // Break any existing ties that another PICurrent has with our table
  // since our table will no longer exists once this destructor completes.
  if (0 != this->impending_change_callback_)
    this->impending_change_callback_->convert_from_lazy_to_real_copy ();

  // If we have logically copied another table, ensure it is told about our
  // demise so that it will not call our non-existant
  // convert_from_lazy_to_real_copy() when it changes/destructs.
  if (0 != this->lazy_copy_)
    this->lazy_copy_->set_callback_for_impending_change (0);

  if (this->pop_)
    {
      // We have OLDER stack members to REMOVE as well. HOWEVER we
      // don't want multiple adjustments of the stack head pointer from
      // every older entry as they delete, as this requires multiple
      // set_tss_resource updates which would be slow and unnecessary.
      this->pop_->orb_core_= 0;

      // We also don't want double deletions of what used to be above these
      // since we have just completed the deletion of these ourselves.
      this->pop_->push_= 0;
      delete this->pop_;
    }
}

void
TAO::PICurrent_Impl::push (void)
{
  if (this->orb_core_) // We have a stack to adjust
    {
      TAO::PICurrent_Impl *const currentHead =
        static_cast<TAO::PICurrent_Impl *> (
          this->orb_core_->get_tss_resource (this->tss_slot_));
      if (!currentHead->push_)
        {
          // Since there is nothing younger above us, we need to create
          // a new entry.
          ACE_NEW_THROW_EX (currentHead->push_,
                            PICurrent_Impl (this->orb_core_, this->tss_slot_, currentHead),
                            CORBA::NO_MEMORY (
                              CORBA::SystemException::_tao_minor_code (
                                0,
                                ENOMEM),
                              CORBA::COMPLETED_NO));
        }
      this->orb_core_->set_tss_resource (this->tss_slot_, currentHead->push_);
    }
  else
    throw ::CORBA::INTERNAL (); // Should only call push if we have a stack
}

void
TAO::PICurrent_Impl::pop (void)
{
  if (this->orb_core_) // We have a stack to adjust
    {
      TAO::PICurrent_Impl *const currentHead =
        static_cast<TAO::PICurrent_Impl *> (
          this->orb_core_->get_tss_resource (this->tss_slot_));
      if (currentHead->pop_)
        orb_core_->set_tss_resource (tss_slot_, currentHead->pop_);
      else
        throw ::CORBA::INTERNAL (); // Too many pop's
    }
  else
    throw ::CORBA::INTERNAL (); // Should only call push if we have a stack
}

TAO_END_VERSIONED_NAMESPACE_DECL

#endif  /* TAO_HAS_INTERCEPTORS == 1 */
