// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/Basic.h>
#include <OB/Except.h>
#include <OB/Template.h>
#include <OB/TemplateI.h>
#include <OB/Declarations.h>
#include <OB/Any.h>
#include <OB/TCKind.h>
#include <OB/TypeCode.h>
#include <OB/Environment.h>
#include <OB/Context.h>
#include <OB/IOP.h>
#include <OB/Object.h>
#include <OB/IntRep.h>
#include <OB/ORB.h>
#include <OB/NamedValue.h>
#include <OB/DII.h>

// ----------------------------------------------------------------------
// External, non-inline duplicate/release for templates
// ----------------------------------------------------------------------

void
OBDuplicate(CORBA_ExceptionList_ptr p)
{
    if(p)
	p -> _OB_incRef();
}

void
OBRelease(CORBA_ExceptionList_ptr p)
{
    if(p)
	p -> _OB_decRef();
}

void
OBDuplicate(CORBA_ContextList_ptr p)
{
    if(p)
	p -> _OB_incRef();
}

void
OBRelease(CORBA_ContextList_ptr p)
{
    if(p)
	p -> _OB_decRef();
}

void
OBDuplicate(CORBA_Request_ptr p)
{
    if(p)
	p -> _OB_incRef();
}

void
OBRelease(CORBA_Request_ptr p)
{
    if(p)
	p -> _OB_decRef();
}

// ----------------------------------------------------------------------
// Template instantiations
// ----------------------------------------------------------------------

#ifndef HAVE_NO_EXPLICIT_TEMPLATES
template class OBObjVar< CORBA_ExceptionList >;
template class OBObjForSeq< CORBA_ExceptionList >;
//template class OBObjSeq< CORBA_ExceptionList >;
//template class OBSeqVar< OBObjSeq< CORBA_ExceptionList > >;

template class OBObjVar< CORBA_ContextList >;
template class OBObjForSeq< CORBA_ContextList >;
//template class OBObjSeq< CORBA_ContextList >;
//template class OBSeqVar< OBObjSeq< CORBA_ContextList > >;

template class OBObjVar< CORBA_Request >;
template class OBObjForSeq< CORBA_Request >;
template class OBObjSeq< CORBA_Request >;
template class OBSeqVar< OBObjSeq< CORBA_Request > >;
#else
#ifdef HAVE_PRAGMA_DEFINE
#pragma define(OBObjVar< CORBA_ExceptionList >)
#pragma define(OBObjForSeq< CORBA_ExceptionList >)
//#pragma define(OBObjSeq< CORBA_ExceptionList >)
//#pragma define(OBSeqVar< OBObjSeq< CORBA_ExceptionList > >)

#pragma define(OBObjVar< CORBA_ContextList >)
#pragma define(OBObjForSeq< CORBA_ContextList >)
//#pragma define(OBObjSeq< CORBA_ContextList >)
//#pragma define(OBSeqVar< OBObjSeq< CORBA_ContextList > >)

#pragma define(OBObjVar< CORBA_Request >)
#pragma define(OBObjForSeq< CORBA_Request >)
#pragma define(OBObjSeq< CORBA_Request >)
#pragma define(OBSeqVar< OBObjSeq< CORBA_Request > >)
#endif
#endif

// ----------------------------------------------------------------------
// ExceptionList public member implementation
// ----------------------------------------------------------------------

CORBA_ULong
CORBA_ExceptionList::count() const
{
    return typeCodeSeq_.length();
}

void
CORBA_ExceptionList::add(CORBA_TypeCode_ptr tc)
{
    typeCodeSeq_.append(CORBA_TypeCode::_duplicate(tc));
}

void
CORBA_ExceptionList::add_consume(CORBA_TypeCode_ptr tc)
{
    typeCodeSeq_.append(tc);
}

CORBA_TypeCode_ptr
CORBA_ExceptionList::item(CORBA_ULong n)
{
    return typeCodeSeq_[n];
}

