/*
 *  MICO --- an Open Source CORBA implementation
 *  Copyright (c) 1997-2010 by The Mico Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  For more information, visit the MICO Home Page at
 *  http://www.mico.org/
 */

#ifdef FAST_PCH
#include "orb_pch.h"
#endif // FAST_PCH
#ifdef __COMO__
#pragma hdrstop
#endif // __COMO__

#ifndef FAST_PCH

#define MICO_CONF_IMR

#include <CORBA-SMALL.h>
#ifndef _WIN32
#include <string.h>
#endif
#include <mico/template_impl.h>
#include <mico/util.h>
#include <mico/throw.h>
#ifdef USE_MEMTRACE
#include <mico/memtrace.h>
#endif
#ifdef HAVE_ANSI_CPLUSPLUS_HEADERS
#include <sstream>
#else // HAVE_ANSI_CPLUSPLUS_HEADERS
#include <strstream.h>
#endif // HAVE_ANSI_CPLUSPLUS_HEADERS
#ifdef USE_MESSAGING
#include <mico/messaging.h>
#endif // USE_MESSAGING

#ifdef HAVE_SOLARIS_ATOMICS
#include <atomic.h>
#endif // HAVE_SOLARIS_ATOMICS

#endif // FAST_PCH


using namespace std;

#ifdef USE_MESSAGING
CORBA::ULong
CORBA::Object::S_timeout_policy_instance_counter_ = 0;
MICOMT::RWLock
CORBA::Object::S_timeout_policy_instance_counter_lock_;
#endif // USE_MESSAGING

/*************************** MagicChecker ****************************/


void
CORBA::MagicChecker::_check () const
{
    if (!this || _magic != MICO_OBJ_MAGIC) {
#ifdef USE_MEMTRACE
	MemTrace_SelfTrace (stderr);
#endif
	mico_throw (BAD_PARAM());
    }
}

void
CORBA::MagicChecker::_check (const CORBA::Exception &ex) const
{
    if (!this || _magic != MICO_OBJ_MAGIC) {
#ifdef USE_MEMTRACE
	MemTrace_SelfTrace (stderr);
#endif
	((CORBA::Exception&)ex)._raise();
    }
}

CORBA::Boolean
CORBA::MagicChecker::_check_nothrow () const
{
    if (!this || _magic != MICO_OBJ_MAGIC) {
      if (MICO::Logger::IsLogged (MICO::Logger::Warning)) {
	MICO::Logger::Stream (MICO::Logger::Warning)
	  << "invalid object reference" << endl;
      }
#ifdef USE_MEMTRACE
      MemTrace_SelfTrace (stderr);
#endif
      return FALSE;
    }
    return TRUE;
}


/************************* ServerlessObject **************************/


CORBA::ServerlessObject::~ServerlessObject ()
{
}

void
CORBA::ServerlessObject::_ref ()
{
#if defined(HAVE_GCC_ATOMICS)
    _check();
    __sync_fetch_and_add(&refs, 1);
#elif defined(HAVE_SOLARIS_ATOMICS)
    _check();
    atomic_inc_32((uint32_t*)&refs);
#else
    MICOMT::AutoLock lock(refslock);
    _check ();
    ++refs;
#endif
}

CORBA::Boolean
CORBA::ServerlessObject::_deref ()
{
#if defined(HAVE_GCC_ATOMICS)
    return _check_nothrow() && __sync_sub_and_fetch(&refs, 1) <= 0;
#elif defined(HAVE_SOLARIS_ATOMICS)
    return _check_nothrow() && atomic_dec_32_nv((uint32_t*)&refs) <= 0;
#else
    MICOMT::AutoLock lock(refslock);
    return _check_nothrow() && --refs <= 0;
#endif // HAVE_GCC_ATOMICS
}

CORBA::Long
CORBA::ServerlessObject::_refcnt () const
{
    _check ();
    return refs;
}


/****************************** Object *******************************/


// consumes IOR* argument
CORBA::Object::Object (IOR *i)
{
    ior = i;
    fwd_ior = 0;
    _orb = CORBA::ORB_instance ("mico-local-orb", FALSE);
    if (!CORBA::is_nil(_orb) && !_orb->plugged() && ior)
	ior->addressing_disposition (GIOP::ReferenceAddr);
}

CORBA::Object::Object (const Object &o)
{
    ior = o.ior ? new IOR (*o.ior) : 0;
    fwd_ior = o.fwd_ior ? new IOR (*o.fwd_ior) : 0;
    _orb = CORBA::ORB::_duplicate (o._orb);
    _managers = o._managers;
    _policies = o._policies;
}

