/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/**
 * bonobo-main.c: Bonobo Main
 *
 * Author:
 *    Miguel de Icaza  (miguel@gnu.org)
 *    Nat Friedman     (nat@nat.org)
 *    Peter Wainwright (prw@wainpr.demo.co.uk)
 *    Michael Meeks    (michael@ximian.com)
 *
 * Copyright 1999,2003 Ximian, Inc.
 */
#include <config.h>
#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-main.h>
#include <bonobo/bonobo-object.h>
#include <bonobo/bonobo-context.h>
#include <bonobo/bonobo-private.h>
#include <bonobo/bonobo-arg.h>

#ifdef G_OS_WIN32
#include "bonobo-activation/bonobo-activation-private.h"
#endif

#include <libintl.h>
#include <string.h>

#include <glib/gmain.h>

#ifdef HAVE_GTHREADS
GMutex                   *_bonobo_lock;
#endif
static CORBA_ORB                 __bonobo_orb = CORBA_OBJECT_NIL;
static PortableServer_POA        __bonobo_poa = CORBA_OBJECT_NIL;
static PortableServer_POAManager __bonobo_poa_manager = CORBA_OBJECT_NIL;

static guint              bonobo_main_loop_level = 0;
static GSList *           bonobo_main_loops = NULL;

#ifdef G_OS_WIN32

#undef BONOBO_LOCALEDIR
#define BONOBO_LOCALEDIR _bonobo_activation_win32_get_localedir ()

#endif

/**
 * bonobo_orb:
 *
 * Returns: The ORB used for this Bonobo application.  The ORB
 * is created in bonobo_init().
 */
CORBA_ORB
bonobo_orb (void)
{
	return __bonobo_orb;
}

/**
 * bonobo_poa:
 *
 * Returns: The POA used for this Bonobo application.  The POA
 * is created when bonobo_init() is called.
 */
PortableServer_POA
bonobo_poa (void)
{
	return __bonobo_poa;
}

/**
 * bonobo_poa_manager:
 *
 * Returns: The POA Manager used for this Bonobo application.  The POA
 * Manager is created when bonobo_init() is called, but it is not
 * activated until bonobo_main() is called.
 */
PortableServer_POAManager
bonobo_poa_manager (void)
{
	return __bonobo_poa_manager;
}

static gint bonobo_inited = 0;

/**
 * bonobo_is_initialized:
 * 
 *   This allows you to protect against double
 * initialization in your code.
 * 
 * Return value: whether the ORB is initialized
 **/
gboolean
bonobo_is_initialized (void)
{
	return (bonobo_inited > 0);
}

/**
 * bonobo_debug_shutdown:
 * 
 *   This shuts down the ORB and any other bonobo related
 * resources.
 * 
 * Return value: whether the shutdown was clean, a good
 * value to return from 'main'.
 **/
int
bonobo_debug_shutdown (void)
{
	int retval = 0;

	if (bonobo_inited == 1) {
		CORBA_Environment ev;

		bonobo_inited--;

		CORBA_exception_init (&ev);

		bonobo_property_bag_shutdown ();
		bonobo_running_context_shutdown ();
		bonobo_context_shutdown ();
		if (bonobo_object_shutdown ())
			retval = 1;
		bonobo_exception_shutdown ();

		if (__bonobo_poa != CORBA_OBJECT_NIL)
			CORBA_Object_release (
				(CORBA_Object) __bonobo_poa, &ev);
		__bonobo_poa = CORBA_OBJECT_NIL;

		if (__bonobo_poa_manager != CORBA_OBJECT_NIL)
			CORBA_Object_release (
				(CORBA_Object) __bonobo_poa_manager, &ev);
		__bonobo_poa_manager = CORBA_OBJECT_NIL;

		if (!bonobo_activation_debug_shutdown ())
			retval = 1;

		__bonobo_orb = CORBA_OBJECT_NIL;
	} else if (bonobo_inited > 1) {
		bonobo_inited--;		
	} else /* shutdown when we didn't need to error */
		retval = 1;

	return retval;
}

/**
 * bonobo_init_full:
 * @argc: a pointer to the number of arguments
 * @argv: the array of arguments
 * @opt_orb: the ORB in which we run
 * @opt_poa: optional, a POA
 * @opt_manager: optional, a POA Manager
 *
 * Initializes the bonobo document model.  It requires at least
 * the value for @orb.  If @poa is CORBA_OBJECT_NIL, then the
 * RootPOA will be used, in this case @manager should be CORBA_OBJECT_NIL.
 *
 * Returns %TRUE on success, or %FALSE on failure.
 */