CORBA_Status
CORBA_ExceptionList::remove(CORBA_ULong n)
{
    typeCodeSeq_.remove(n);
}

// ----------------------------------------------------------------------
// ContextList public member implementation
// ----------------------------------------------------------------------

CORBA_ULong
CORBA_ContextList::count() const
{
    return stringSeq_.length();
}

void
CORBA_ContextList::add(const char* ctxt)
{
    assert_nca(ctxt, OBNCANullString);
    stringSeq_.append(ctxt);
}

void
CORBA_ContextList::add_consume(char* ctxt)
{
    assert_nca(ctxt, OBNCANullString);
    stringSeq_.append(ctxt);
}

const char*
CORBA_ContextList::item(CORBA_ULong n)
{
    return stringSeq_[n];
}

CORBA_Status
CORBA_ContextList::remove(CORBA_ULong n)
{
    stringSeq_.remove(n);
}

// ----------------------------------------------------------------------
// Request private member functions
// ----------------------------------------------------------------------

void
CORBA_Request::marshal(OBBuffer& buf)
{
    OBClient_var clt = target_ -> _OB_client();
    if(CORBA_is_nil(clt))
        throw CORBA_NO_IMPLEMENT();

    CORBA_ULong offset = clt -> offset(target_, operation_);
    CORBA_ULong count = offset;
    CORBA_ULong i;

    for(i = 0 ; i < arguments_ -> count() ; i++)
    {
	CORBA_NamedValue_ptr nv = arguments_ -> item(i);
	if(nv -> flags() != CORBA_ARG_OUT)
	{
	    CORBA_Any* any = nv -> value();
	    OBMarshalCountNoTypeCode(*any, count);
	}
    }

    OBStrSeq ctxSeq;
    if(!CORBA_is_nil(ctx_))
    {
	for(i = 0 ; i < contexts_ -> count() ; i++)
	{
	    const char* item = contexts_ -> item(i);
	    ctx_ -> _OB_getValues("", 0, item, ctxSeq);
	}

	OBMarshalCount(ctxSeq, count);
    }

    buf.alloc(count);
    CORBA_Octet* oct = buf.data + offset;

    for(i = 0 ; i < arguments_ -> count() ; i++)
    {
	CORBA_NamedValue_ptr nv = arguments_ -> item(i);
	if(nv -> flags() != CORBA_ARG_OUT)
	{
	    CORBA_Any* any = nv -> value();
	    OBMarshalNoTypeCode(*any, oct);
	}
    }

    if(!CORBA_is_nil(ctx_))
	OBMarshal(ctxSeq, oct);
}

void
CORBA_Request::unmarshal(const OBBuffer& cbuf, CORBA_ULong offset,
			 bool swap, bool exc)
{
    const CORBA_Octet* coct = cbuf.data + offset;

    if(exc)
    {
	CORBA_String_var id;
	const CORBA_Octet* coctid = coct;
	OBUnmarshal(id.inout(), coctid, swap);
	
	for(CORBA_ULong i = 0 ; i < exceptions_ -> count() ; i++)
	{
	    CORBA_TypeCode_ptr tc = exceptions_ -> item(i);
	    if(strcmp(tc -> id(), id) == 0)
	    {
		CORBA_UnknownUserException* ex =
		    new CORBA_UnknownUserException;
		CORBA_Any& any = ex -> exception();
		OBUnmarshalNoTypeCode(any, coct, swap, tc);
		environment_ -> exception(ex);
		return;
	    }
	}
	
	environment_ -> exception(new CORBA_UNKNOWN);
	return;
    }

    CORBA_TypeCode_var tc;
    CORBA_Any* any;
    
    any = result_ -> value();
    tc = any -> type();
    OBUnmarshalNoTypeCode(*any, coct, swap, tc);
    
    for(CORBA_ULong i = 0 ; i < arguments_ -> count() ; i++)
    {
	CORBA_NamedValue_ptr nv = arguments_ -> item(i);
	if(nv -> flags() != CORBA_ARG_IN)
	{
	    any = nv -> value();
	    tc = any -> type();
	    OBUnmarshalNoTypeCode(*any, coct, swap, tc);
	}
    }
}

