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

#include <OB/CORBA.h>
#include <OB/Util.h>
#include <OB/Hashtable.h>
#include <OB/Properties.h>

#include <OB/CosTypedEventChannelAdmin_skel.h>
#include <OB/OBTypedEventChannelFactory_skel.h>

#include <TEC_TypedConsumerAdmin_impl.h>
#include <TEC_TypedSupplierAdmin_impl.h>
#include <TEC_TypedEventChannel_impl.h>
#include <TypedEventChannelFactory_impl.h>
#include <Event_impl.h>
#include <SetDefaultProperties.h>

// ----------------------------------------------------------------------
// TEC_TypedEventChannel_impl private member implementation
// ----------------------------------------------------------------------

void
TEC_TypedEventChannel_impl::typeIds(CORBA_InterfaceDef_ptr iface)
{
    typeIds_.append(iface -> id());

    CORBA_InterfaceDefSeq_var base = iface -> base_interfaces();
    for(CORBA_ULong i = 0; i < base -> length(); ++i)
    {
        //
        // Get the ID.
        //
        CORBA_RepositoryId_var id = base[i] -> id();

        //
        // Find out whether this ID is already in the set of IDs.
        //
        CORBA_ULong j;
        for(j = 0; j < typeIds_.length(); ++j)
            if(strcmp(typeIds_[j], id) == 0)
                break;

        //
        // Nope, then add all the typeIds for this interface.
        //
        if(j == typeIds_.length())
            typeIds(base[i]);
    }
}

// ----------------------------------------------------------------------
// TEC_TypedEventChannel_impl constructor and destructor
// ----------------------------------------------------------------------

TEC_TypedEventChannel_impl::TEC_TypedEventChannel_impl(
    CORBA_ORB_ptr orb, TypedEventChannelFactory_impl* eventChannelFactory,
    const char* myId)
        : orb_(CORBA_ORB::_duplicate(orb)),
          consumerAdmin_(new TEC_TypedConsumerAdmin_impl(orb_, this)),
          supplierAdmin_(new TEC_TypedSupplierAdmin_impl(orb_, this)),
          myId_(CORBA_string_dup(myId)),
	  destroyed_(false),
          eventChannelFactory_(eventChannelFactory)
{
    SetDefaultProperties::setDefaults();
}

TEC_TypedEventChannel_impl::~TEC_TypedEventChannel_impl()
{
    CORBA_release(consumerAdmin_);
    CORBA_release(supplierAdmin_);
}

// ----------------------------------------------------------------------
// TEC_TypedEventChannel_impl public member implementation
// ----------------------------------------------------------------------

CosTypedEventChannelAdmin_TypedConsumerAdmin_ptr
TEC_TypedEventChannel_impl::for_consumers()
{
    return CosTypedEventChannelAdmin_TypedConsumerAdmin::
        _duplicate(consumerAdmin_);
}

CosTypedEventChannelAdmin_TypedSupplierAdmin_ptr
TEC_TypedEventChannel_impl::for_suppliers()
{
    return CosTypedEventChannelAdmin_TypedSupplierAdmin::
        _duplicate(supplierAdmin_);
}

void
TEC_TypedEventChannel_impl::destroy()
{
    JTCSynchronized sync(*this);
    if(destroyed_)
	throw CORBA_OBJECT_NOT_EXIST();
    destroyed_ = true;

    eventChannelFactory_ -> removeChannel(myId_);
    
    orb_ -> disconnect(this);

    consumerAdmin_ -> disconnect();
    supplierAdmin_ -> disconnect();
}

//
// Receive event
//
void
TEC_TypedEventChannel_impl::receive(Event_impl* event)
{
    //
    // Forward the event to the consumers.
    //
    consumerAdmin_ -> receive(event);

    //
    // We're no longer interested in the event.
    //
    event -> discard();
}

bool
TEC_TypedEventChannel_impl::isCompatible(const char* id)
{
    for(CORBA_ULong i = 0; i < typeIds_.length(); ++i)
        if(strcmp(typeIds_[i], id) == 0)
            return true;
    return false;
}

bool
TEC_TypedEventChannel_impl::assignTypeId(const char* id)
{
    //
    // Do we already have a type-id assigned?
    //
    if(typeIds_.length() != 0)
        return isCompatible(id);

    CORBA_Repository_var ir;
    try
    {
        CORBA_Object_var irObj =
            orb_ -> resolve_initial_references("InterfaceRepository");
        ir = CORBA_Repository::_narrow(irObj);
    }
    catch(const CORBA_ORB::InvalidName&)
    {
	//
	// This is really an internal error since we ensure that there
	// is a InterfaceRepository at process startup.
	//
    }
    //
    // We want COMM_FAILURE and TRANSIENT exceptions to cause
    // INTF_REPOS exceptions to be raised. Another valid choice would
    // be InterfaceNotSupported. However, I have chosen to raise
    // INTF_REPOS in the case that the IR isn't available so that the
    // user can differentiate this case from the ID not present in the
    // IR.
    //
    catch(const CORBA_COMM_FAILURE&)
    {
    }
    catch(const CORBA_TRANSIENT&)
    {
    }
    if(CORBA_is_nil(ir))
        throw CORBA_INTF_REPOS(OBMinorNoIntfRepos, CORBA_COMPLETED_NO);
    
    CORBA_Contained_var contained = ir -> lookup_id(id);
    
    //
    // Not in the interface repository? Not supported.
    //
    if(CORBA_is_nil(contained))
        return false;
    
    CORBA_InterfaceDef_var iDef = CORBA_InterfaceDef::_narrow(contained);
    if(CORBA_is_nil(iDef))
	return false;

    //
    // Retrieve the type-ids.
    //
    typeIds(iDef);

    iface_ = iDef -> describe_interface();

    return true;
}

CORBA_InterfaceDef::FullInterfaceDescription*
TEC_TypedEventChannel_impl::getIface()
{
    return new CORBA_InterfaceDef::FullInterfaceDescription(iface_);
}