gboolean
bonobo_init_full (int *argc, char **argv,
		  CORBA_ORB opt_orb, PortableServer_POA opt_poa,
		  PortableServer_POAManager opt_manager)
{
	CORBA_Environment ev;

	bonobo_activation_init (argc ? *argc : 0, argv);

	bonobo_inited++;
	if (bonobo_inited > 1)
		return TRUE;

	/* Init neccessary bits */
	g_type_init_with_debug_flags (0);

	bonobo_arg_init ();

	CORBA_exception_init (&ev);

	/*
	 * Create the POA.
	 */
	if (opt_orb == CORBA_OBJECT_NIL) {
		opt_orb = bonobo_activation_orb_get ();
		if (opt_orb == CORBA_OBJECT_NIL) {
			g_warning ("Can not resolve initial reference to ORB");
			CORBA_exception_free (&ev);
			return FALSE;
		}
	}
	
	if (opt_poa == CORBA_OBJECT_NIL) {
		opt_poa = (PortableServer_POA)
			CORBA_ORB_resolve_initial_references (
				opt_orb, "RootPOA", &ev);
		if (BONOBO_EX (&ev)) {
			g_warning ("Can not resolve initial reference to RootPOA");
			CORBA_exception_free (&ev);
			return FALSE;
		}
		
	}

	/*
	 * Create the POA Manager.
	 */
	if (opt_manager == CORBA_OBJECT_NIL) {
		opt_manager = PortableServer_POA__get_the_POAManager (opt_poa, &ev);
		if (BONOBO_EX (&ev)){
			g_warning ("Can not get the POA manager");
			CORBA_exception_free (&ev);
			return FALSE;
		}
	}

	/*
	 * Store global copies of these which can be retrieved with
	 * bonobo_orb()/bonobo_poa()/bonobo_poa_manager().
	 */
	__bonobo_orb = opt_orb;
	__bonobo_poa = opt_poa;
	__bonobo_poa_manager = opt_manager;

	CORBA_exception_free (&ev);

#ifdef HAVE_GTHREADS
	_bonobo_lock = g_mutex_new ();
#endif

	bonobo_context_init ();

	bindtextdomain (GETTEXT_PACKAGE, BONOBO_LOCALEDIR);
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif

	return TRUE;
}

/**
 * bonobo_init:
 * @argc: a pointer to the number of arguments
 * @argv: the array of arguments
 *
 * Initializes the bonobo component model.
 *
 * Returns %TRUE on success, or %FALSE on failure.
 */
gboolean
bonobo_init (int *argc, char **argv)
{
	return bonobo_init_full (
		argc, argv, NULL, NULL, NULL);
}

/**
 * bonobo_activate:
 *
 * Activates the Bonobo POA manager registered by bonobo_init.
 * This should be called at the end of application initialization.
 * You do not need to call this function if you use bonobo_main().
 * 
 * Returns %TRUE on success, or %FALSE on failure.
 */
gboolean
bonobo_activate (void)
{
	CORBA_Environment ev;

	CORBA_exception_init (&ev);

	if (!__bonobo_poa_manager) {
		g_warning ("Tried to activate Bonobo before initializing");
		CORBA_exception_free (&ev);
		return FALSE;
	}
	PortableServer_POAManager_activate (__bonobo_poa_manager, &ev);
	if (BONOBO_EX (&ev)){
		g_warning ("Failed to activate the Bonobo POA manager");
		CORBA_exception_free (&ev);
		return FALSE;
	}

	CORBA_exception_free (&ev);
	
	return TRUE;
}

/**
 * bonobo_main:
 * 
 * Activates the Bonobo POA Manager and enters the main event loop.
 */
void
bonobo_main (void)
{
	GMainLoop *loop;

	bonobo_activate ();

	bonobo_main_loop_level++;
  
	loop = g_main_loop_new (NULL, TRUE);
	bonobo_main_loops = g_slist_prepend (bonobo_main_loops, loop);

	if (g_main_loop_is_running (bonobo_main_loops->data))
		g_main_loop_run (loop);

	bonobo_main_loops = g_slist_remove (bonobo_main_loops, loop);

	g_main_loop_unref (loop);

	bonobo_main_loop_level--;
}