// ----------------------------------------------------------------------
// Request constructor and destructor
// ----------------------------------------------------------------------

CORBA_Request::CORBA_Request(CORBA_Object_ptr target,
			     const char* operation,
			     CORBA_NVList_ptr arguments,
			     CORBA_NamedValue_ptr result,
			     CORBA_ExceptionList_ptr exceptions,
			     CORBA_ContextList_ptr contexts)
    : target_(CORBA_Object::_duplicate(target)),
      operation_(operation),
      arguments_(CORBA_NVList::_duplicate(arguments)),
      result_(CORBA_NamedValue::_duplicate(result)),
      environment_(new CORBA_Environment),
      exceptions_(CORBA_ExceptionList::_duplicate(exceptions)),
      contexts_(CORBA_ContextList::_duplicate(contexts)),
      reqId_(0)
{
}

CORBA_Request::CORBA_Request(CORBA_Object_ptr target,
			     const char* operation,
			     CORBA_NVList_ptr arguments,
			     CORBA_NamedValue_ptr result)
    : target_(CORBA_Object::_duplicate(target)),
      operation_(operation),
      arguments_(CORBA_NVList::_duplicate(arguments)),
      result_(CORBA_NamedValue::_duplicate(result)),
      environment_(new CORBA_Environment),
      exceptions_(new CORBA_ExceptionList),
      contexts_(new CORBA_ContextList),
      reqId_(0)
{
}

CORBA_Request::CORBA_Request(CORBA_Object_ptr target,
			     const char* operation)
    : target_(CORBA_Object::_duplicate(target)),
      operation_(operation),
      arguments_(new CORBA_NVList),
      result_(new CORBA_NamedValue(CORBA_string_dup(""),
				   new CORBA_Any, CORBA_ARG_OUT)),
      environment_(new CORBA_Environment),
      exceptions_(new CORBA_ExceptionList),
      contexts_(new CORBA_ContextList),
      reqId_(0)
{
}

// ----------------------------------------------------------------------
// Request public member implementation
// ----------------------------------------------------------------------

CORBA_Object_ptr
CORBA_Request::target() const
{
    return target_;
}

const char*
CORBA_Request::operation() const
{
    return operation_;
}

CORBA_NVList_ptr
CORBA_Request::arguments()
{
    return arguments_;
}

CORBA_NamedValue_ptr
CORBA_Request::result()
{
    return result_;
}

CORBA_Environment_ptr
CORBA_Request::env()
{
    return environment_;
}

CORBA_ExceptionList_ptr
CORBA_Request::exceptions()
{
    return exceptions_;
}

CORBA_ContextList_ptr
CORBA_Request::contexts()
{
    return contexts_;
}

void
CORBA_Request::ctx(CORBA_Context_ptr ctx)
{
    ctx_ = CORBA_Context::_duplicate(ctx);
}

CORBA_Context_ptr
CORBA_Request::ctx() const
{
    return ctx_;
}

CORBA_Any&
CORBA_Request::add_in_arg()
{
    CORBA_NamedValue_ptr p = arguments_ -> add(CORBA_ARG_IN);
    return *(p -> value());
}

CORBA_Any&
CORBA_Request::add_in_arg(const char* name)
{
    assert_nca(name, OBNCANullString);
    CORBA_NamedValue_ptr p = arguments_ -> add_item(name, CORBA_ARG_IN);
    return *(p -> value());
}

CORBA_Any&
CORBA_Request::add_inout_arg()
{
    CORBA_NamedValue_ptr p = arguments_ -> add(CORBA_ARG_INOUT);
    return *(p -> value());
}