CORBA::Object &
CORBA::Object::operator= (const Object &o)
{
    MICO_OBJ_CHECK (this);

    if (this != &o) {
	if (ior)
	    delete ior;
	ior = o.ior ? new IOR (*o.ior) : 0;
	if (fwd_ior)
	    delete fwd_ior;
	fwd_ior = o.fwd_ior ? new IOR (*o.fwd_ior) : 0;
	CORBA::release (_orb);
	_orb = CORBA::ORB::_duplicate (o._orb);
	_managers = o._managers;
	_policies = o._policies;
    }
    return *this;
}

CORBA::Object::~Object ()
{
    if (ior)
        delete ior;
    if (fwd_ior)
        delete fwd_ior;
    CORBA::release (_orb);
}

void
CORBA::Object::_forward (CORBA::Object_ptr o)
{
    assert (!CORBA::is_nil (o) && o->ior);
    if (fwd_ior)
	delete fwd_ior;
    fwd_ior = new IOR (*o->ior);
}

void
CORBA::Object::_unforward ()
{
    if (fwd_ior) {
	delete fwd_ior;
	fwd_ior = 0;
    }
}

void
CORBA::Object::_setup_domains (CORBA::Object_ptr parent)
{
    if (CORBA::is_nil (parent)) {
	CORBA::DomainManager_var def_manager;
	this->_orbnc()->get_default_domain_manager (def_manager);

	_managers.length (1);

	CORBA::Policy_var p =
	    def_manager->_get_policy (CORBA::SecConstruction);
	assert (!CORBA::is_nil (p));

	CORBA::ConstructionPolicy_var cp =
	    CORBA::ConstructionPolicy::_narrow (p);

	if (cp->constr_policy (this)) {
	    _managers[0] = def_manager->copy();
	} else {
	    _managers[0] = def_manager._retn();
	}
    } else {
	CORBA::DomainManagerList_var dml = parent->_get_domain_managers();
	_managers.length (dml->length());

	for (CORBA::ULong i = 0; i < dml->length(); ++i) {
	    CORBA::Policy_var p =
		dml[i]->_get_policy (CORBA::SecConstruction);
	    assert (!CORBA::is_nil (p));

	    CORBA::ConstructionPolicy_var cp =
		CORBA::ConstructionPolicy::_narrow (p);

	    if (cp->constr_policy (this)) {
		_managers[i] = CORBA::DomainManager::_duplicate (dml[i]);
	    } else {
		_managers[i] = dml[i]->copy();
	    }
	}
    }
}

const char *
CORBA::Object::_ident ()
{
    IORProfile *prof = _ior()->profile (IORProfile::TAG_ANY);
    assert (prof);
    Long len;
    const Octet *key = prof->objectkey (len);
    ident = "";
    for (int i = 0; i < len; ++i) {
	ident += mico_to_xdigit ((key[i] >> 4) & 0xf);
	ident += mico_to_xdigit (key[i] & 0xf);
    }
    return ident.c_str();
}

void *
CORBA::Object::_narrow_helper (const char *)
{
    return NULL;
}

CORBA::ImplementationDef_ptr
CORBA::Object::_get_implementation ()
{
    return _orbnc()->get_impl (this);
}

CORBA::InterfaceDef_ptr
CORBA::Object::_get_interface ()
{
    MICO_OBJ_CHECK2 (this, CORBA::OBJECT_NOT_EXIST());

    return _orbnc()->get_iface (this);
}

CORBA::Object_ptr
CORBA::Object::_get_component ()
{
    MICO_OBJ_CHECK2 (this, CORBA::OBJECT_NOT_EXIST());

    return _orbnc()->get_component (this);
}

void
CORBA::Object::_create_request (Context_ptr ctx,
				const char *op,
				NVList_ptr args,
				NamedValue_ptr result,
				Request_out request,
				Flags flags)
{
    request = new Request (this, ctx, op, args, result, flags);
}

void
CORBA::Object::_create_request (Context_ptr ctx,
				const char *op,
				NVList_ptr args,
				NamedValue_ptr result,
				ExceptionList_ptr elist,
				ContextList_ptr clist,
				Request_out request,
				Flags flags)
{
    request = new Request (this, ctx, op, args, result, elist, clist, flags);
}

CORBA::Request_ptr
CORBA::Object::_request (const char *op)
{
    return new Request (this, op);
}

CORBA::Policy_ptr
CORBA::Object::_get_policy (PolicyType policy_type)
{
    Policy_var client_policy = this->_get_client_policy(policy_type);
    if (!CORBA::is_nil(client_policy)) {
        // kcg: here we should do policy reconciliation, but
        // currently MICO does not support such messaging related
        // policies which would require it, so let's just return the
        // obtained client policy
        return client_policy._retn();
    }
    // look at domain manager policies
    for (CORBA::ULong i1 = 0; i1 < _managers.length(); ++i1) {
	CORBA::Policy_ptr p = _managers[i1]->_get_policy (policy_type);
	if (!CORBA::is_nil (p))
	    return p;
    }
    mico_throw (CORBA::INV_POLICY());
    return CORBA::Policy::_nil();
}