/**
 * bonobo_main_level:
 *
 * Determines the number of times the bonobo main loop has been entered (minus
 * the number of exits from the main loop).
 *
 * Returns: The number of main loops currently running (0 if no main loops are
 * running).
 */
guint
bonobo_main_level (void)
{
	return bonobo_main_loop_level;
}

/**
 * bonobo_main_quit:
 * 
 * Quits the main event loop.
 */
void
bonobo_main_quit (void)
{
	g_return_if_fail (bonobo_main_loops != NULL);

	g_main_loop_quit (bonobo_main_loops->data);
}

/**
 * bonobo_poa_get_threadedv:
 * @hint: the desired thread hint
 * @args: va_args related to that hint
 * 
 * Get a predefined POA for a given threading policy/hint.  The
 * returned POA can be passed as the "poa" constructor property of a
 * #BonoboOject.
 * 
 * Return value: the requested POA.
 **/
PortableServer_POA
bonobo_poa_get_threadedv (ORBitThreadHint hint,
			  va_list         args)
{
	PortableServer_POA  poa;
	CORBA_Environment   ev[1];
	CORBA_PolicyList    policies;
	CORBA_Object        policy_vals[1];
	const char         *poa_name = NULL;

#define MAP(a,b) \
	case ORBIT_THREAD_HINT_##a: \
		poa_name = b; \
		break

	switch (hint) {
		MAP (NONE,           "BonoboPOAHintNone");
		MAP (PER_OBJECT,     "BonoboPOAHintPerObject");
		MAP (PER_REQUEST,    "BonoboPOAHintPerRequest");
		MAP (PER_POA,        "BonoboPOAHintPerPOA");
		MAP (PER_CONNECTION, "BonoboPOAHintPerConnection");
		MAP (ONEWAY_AT_IDLE, "BonoboPOAHintOnewayAtIdle");
		MAP (ALL_AT_IDLE,    "BonoboPOAHintAllAtIdle");
		MAP (ON_CONTEXT,     "BonoboPOAHintOnContext");
#undef MAP
	default:
		g_assert_not_reached();
	}

	CORBA_exception_init (ev);

	/* (Copy-paste from ORBit2/test/poa/poatest-basic08.c) */

	policies._length = 1;
	policies._buffer = policy_vals;
	policies._buffer[0] = (CORBA_Object)
		PortableServer_POA_create_thread_policy
			(bonobo_poa (),
			 PortableServer_ORB_CTRL_MODEL,
			 ev);

	poa = bonobo_poa_new_from (__bonobo_poa,
				   poa_name, &policies, ev);

	CORBA_Object_release (policies._buffer[0], NULL);

	if (ev->_major == CORBA_NO_EXCEPTION)
		ORBit_ObjectAdaptor_set_thread_hintv
			((ORBit_ObjectAdaptor) poa, hint, args);

	else {
		if (strcmp (CORBA_exception_id (ev),
			    ex_PortableServer_POA_AdapterAlreadyExists) == 0) {
			CORBA_exception_free (ev);
			poa = PortableServer_POA_find_POA (bonobo_poa (),
							   poa_name,
							   CORBA_FALSE, ev);
		}
	}
	CORBA_exception_free (ev);
	if (!poa)
		g_warning ("Could not create/get poa '%s'", poa_name);

	return poa;
}

/**
 * bonobo_poa_get_threaded:
 * @hint: the desired thread hint
 * 
 * Get a predefined POA for a given threading policy/hint.  The
 * returned POA can be passed as the "poa" constructor property of a
 * #BonoboOject.
 * 
 * Return value: the requested POA.
 **/
PortableServer_POA
bonobo_poa_get_threaded (ORBitThreadHint hint, ...)
{
	va_list args;
	PortableServer_POA poa;

	va_start (args, hint);
	poa = bonobo_poa_get_threadedv (hint, args);
	va_end (args);

	return poa;
}

PortableServer_POA
bonobo_poa_new_from (PortableServer_POA      tmpl,
		     const char             *name,
		     const CORBA_PolicyList *opt_policies,
		     CORBA_Environment      *opt_ev)
{
	PortableServer_POA poa;
	CORBA_Environment real_ev[1], *ev;

	if (!opt_ev)
		CORBA_exception_init ((ev = real_ev));
	else
		ev = opt_ev;

	poa = ORBit_POA_new_from (bonobo_orb(),
				  tmpl, name, opt_policies, ev);

	if (!opt_ev)
		CORBA_exception_free (real_ev);

	return poa;
}


syntax highlighted by Code2HTML, v. 0.9.1