CORBA_Any&
CORBA_Request::add_inout_arg(const char* name)
{
    assert_nca(name, OBNCANullString);
    CORBA_NamedValue_ptr p = arguments_ -> add_item(name, CORBA_ARG_INOUT);
    return *(p -> value());
}

CORBA_Any&
CORBA_Request::add_out_arg()
{
    CORBA_NamedValue_ptr p = arguments_ -> add(CORBA_ARG_OUT);
    return *(p -> value());
}

CORBA_Any&
CORBA_Request::add_out_arg(const char* name)
{
    assert_nca(name, OBNCANullString);
    CORBA_NamedValue_ptr p = arguments_ -> add_item(name, CORBA_ARG_OUT);
    return *(p -> value());
}

void
CORBA_Request::set_return_type(CORBA_TypeCode_ptr tc)
{
    result_ -> value() -> replace(tc, 0);
}

CORBA_Any&
CORBA_Request::return_value()
{
    return *(result_ -> value());
}

CORBA_Status
CORBA_Request::invoke()
{
    try
    {
	OBBuffer buf;
	marshal(buf);
	
	OBClient_var clt = target_ -> _OB_client();
	if(CORBA_is_nil(clt))
	    throw CORBA_NO_IMPLEMENT();

	bool swap, exc, fwd;
	CORBA_ULong offset = clt -> request(target_, operation_,
					    buf, swap, exc, fwd,
					    target_ -> _timeout(),
					    target_ -> _retry());
	
	assert(offset > 0);

	if(fwd)
	{
	    const CORBA_Octet* coct = buf.data + offset;
	    target_ -> _OB_forward(coct, swap);
	    invoke();
	    return;
	}

	unmarshal(buf, offset, swap, exc);
    }
    catch(const CORBA_SystemException& ex)
    {
	environment_ -> exception(ex._OB_clone());
        if(CORBA_ORB::_OB_raiseDIIExceptions())
            ex._raise();
    }
}

CORBA_Status
CORBA_Request::send_oneway()
{
    try
    {
	OBBuffer buf;
	marshal(buf);

	OBClient_var clt = target_ -> _OB_client();
	if(CORBA_is_nil(clt))
	    throw CORBA_NO_IMPLEMENT();

	clt -> oneway(target_, operation_, buf, target_ -> _retry());
    }
    catch(const CORBA_SystemException& ex)
    {
	environment_ -> exception(ex._OB_clone());
        if(CORBA_ORB::_OB_raiseDIIExceptions())
            ex._raise();
    }
}

CORBA_Status
CORBA_Request::send_deferred()
{
    try
    {
	OBBuffer buf;
	marshal(buf);
	
	OBClient_var clt = target_ -> _OB_client();
	if(CORBA_is_nil(clt))
	    throw CORBA_NO_IMPLEMENT();

	clt -> deferred(target_, operation_, buf, reqId_, target_ -> _retry());
    }
    catch(const CORBA_SystemException& ex)
    {
	environment_ -> exception(ex._OB_clone());
        if(CORBA_ORB::_OB_raiseDIIExceptions())
            ex._raise();
    }
}

CORBA_Status
CORBA_Request::get_response()
{
    try
    {
	OBBuffer buf;

	OBClient_var clt = target_ -> _OB_client();
	if(CORBA_is_nil(clt))
	    throw CORBA_NO_IMPLEMENT();

	bool swap, exc, fwd;
	CORBA_ULong offset = clt -> response(reqId_, buf, swap, exc, fwd,
					     target_ -> _timeout());

	assert(offset > 0);

	if(fwd)
	{
	    const CORBA_Octet* coct = buf.data + offset;
	    target_ -> _OB_forward(coct, swap);
	    send_deferred();
	    get_response();
	    return;
	}

	unmarshal(buf, offset, swap, exc);
    }
    catch(const CORBA_SystemException& ex)
    {
	environment_ -> exception(ex._OB_clone());
        if(CORBA_ORB::_OB_raiseDIIExceptions())
            ex._raise();
    }
}

