// **********************************************************************
//
// 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/Declarations.h>
#include <OB/Any.h>
#include <OB/IOP.h>
#include <OB/Object.h>
#include <OB/Policy.h>
#include <OB/IntRepMember.h>
#include <OB/ORB.h>
#include <OB/ImplRep.h>
#include <OB/BOA.h>
#include <OB/GIOP.h>
#include <OB/IIOP.h>
#include <OB/OCI.h>
#include <OB/OCI_IIOP.h>
#include <OB/Util.h>
#include <OB/Net.h>

#include <OB/Properties.h>

#include <OCI_IIOP_impl.h> // For new OCI_IIOP_..._impl

// ----------------------------------------------------------------------
// ORB Initialization
// ----------------------------------------------------------------------

CORBA_ORB_ptr
CORBA_ORB_init(int& ac, char** av, const char* orb_identifier,
	       const char* version)
{
    assert_nca(orb_identifier, OBNCANullString);

    OBMessageViewer* viewer = OBMessageViewer::instance();

    CORBA_ORB_var orb = CORBA_ORB::_OB_instance();
    if(!CORBA_is_nil(orb))
    {
	viewer -> warning("ORB_init: ORB already initialized");
	return orb._retn();
    }

    //
    // Check the version
    //
    if(strcmp(version, OBVersion) != 0)
    {
        CORBA_String_var err = CORBA_string_dup("ORB_init: version mismatch:");
        err += "\nlibrary version is ";
        err += OBVersion;
        err += "\nheader file version is ";
        err += version;
        viewer -> error(err);
        throw CORBA_INITIALIZE();
    }

    //
    // Initialize the properties
    //
    OBProperties* properties = OBProperties::init(ac, av);
    
    //
    // Process each argument. Turn each argument into an appropriate
    // property.
    //
    int i = 0;
    while(i < ac)
    {
        const char* option = av[i];
        if(strcmp(option, "-ORBid") == 0)
        {
            if(i + 1 >= ac)
            {
                viewer -> error("ORB_init: argument expected for -ORBid");
                throw CORBA_INITIALIZE();
            }

            properties -> setProperty("ooc.orb.id", av[i + 1]);
            
            for(int j = i ; j + 2 < ac ; j++)
                av[j] = av[j + 2];
            
            ac -= 2;
        }
        else if(strcmp(option, "-ORBservice") == 0)
        {
            if(i + 2 >= ac)
            {
                viewer -> error("ORB_init: arguments expected for "
                                "-ORBservice");
                throw CORBA_INITIALIZE();
            }

            CORBA_String_var prop = CORBA_string_dup("ooc.service.");
            prop += av[i + 1];
            properties -> setProperty(prop, av[i + 2]);
                
            for(int j = i ; j + 3 < ac ; j++)
                av[j] = av[j + 3];
            
            ac -= 3;
        }
        else if(strcmp(option, "-ORBInitRef") == 0)
        {
            if(i + 1 >= ac)
            {
                viewer -> error("ORB_init: argument expected for "
                                "-ORBInitRef");
                throw CORBA_INITIALIZE();
            }

            const char* cp = strchr(av[i + 1], '=');
            if(cp != 0 && av[i + 1][0] != '=' && strlen(cp + 1) > 0)
            {
                CORBA_String_var id = CORBA_string_alloc(cp - av[i + 1]);
                strncpy(id.inout(), av[i + 1], cp - av[i + 1]);
                id[cp - av[i + 1]] = '\0';
                CORBA_String_var prop = CORBA_string_dup("ooc.service.");
                prop += id;
                properties -> setProperty(prop, cp + 1);
            }
            else
            {
                viewer -> error("ORB_init: invalid argument for "
                                "-ORBInitRef");
                throw CORBA_INITIALIZE();
            }

            for(int j = i ; j + 2 < ac ; j++)
                av[j] = av[j + 2];
            
            ac -= 2;
        }
        else if(strcmp(option, "-ORBDefaultInitRef") == 0)
        {
            if(i + 1 >= ac)
            {
                viewer -> error("ORB_init: argument expected for "
                                "-ORBDefaultInitRef");
                throw CORBA_INITIALIZE();
            }

            if(strlen(av[i + 1]) == 0)
            {
                viewer -> error("ORB_init: invalid argument for "
                                "-ORBDefaultInitRef");
                throw CORBA_INITIALIZE();
            }

            properties -> setProperty("ooc.orb.default_init_ref", av[i + 1]);

            for(int j = i ; j + 2 < ac ; j++)
                av[j] = av[j + 2];
            
            ac -= 2;
        }
        else if(strcmp(option, "-ORBrepository") == 0)
        {
            if(i + 1 >= ac)
            {
                viewer -> error("ORB_init: argument expected for "
                                "-ORBrepository");
                throw CORBA_INITIALIZE();
            }

            properties -> setProperty("ooc.service.InterfaceRepository",
				      av[i + 1]);

            for(int j = i ; j + 2 < ac ; j++)
                av[j] = av[j + 2];
            
            ac -= 2;
        }
        else if(strcmp(option, "-ORBnaming") == 0)
        {
            if(i + 1 >= ac)
            {
                viewer -> error("ORB_init: argument expected for "
                                "-ORBnaming");
                throw CORBA_INITIALIZE();
            }

            properties -> setProperty("ooc.service.NameService", av[i + 1]);
                
            for(int j = i ; j + 2 < ac ; j++)
                av[j] = av[j + 2];
            
            ac -= 2;
        }
        else if(strcmp(option, "-ORBtrace_level") == 0)
        {
            if(i + 1 >= ac)
            {
                viewer -> error("ORB_init: argument expected for "
                                "-ORBtrace_level");
                throw CORBA_INITIALIZE();
            }

            properties -> setProperty("ooc.orb.trace_level", av[i + 1]);
            
            for(int j = i ; j + 2 < ac ; j++)
                av[j] = av[j + 2];
            
            ac -= 2;
        }        
        else if(strcmp(option, "-ORBblocking") == 0)
        {
            properties -> setProperty("ooc.orb.conc_model", "blocking");
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
        else if(strcmp(option, "-ORBreactive") == 0)
        {
            properties -> setProperty("ooc.orb.conc_model", "reactive");
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
#ifdef HAVE_JTC
        else if(strcmp(option, "-ORBthreaded") == 0)
        {
            properties -> setProperty("ooc.orb.conc_model", "threaded");
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
#endif
        else if(strcmp(option, "-ORBversion") == 0)
        {
            viewer -> message(OBVersion);
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
        else if(strcmp(option, "-ORBlicense") == 0)
        {
            viewer -> message(OBLicense);
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
        else if(strncmp(option, "-ORB", 4) == 0)
        {
            CORBA_String_var err =
                CORBA_string_dup("ORB_init: unknown option: ");
            err += option;
            viewer -> warning(err);
	    
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
	    
            ac -= 1;
        }
        else
            i++;
    }

    //
    // Process each property
    //
    CORBA_String_var id = orb_identifier;
    CORBA_ORB::ConcModel concModel = CORBA_ORB::conc_model();
    bool addIIOPConnector = true;
    CORBA_String_var defaultInitRef = CORBA_string_dup("");
    OBProperties::KeySeq keys = properties -> getKeys("ooc.orb.");
    bool raiseDIIExceptions = false;
    CORBA_ULong j;
    for(j = 0; j < keys.length(); ++j)
    {
        const char* key = keys[j];
        const char* value = properties -> getProperty(key);
        assert(value != 0);

        if(strcmp(key, "ooc.orb.conc_model") == 0)
        {
            if(strcmp(value, "blocking") == 0)
                concModel = CORBA_ORB::ConcModelBlocking;
            else if(strcmp(value, "reactive") == 0)
                concModel = CORBA_ORB::ConcModelReactive;
#ifdef HAVE_JTC
            else if(strcmp(value, "threaded") == 0)
                concModel = CORBA_ORB::ConcModelThreaded;
#endif
            else
            {
                CORBA_String_var err =
                CORBA_string_dup("ORB_init:: unknown value for "
                                 "ooc.orb.conc_model: ");
                err += value;
                viewer -> warning(err);
            }
        }
        else if(strcmp(key, "ooc.orb.trace_level") == 0)
        {
            OBMessageViewer::setTraceLevel(atoi(value));
        }
        else if(strcmp(key, "ooc.orb.id") == 0)
        {
            id = CORBA_string_dup(value);
        }
        else if(strcmp(key, "ooc.orb.add_iiop_connector") == 0)
        {
            if(strcmp(value, "true") == 0)
                addIIOPConnector = true;
            else
                addIIOPConnector = false;
        }
        else if(strcmp(key, "ooc.orb.default_init_ref") == 0)
        {
            if(strlen(value) == 0)
                viewer -> warning("ORB_init: invalid value for "
                                  "ooc.orb.default_init_ref");
            else
                defaultInitRef = CORBA_string_dup(value);
        }
        else if(strcmp(key, "ooc.orb.raise_dii_exceptions") == 0)
        {
            if(strcmp(value, "true") == 0)
                raiseDIIExceptions = true;
            else
                raiseDIIExceptions = false;
        }
        else
        {
            CORBA_String_var err =
            CORBA_string_dup("ORB_init: unknown property: ");
            err += key;
            viewer -> warning(err);
        }
    }

    //
    // Verify the ORB id
    //
    if(strlen(id) > 0 && strcmp(id, "OB_ORB") != 0)
    {
        CORBA_String_var err =
	    CORBA_string_dup("ORB_init: unknown ORB `");
        err += id;
        err += "'";
        viewer -> error(err);
        throw CORBA_INITIALIZE();
    }

    //
    // Create the ORB
    //
    CORBA_ORB_var p = new CORBA_ORB();

    //
    // Set the concurrency model
    //
    p -> conc_model(concModel);

    //
    // Add an IIOP connector if necessary
    //
    if(addIIOPConnector)
    {
        OCI_ConFactoryRegistry_var registry = p -> get_con_factory_registry();
        assert(!CORBA_is_nil(registry));
        OCI_ConFactory_var factoryForIIOP = new OCI_IIOP_ConFactory_impl;
        registry -> add_factory(factoryForIIOP);
    }
        
    //
    // Add those services configured in the "ooc.service" property.
    //
    OBProperties::KeySeq serviceKeys = properties -> getKeys("ooc.service.");
    for(j = 0 ; j < serviceKeys.length() ; j++)
    {
        const char* key = serviceKeys[j];
        const char* value = properties -> getProperty(key);
        assert(value != 0);
        assert(strncmp(key, "ooc.service.", strlen("ooc.service.")) == 0);
        key += strlen("ooc.service.");
        p -> _OB_addInitialService(key, value);
    }

    //
    // Set the default initial reference "template"
    //
    p -> defaultInitRef_ = defaultInitRef;

    //
    // Set the semantics for DII system exceptions
    //
    p -> raiseDIIExceptions_ = raiseDIIExceptions;

    return p._retn();
}

// ----------------------------------------------------------------------
// BOA Initialization
// ----------------------------------------------------------------------

CORBA_BOA_ptr
CORBA_ORB::BOA_init(int& ac, char** av, const char* boa_identifier)
{
    assert_nca(boa_identifier, OBNCANullString);

    OBMessageViewer* viewer = OBMessageViewer::instance();

    CORBA_BOA_var boa = CORBA_BOA::_OB_instance();
    if(!CORBA_is_nil(boa))
    {
	viewer -> warning("ORB::BOA_init: BOA already initialized");
	return boa._retn();
    }
    
    //
    // Get the set of properties
    //
    OBProperties* properties = OBProperties::instance();

    //
    // Process each argument. Turn each argument into an appropriate
    // property.
    //
    int i = 0;
    while(i < ac)
    {
        const char* option = av[i];
        assert(option != 0);
        if(strcmp(option, "-OAid") == 0)
        {
            if(i + 1 >= ac)
            {
                viewer -> error("ORB::BOA_init: argument expected for -OAid");
                throw CORBA_INITIALIZE();
            }

            properties -> setProperty("ooc.boa.id", av[i + 1]);
            
            for(int j = i ; j + 2 < ac ; j++)
                av[j] = av[j + 2];
            
            ac -= 2;
        }
        else if(strcmp(option, "-OAdisable_iiop_acceptor") == 0)
        {
            properties -> setProperty("ooc.boa.disable_iiop_acceptor", "true");
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
        else if(strcmp(option, "-OAnumeric") == 0)
	{
            properties -> setProperty("ooc.boa.numeric", "true");

            for(int j = i ; j + 1 < ac ; j++)
		av[j] = av[j + 1];

	    ac -= 1;
	}
	else if(strcmp(option, "-OAhost") == 0)
	{
	    if(i + 1 >= ac)
	    {
		viewer -> error("ORB::BOA_init: argument expected for "
				"-OAhost");
		throw CORBA_INITIALIZE();
	    }

            properties -> setProperty("ooc.boa.host", av[i + 1]);

	    for(int j = i ; j + 2 < ac ; j++)
		av[j] = av[j + 2];

	    ac -= 2;
	}
	else if(strcmp(option, "-OAport") == 0)
	{
	    if(i + 1 >= ac)
	    {
                viewer -> error("ORB::BOA_init: argument expected for "
				"-OAport");
		throw CORBA_INITIALIZE();
            }

            properties -> setProperty("ooc.boa.port", av[i + 1]);

	    for(int j = i ; j + 2 < ac ; j++)
		av[j] = av[j + 2];

	    ac -= 2;
	}
        else if(strcmp(option, "-OAblocking") == 0)
        {
            properties -> setProperty("ooc.boa.conc_model", "blocking");

            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
        else if(strcmp(option, "-OAreactive") == 0)
        {
            properties -> setProperty("ooc.boa.conc_model", "reactive");
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
#ifdef HAVE_JTC
        else if(strcmp(option, "-OAthreaded") == 0)
        {
            properties -> setProperty("ooc.boa.conc_model", "threaded");
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
        else if(strcmp(option, "-OAthread_per_client") == 0)
        {
            properties -> setProperty("ooc.boa.conc_model",
                                      "thread_per_client");
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
        else if(strcmp(option, "-OAthread_per_request") == 0)
        {
            properties -> setProperty("ooc.boa.conc_model",
                                      "thread_per_request");
            
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
            
            ac -= 1;
        }
        else if(strcmp(option, "-OAthread_pool") == 0)
        {
            if(i + 1 >= ac)
            {
                viewer -> error("ORB::BOA_init: argument expected for "
                                "-OAthread_pool");
                throw CORBA_INITIALIZE();
            }

            properties -> setProperty("ooc.boa.conc_model", "thread_pool");
            properties -> setProperty("ooc.boa.thread_pool", av[i + 1]);
            
            for(int j = i ; j + 2 < ac ; j++)
                av[j] = av[j + 2];
            
            ac -= 2;
        }
#endif
        else if(strncmp(option, "-OA", 3) == 0)
        {
            CORBA_String_var err =
            CORBA_string_dup("ORB::BOA_init: unknown option: ");
            err += option;
            viewer -> warning(err);
	    
            for(int j = i ; j + 1 < ac ; j++)
                av[j] = av[j + 1];
	    
            ac -= 1;
        }
        else
            i++;
    }

    //
    // Process each property
    //
    CORBA_String_var id = boa_identifier;
    CORBA_BOA::ConcModel concModel = CORBA_BOA::conc_model();
    CORBA_ULong concModelThreadPool = CORBA_BOA::conc_model_thread_pool();
    bool iiopAcceptorDisabled = false;
    CORBA_String_var host = CORBA_string_dup("");
    CORBA_UShort port = 0;
    bool numeric = false;
    bool addIIOPAcceptor = true;
    OBProperties::KeySeq keys = properties -> getKeys("ooc.boa.");
    CORBA_ULong j;
    for(j = 0; j < keys.length(); ++j)
    {
        const char* key = keys[j];
        const char* value = properties -> getProperty(key);
        assert(value != 0);

        if(strcmp(key, "ooc.boa.id") == 0)
        {
            id = CORBA_string_dup(value);
        }
        else if(strcmp(key, "ooc.boa.disable_iiop_acceptor") == 0)
        {
            if(strcmp(value, "true") == 0)
                iiopAcceptorDisabled = true;
            else
                iiopAcceptorDisabled = false;
        }
        else if(strcmp(key, "ooc.boa.add_iiop_acceptor") == 0)
        {
            if(strcmp(value, "true") == 0)
                addIIOPAcceptor = true;
            else
                addIIOPAcceptor = false;
        }
        else if(strcmp(key, "ooc.boa.port") == 0)
        {
            int po = atoi(value);
            if(po < 0 || po > 65535)
            {
                viewer -> error("ORB::BOA_init: port must be between "
                                "0 and 65535");
                throw CORBA_INITIALIZE();
            }
            port = (CORBA_UShort)po;
        }
        else if(strcmp(key, "ooc.boa.host") == 0)
        {
            host = CORBA_string_dup(value);
        }
        else if(strcmp(key, "ooc.boa.numeric") == 0)
        {
            if(strcmp(value, "true") == 0)
                numeric = true;
            else
                numeric = false;
        }
        else if(strcmp(key, "ooc.boa.conc_model") == 0)
        {
            if(strcmp(value, "blocking") == 0)
                concModel = CORBA_BOA::ConcModelBlocking;
            else if(strcmp(value, "reactive") == 0)
                concModel = CORBA_BOA::ConcModelReactive;
#ifdef HAVE_JTC
            else if(strcmp(value, "threaded") == 0)
                concModel = CORBA_BOA::ConcModelThreaded;
            else if(strcmp(value, "thread_per_client") == 0)
                concModel = CORBA_BOA::ConcModelThreadPerClient;
            else if(strcmp(value, "thread_per_request") == 0)
                concModel = CORBA_BOA::ConcModelThreadPerRequest;
            else if(strcmp(value, "thread_pool") == 0)
                concModel = CORBA_BOA::ConcModelThreadPool;
#endif
            else
            {
                CORBA_String_var err =
                CORBA_string_dup("ORB::BOA_init: unknown value for "
                                 "ooc.boa.conc_model: ");
                err += value;
                viewer -> warning(err);
            }
        }
#ifdef HAVE_JTC
        else if(strcmp(key, "ooc.boa.thread_pool") == 0)
        {
            concModelThreadPool = atoi(value);
        }
#endif
        else
        {
            CORBA_String_var err =
            CORBA_string_dup("ORB::BOA_init: unknown property: ");
            err += key;
            viewer -> warning(err);
        }
    }
    
    //
    // Verify the BOA id
    //
    if(strlen(id) > 0 && strcmp(id, "OB_BOA") != 0)
    {
	CORBA_String_var err =
        CORBA_string_dup("ORB::BOA_init: unknown BOA `");
	err += id;
	err += "'";
	viewer -> error(err);
	throw CORBA_INITIALIZE();
    }

    //
    // Get the hostname
    //
    if(strlen(host) == 0)
    {
        //
        // We use MAXHOSTNAMELEN + 1 since we're not sure whether the
        // len parameter to gethostname() includes '\0' or not.
        //
	char hostname[MAXHOSTNAMELEN + 1];
	if(gethostname(hostname, MAXHOSTNAMELEN) == -1)
	{
	    throw CORBA_COMM_FAILURE(OBLastError(), OBMinorGethostname,
				     CORBA_COMPLETED_NO);
	}
	
	struct hostent* ent;
	int retry = OB_GETHOSTBYNAME_RETRY;
	do
	{
	    ent = gethostbyname(hostname);
	}
#ifdef WIN32
	while(!ent && WSAGetLastError() == WSATRY_AGAIN && retry-- > 0);
#else
	while(!ent && h_errno == TRY_AGAIN && retry-- > 0);
#endif
	
	if(!ent)
	{
#ifdef WIN32
	    if(WSAGetLastError() == WSANO_DATA)
#else
	    if(h_errno == NO_DATA)
#endif
	    {
		viewer -> warning("ORB::BOA_init: "
				  "can't resolve hostname\n"
				  "using `localhost' (127.0.0.1) "
				  "instead of hostname");

		if(numeric)
		    host = CORBA_string_dup("127.0.0.1");
		else
		    host = CORBA_string_dup("localhost");
	    }
	    else
	    {
		throw CORBA_COMM_FAILURE(OBLastHError(),
					 OBMinorGethostbyname,
					 CORBA_COMPLETED_NO);
	    }
	}
	else
	{
	    if(numeric || strrchr(ent -> h_name, '.') == 0)
	    {
/*
		if(!numeric)
		{
		    viewer -> warning("ORB::BOA_init: "
				      "hostname is a domainless name\n"
				      "using dotted decimal notation "
				      "instead of hostname");
		}
*/
		
		struct in_addr in;
		memcpy(&in.s_addr, ent -> h_addr, sizeof(in.s_addr));
		host = CORBA_string_dup(inet_ntoa(in));
	    }
	    else
	    {
		host = CORBA_string_dup(ent -> h_name);
	    }

	    if(strcmp(host, "127.0.0.1") == 0 ||
	       strcmp(host, "localhost") == 0)
	    {
		viewer -> warning("ORB::BOA_init: "
				  "hostname lookup returned "
				  "`localhost' (127.0.0.1)\n"
				  "Use the -OAhost option to select "
				  "some other hostname");
	    }
	}
    }

    //
    // Create the BOA
    //
    CORBA_BOA_var p = new CORBA_BOA();

    //
    // Set the concurrency model
    //
    p -> conc_model(concModel);
    p -> conc_model_thread_pool(concModelThreadPool);

    //
    // Add an IIOP acceptor if necessary
    //
    if(addIIOPAcceptor)
    {
        OCI_AccRegistry_var registry = p -> get_acc_registry();
        assert(!CORBA_is_nil(registry));
        assert(strlen(host) > 0);
        OCI_Acceptor_var acceptor = new OCI_IIOP_Acceptor_impl(host, port);
        registry -> add_acceptor(acceptor);

        //
        // If the acceptor is disabled then inform the BOA to disable it
        //
        if(iiopAcceptorDisabled)
            p -> disable_acceptor(acceptor -> tag());
    }
    
    return p._retn();
}