CORBA::DomainManagerList *
CORBA::Object::_get_domain_managers ()
{
    return new DomainManagerList (_managers);
}

CORBA::Object_ptr
CORBA::Object::_set_policy_overrides (const PolicyList &policies,
				      SetOverrideType set_add)
{
    CORBA::Object_ptr nobj = new CORBA::Object (*this);
    switch (set_add) {
    case CORBA::SET_OVERRIDE:
	nobj->_policies = policies;
	break;

    case CORBA::ADD_OVERRIDE: {
	for (CORBA::ULong i = 0; i < policies.length(); ++i) {
	    CORBA::ULong j;
	    for (j = 0; j < nobj->_policies.length(); ++j) {
		if (nobj->_policies[j]->policy_type() ==
		      policies[i]->policy_type()) {
		    nobj->_policies[j] =
			CORBA::Policy::_duplicate (policies[i].in());
		    break;
		}
	    }
	    if (j == nobj->_policies.length()) {
		nobj->_policies.length (j+1);
		nobj->_policies[j] = CORBA::Policy::_duplicate (policies[i].in());
	    }
	}
	break;
    }
    default:
	assert (0);
    }
    return nobj;
}

CORBA::Policy_ptr
CORBA::Object::_get_client_policy(PolicyType policy_type)
{
    // search object scope first
    for (CORBA::ULong i0 = 0; i0 < _policies.length(); ++i0) {
	if (_policies[i0]->policy_type() == policy_type)
	    return CORBA::Policy::_duplicate (_policies[i0]);
    }
    // thread scope as a second
    Object_var obj = this->_orbnc()->resolve_initial_references("PolicyCurrent");
    PolicyCurrent_var current = PolicyCurrent::_narrow(obj);
    assert(!CORBA::is_nil(current));
    PolicyTypeSeq pts;
    pts.length(1);
    pts[0] = policy_type;
    PolicyList_var pl = current->get_policy_overrides(pts);
    assert(pl->length() == 0 || pl->length() == 1);
    if (pl->length() == 1)
        return Policy::_duplicate(pl[(CORBA::ULong)0]);
    // global ORB scope as a third
    obj = this->_orbnc()->resolve_initial_references("ORBPolicyManager");
    PolicyManager_var manager = PolicyManager::_narrow(obj);
    assert(!CORBA::is_nil(manager));
    pl = manager->get_policy_overrides(pts);
    assert(pl->length() == 0 || pl->length() == 1);
    if (pl->length() == 1)
        return Policy::_duplicate(pl[(CORBA::ULong)0]);
    return Policy::_nil();
}

CORBA::PolicyList*
CORBA::Object::_get_policy_overrides(const PolicyTypeSeq& types)
{
    if (types.length() == 0)
        return new CORBA::PolicyList(_policies);
    
    PolicyList_var retval = new PolicyList;
    for (ULong i = 0; i < types.length(); i++) {
        for (ULong j = 0; j < this->_policies.length(); j++) {
            if (this->_policies[j]->policy_type() == types[i]) {
                retval->length(retval->length() + 1);
                retval[retval->length() - 1] = Policy::_duplicate(this->_policies[j]);
            }
        }
    }
    return retval._retn();
}

CORBA::Boolean
CORBA::Object::_validate_connection(CORBA::PolicyList_out inconsistent_policies)
{
    return this->_orbnc()->validate_connection(this, inconsistent_policies);
}

CORBA::Boolean
CORBA::Object::_is_a (const char *repoid)
{
    MICO_OBJ_CHECK2 (this, CORBA::OBJECT_NOT_EXIST());

    // check some trivial cases first
    if (!strcmp (repoid, "IDL:omg.org/CORBA/Object:1.0"))
	return TRUE;
    if (ior && !strcmp (repoid, _repoid()))
	return TRUE;

    // try IDL compiler generated narrowing support
    if (_narrow_helper (repoid))
	return TRUE;

    return _is_a_remote (repoid);
}

CORBA::Boolean
CORBA::Object::_is_a_remote (const char *repoid)
{
    // only ask remote if we are a stub
    if (_orbnc()->is_impl (this))
	return FALSE;
    if (!ior)
        return FALSE;
    return _orbnc()->is_a (this, repoid);
}

CORBA::Boolean
CORBA::Object::_non_existent ()
{
    if (CORBA::is_nil (this))
	return FALSE;

    if (!_check_nothrow())
	return TRUE;

    return _orbnc()->non_existent (this);
}