CORBA_Boolean
CORBA_Request::poll_response()
{
    try
    {
	OBBuffer buf;
	
	OBClient_var clt = target_ -> _OB_client();
	if(CORBA_is_nil(clt))
	    throw CORBA_NO_IMPLEMENT();

	bool swap, exc, fwd;
	CORBA_ULong offset = clt -> poll(reqId_, buf, swap, exc, fwd);
	
	if(offset == 0)
	    return false;

	if(fwd)
	{
	    const CORBA_Octet* coct = buf.data + offset;
	    target_ -> _OB_forward(coct, swap);
	    send_deferred();
	    return false;
	}

	unmarshal(buf, offset, swap, exc);
	return true;
    }
    catch(const CORBA_SystemException& ex)
    {
	environment_ -> exception(ex._OB_clone());
        if(CORBA_ORB::_OB_raiseDIIExceptions())
            ex._raise();
	return true;
    }
}

// ----------------------------------------------------------------------
// Object public member implementations that are related to the DII
// ----------------------------------------------------------------------

CORBA_Status
CORBA_Object::_create_request(CORBA_Context_ptr ctx,
			      const char* operation,
			      CORBA_NVList_ptr arg_list,
			      CORBA_NamedValue_ptr result,
			      CORBA_Request_ptr& request,
			      CORBA_Flags /*req_flags*/)
{
    assert_nca(operation, OBNCANullString);
    request = new CORBA_Request(this, operation, arg_list, result);
    request -> ctx(ctx);
}

CORBA_Status
CORBA_Object::_create_request(CORBA_Context_ptr ctx,
			      const char* operation,
			      CORBA_NVList_ptr arg_list,
			      CORBA_NamedValue_ptr result,
			      CORBA_ExceptionList_ptr exceptions,
			      CORBA_ContextList_ptr contexts,
			      CORBA_Request_ptr& request,
			      CORBA_Flags /*req_flags*/)
{
    assert_nca(operation, OBNCANullString);
    request = new CORBA_Request(this, operation, arg_list, result,
				exceptions, contexts);
    request -> ctx(ctx);
}

CORBA_Request_ptr
CORBA_Object::_request(const char* operation)
{
    assert_nca(operation, OBNCANullString);
    return new CORBA_Request(this, operation);
}

// ----------------------------------------------------------------------
// ORB public member implementations that are related to the DII
// ----------------------------------------------------------------------

CORBA_Status
CORBA_ORB::create_list(CORBA_Long n, CORBA_NVList_ptr& list)
{
    if(n < 0)
	n = 0;

    list = new CORBA_NVList((CORBA_ULong)n);
}

CORBA_Status
CORBA_ORB::create_operation_list(CORBA_OperationDef_ptr opDef,
				 CORBA_NVList_ptr& list)
{
    //
    // Get operation description
    //
    CORBA_Contained::Description_var d = opDef -> describe();
    CORBA_OperationDescription* desc;
    CORBA_Boolean b = d -> value >>= desc;
    assert(b);

    //
    // Create list
    //
    list = new CORBA_NVList;
    for(CORBA_ULong i = 0 ; i < desc -> parameters.length() ; i++)
    {
	CORBA_ParameterDescription& par = desc -> parameters[i];

	char* name = CORBA_string_dup(par.name);

	CORBA_Any* any = new CORBA_Any;
	any -> replace(par.type, 0);

	CORBA_Flags flags;
	switch(par.mode)
	{
	case CORBA_PARAM_IN:
	    flags = CORBA_ARG_IN;
	    break;
	    
	case CORBA_PARAM_OUT:
	    flags = CORBA_ARG_OUT;
	    break;
	    
	case CORBA_PARAM_INOUT:
	    flags = CORBA_ARG_INOUT;
	    break;
	}

	list -> add_value_consume(name, any, flags);
    }
}

CORBA_Status
CORBA_ORB::create_named_value(CORBA_NamedValue_ptr& nv)
{
    nv = new CORBA_NamedValue;
}

