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

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

#include <Types.h>

#include <OBNaming_impl.h>

#include <stdio.h>
#include <stdlib.h>

#include <NamingDatabase.h>

#include <Logger.h>

#include <NTNamingService.h>

#include <NamingMsg.h>

#include <Config.h> // Make sure that the compiler complains if the
                    // configuration doesn't match

// ----------------------------------------------------------------------
// NTNamingService constructor
// ----------------------------------------------------------------------

NTNamingService::NTNamingService()
    : OBNTService("NamingService", "ORBacus Naming Service", NAMING_MSG),
      stopped_(false)
{
}

// ----------------------------------------------------------------------
// NTNamingService public member implementation
// ----------------------------------------------------------------------

void
NTNamingService::start(int argc, char** argv)
{
    try
    {
	//
	// Create ORB
	//
	CORBA_ORB_var orb = CORBA_ORB_init(argc, argv);

	//
	// Resolve the service object if available as an initial
	// reference.
	//
	const char* service = "NameService";
	CORBA_Object_var serviceObject;
	try
	{
	    serviceObject = orb -> resolve_initial_references(service);
	}
	catch(CORBA_ORB::InvalidName&)
	{
	}
	
	//
	// Set the IIOP port based on the port defined in the IIOP profile
	// of the service object (if it exists). Then create the BOA.
	//
	CORBA_BOA::set_iiop_port_from_object(serviceObject);
	boa_ = orb -> BOA_init(argc, argv);
	
	orb -> conc_model(CORBA_ORB::ConcModelThreaded);
	boa_ -> conc_model(CORBA_BOA::ConcModelThreadPerRequest);
	
	//
	// Get options.
	//
	const int defaultTimeout = 5; // Every 5 minutes.
	int timeout = defaultTimeout;
	bool noUpdates = false;
	
	CORBA_String_var database;
	
	OBMessageViewer* viewer = OBMessageViewer::instance();

	//
	// Check properties first.
	//
	OBProperties* properties = OBProperties::instance();
	OBProperties::KeySeq keys = properties -> getKeys("ooc.naming.");
	{
	    for(CORBA_ULong i = 0; i < keys.length(); ++i)
	    {
		const char* key = keys[i];
		const char* value = properties -> getProperty(key);
		assert(value != 0);
	    
		if(strcmp(key, "ooc.naming.database") == 0)
		{
		    database = CORBA_string_dup(value);
		}
		else if(strcmp(key, "ooc.naming.timeout") == 0)
		{
		    timeout = atoi(value);
		}
		else if(strcmp(key, "ooc.naming.no_updates") == 0)
		{
		    noUpdates = (strcmp(value, "true") == 0) ||
			(strcmp(value, "TRUE") == 0) ||
			(strcmp(value, "1") == 0);
		}
		else
		{
		    CORBA_String_var msg = CORBA_string_dup("`");
		    msg += key;
		    msg += "': unknown property";
		    viewer -> warning(msg);
		}
	    }
	}
	
	if(timeout <= 0)
	{
	    viewer -> warning(
		"timeout value must be > 0. Using default value");
	    timeout = defaultTimeout;
	}
	
	NamingContextSet* ncs = new NamingContextSet(63);
	NamingDatabase* store = 0;
	LoggerHandle logger = 0;
	
	bool start = false;
	if(database != 0)
	{
	    //
	    // Create a new NamingDatabase.
	    //
	    store = new NamingDatabase(boa_, orb, database, ncs, noUpdates,
		                       start, true);
	    
	    //
	    // Start logger.
	    //
	    logger = new Logger(store, timeout*60);
	    logger -> start();
	}
	else
	{
	    viewer -> warning("Running in non-persistent mode.");
	}
        
	//
	// Create the root naming context, if we've just created the
        // database or if we're running in transient mode.
	//
	CosNaming_NamingContext_var root;
	if(start || database == 0)
	    root = new CosNaming_OBNamingContext_impl(orb, store, ncs,
						      "NameService",
						      noUpdates);
	
        //
        // Inform the status manager that the service is running.
        //
	statusUpdate(SERVICE_RUNNING);
        
	//
	// Run implementation.
	//
        if(!stopped_)
            boa_ -> impl_is_ready(CORBA_ImplementationDef::_nil());

        boa_ = CORBA_BOA::_nil();
	if(logger)
	{
	    logger -> halt();
	    while(logger -> isAlive())
	    {
		try
		{
		    logger -> join();
		}
		catch(const JTCInterruptedException&)
		{
		}
	    }
	}
	logger = 0;

	//
	// Clean up all contexts.
	//
	NamingContextSet::Enumerator contexts = ncs -> keys();

	CORBA_ULong context;
	for(context = 0; context < contexts.length(); context++)
	{
	    CosNaming_NamingContext_ptr nc;
	    ncs -> get(contexts[context], nc);
	    CosNaming_OBNamingContext_impl* impl =
		CosNaming_OBNamingContext_impl::_narrow_impl(nc);
	    impl -> _OB_releaseInternal();
	    CORBA_release(impl);
	}

	delete ncs;
	delete store;
    }
    catch(CORBA_SystemException& ex)
    {
	CORBA_String_var err = OBExceptionToString(ex);
	OBMessageViewer::instance() -> error(err);
    }
}

void
NTNamingService::stop()
{
    stopped_ = true;
    if(!CORBA_is_nil(boa_))
        boa_ -> deactivate_impl(CORBA_ImplementationDef::_nil());
}