CORBA::Boolean
CORBA::Object::_is_equivalent (Object_ptr o)
{
    if (CORBA::is_nil (this) && CORBA::is_nil (o))
	return TRUE;

    if (!CORBA::is_nil (this))
	MICO_OBJ_CHECK2 (this, CORBA::OBJECT_NOT_EXIST());

    if (!CORBA::is_nil (o))
	MICO_OBJ_CHECK2 (o, CORBA::OBJECT_NOT_EXIST());

    if (CORBA::is_nil (this) || CORBA::is_nil (o))
	return FALSE;

    if (this == o)
	return TRUE;
    return *_ior() == *o->_ior();
}

CORBA::ULong
CORBA::Object::_hash (ULong max)
{
    MICO_OBJ_CHECK2 (this, CORBA::OBJECT_NOT_EXIST());

    string s = _ior()->stringify();
    return mico_string_hash (s.c_str(), max);
}

CORBA::ORB_ptr
CORBA::Object::_orbnc()
{
    if (CORBA::is_nil(_orb))
	_orb = CORBA::ORB_instance ("mico-local-orb");
    return _orb;
}

#ifdef USE_MESSAGING
CORBA::ULong
CORBA::Object::relative_roundtrip_timeout()
{
    MICOMT::AutoRDLock lock(S_timeout_policy_instance_counter_lock_);
    if (S_timeout_policy_instance_counter_ > 0) {
        try {
            Policy_var pol = this->_get_policy(Messaging::RELATIVE_RT_TIMEOUT_POLICY_TYPE);
            Messaging::RelativeRoundtripTimeoutPolicy_var tpol
                = Messaging::RelativeRoundtripTimeoutPolicy::_narrow(pol);
            assert(!is_nil(tpol));
            // needs to convert TimeBase::TimeT which is in 0.1us (100 ns)
            // into ms
            // There is an assumption that max timeout is 2^31 ms
            return tpol->relative_expiry() / 10000;
        }
        catch (const CORBA::INV_POLICY&) {
        }
    }
    return 0;
}

void
CORBA::Object::increase_timeout_policy_instance_counter()
{
    MICOMT::AutoWRLock lock(S_timeout_policy_instance_counter_lock_);
    S_timeout_policy_instance_counter_++;
}

void
CORBA::Object::decrease_timeout_policy_instance_counter()
{
    MICOMT::AutoWRLock lock(S_timeout_policy_instance_counter_lock_);
    S_timeout_policy_instance_counter_--;
}
#endif // USE_MESSAGING

// ref-counting added in ptc/03-03-09

void
CORBA::LocalObject::_add_ref()
{
    this->_ref();
}

void
CORBA::LocalObject::_remove_ref()
{
    if (this->_deref())
	delete this;
}

CORBA::ULong
CORBA::LocalObject::_refcount_value() const
{
    return this->_refcnt();
}

// non-implemented methods of CORBA::Object

CORBA::InterfaceDef*
CORBA::LocalObject::_get_interface()
{
    mico_throw(CORBA::NO_IMPLEMENT());
    // never reached, just to avoid warning
    return NULL;
}

CORBA::DomainManagerList*
CORBA::LocalObject::_get_domain_managers()
{
    mico_throw(CORBA::NO_IMPLEMENT());
    // never reached, just to avoid warning
    return NULL;
}

CORBA::Policy_ptr
CORBA::LocalObject::_get_policy(PolicyType __policy_type)
{
    mico_throw(CORBA::NO_IMPLEMENT());
    // never reached, just to avoid warning
    return CORBA::Policy::_nil();
}

CORBA::Object_ptr
CORBA::LocalObject::_set_policy_overrides
(const PolicyList& __policies, SetOverrideType __set_add)
{
    mico_throw(CORBA::NO_IMPLEMENT());
    // never reached, just to avoid warning
    return CORBA::Object::_nil();
}

CORBA::ULong
CORBA::LocalObject::_hash(CORBA::ULong __max)
{
    // we simply hash string which represent
    // address of this object e.g '0xbffff5e8'
#ifdef HAVE_ANSI_CPLUSPLUS_HEADERS
    stringstream __ss;
#else // HAVE_ANSI_CPLUSPLUS_HEADERS
    ostrstream __ss;
#endif // HAVE_ANSI_CPLUSPLUS_HEADERS
    __ss << hex << (void*)this;
    string __str = __ss.str();
    return mico_string_hash(__str.c_str(), __max);
}

CORBA::Boolean
CORBA::LocalObject::_is_equivalent(CORBA::Object_ptr __obj)
{
    return this == __obj;
}