CORBA_Status
CORBA_ORB::create_exception_list(CORBA_ExceptionList_ptr& list)
{
    list = new CORBA_ExceptionList;
}

CORBA_Status
CORBA_ORB::create_context_list(CORBA_ContextList_ptr& list)
{
    list = new CORBA_ContextList;
}

CORBA_Status
CORBA_ORB::send_multiple_requests_oneway(const RequestSeq& requests)
{
    for(CORBA_ULong i = 0 ; i < requests.length() ; i++)
	requests[i] -> send_oneway();
}

CORBA_Status
CORBA_ORB::send_multiple_requests_deferred(const RequestSeq& requests)
{
    deferredRequests_ = requests;
    deferredRequestCompletions_.length(deferredRequests_.length());

    for(CORBA_ULong i = 0 ; i < deferredRequests_.length() ; i++)
    {
	deferredRequests_[i] -> send_deferred();
	deferredRequestCompletions_[i] = CORBA_FALSE;
    }
}

CORBA_Boolean
CORBA_ORB::poll_next_response()
{
    CORBA_ULong i;

    for(i = 0 ; i < deferredRequests_.length() ; i++)
	if(!deferredRequestCompletions_[i])
	    if(deferredRequests_[i] -> poll_response())
		deferredRequestCompletions_[i] = CORBA_TRUE;

    for(i = 0 ; i < deferredRequests_.length() ; i++)
	if(deferredRequestCompletions_[i] &&
	   !CORBA_is_nil(deferredRequests_[i]))
	    return CORBA_TRUE;

    return CORBA_FALSE;
}

CORBA_Status
CORBA_ORB::get_next_response(CORBA_Request_ptr& request)
{
    for(CORBA_ULong i = 0 ; i < deferredRequests_.length() ; i++)
	if(deferredRequestCompletions_[i] &&
	   !CORBA_is_nil(deferredRequests_[i]))
	{
	    request = CORBA_Request::_duplicate(deferredRequests_[i]);
	    deferredRequests_[i] = CORBA_Request::_nil();
	    return;
	}

    request = CORBA_Request::_nil();
}

// ----------------------------------------------------------------------
// UnkownUserException defined here, since it used for the DII only
// ----------------------------------------------------------------------

CORBA_UnknownUserException::
CORBA_UnknownUserException(const CORBA_UnknownUserException& val)
    : CORBA_UserException(val),
      any_(val.any_)
{
}

CORBA_UnknownUserException::CORBA_UnknownUserException(const CORBA_Any& any)
    : any_(any)
{
}

CORBA_UnknownUserException&
CORBA_UnknownUserException::operator=(const CORBA_UnknownUserException& val)
{
    if(this != &val)
    {
        any_ = val.any_;
    }
    return *this;
}

CORBA_UnknownUserException*
CORBA_UnknownUserException::_narrow(CORBA_Exception* p)
{
#ifdef HAVE_NO_RTTI
    if(p)
	return (CORBA_UnknownUserException*)
	    (p -> _OB_narrowHelp("IDL:omg.org/CORBA/"
				 "UnknownUserException:1.0"));
    else
	return 0;
#else
    return dynamic_cast<CORBA_UnknownUserException*>(p);
#endif
}

#ifdef HAVE_NO_RTTI
void*
CORBA_UnknownUserException::_OB_narrowHelp(const char* name) const
{
    if(strcmp("IDL:omg.org/CORBA/UnknownUserException:1.0", name) == 0)
        return (void*)this;
    else
        return CORBA_UserException::_OB_narrowHelp(name);
}
#endif

const char*
CORBA_UnknownUserException::_type_id() const
{
    return "IDL:omg.org/CORBA/UnknownUserException:1.0";
}

CORBA_Exception*
CORBA_UnknownUserException::_OB_clone() const
{
    return new CORBA_UnknownUserException(*this);
}
