/*
 * Implementation file for Brutus test/sample utility functions.
 * Copyright (C) 2004-2007 OMC Denmark ApS.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "brutus_utils.h"
#include "brutus_macros.h"
#include <bresult/bresult.h>
#include <proptags/proptag.h>
#include <servant_impl/types.h>
#include <servant_impl/servant_utils.h>
#include <string.h>

#include <servant_impl/types_impl.c>

static void
print_ref(const char *desc,
	  CORBA_ORB orb,
	  CORBA_Object obj)
{
	CORBA_string ior = NULL;
	CORBA_Environment ev[1];

	CORBA_exception_init(ev);

	ior = CORBA_ORB_object_to_string(orb, obj, ev);
	g_print("\n%s\n%s\n", desc, ior);

	if (ior)
		CORBA_free(ior);
}


static char *days_to_string(BRUTUS_DayOfWeek days)
{
	char *tmp = NULL;
	char *retv = strdup("");

	if (days & BRUTUS_Monday) {
		tmp = retv;
		retv = g_strconcat(retv, "Monday ", NULL);
		free(tmp);
	}

	if (days & BRUTUS_Tuesday) {
		tmp = retv;
	        retv = g_strconcat(retv, "Tuesday ", NULL);
		free(tmp);
	}

	if (days & BRUTUS_Wednessday) {
		tmp = retv;
		retv = g_strconcat(retv, "Wednessday ", NULL);
		free(tmp);
	}

	if (days & BRUTUS_Thursday) {
		tmp = retv;
		retv = g_strconcat(retv, "Thursday ", NULL);
		free(tmp);
	}

	if (days & BRUTUS_Friday) {
		tmp = retv;
		retv = g_strconcat(retv, "Friday ", NULL);
		free(tmp);
	}

	if (days & BRUTUS_Saturday) {
		tmp = retv;
		retv = g_strconcat(retv, "Saturday ", NULL);
		free(tmp);
	}

	if (days & BRUTUS_Sunday) {
		tmp = retv;
		retv = g_strconcat(retv, "Sunday ", NULL);
		free(tmp);
	}

	return retv;
}

static const char *Month_to_string(BRUTUS_Month M)
{
	switch (M) {
	case BRUTUS_January :
		return "January";
	case BRUTUS_February :
		return "February";
	case BRUTUS_March :
		return "March";
	case BRUTUS_April :
		return "April";
	case BRUTUS_May :
		return "May";
	case BRUTUS_June :
		return "June";
	case BRUTUS_July :
		return "July";
	case BRUTUS_August :
		return "August";
	case BRUTUS_September :
		return "September";
	case BRUTUS_October :
		return "October";
	case BRUTUS_November :
		return "November";
	case BRUTUS_December :
		return "December";
	}

	return "December";
}


PortableServer_POA get_root_poa(const CORBA_ORB Orb,
				CORBA_Environment *ev)
{
	PortableServer_POA root_poa = CORBA_OBJECT_NIL;

	if (CORBA_OBJECT_NIL == Orb)
		return CORBA_OBJECT_NIL;

	root_poa = (PortableServer_POA) CORBA_ORB_resolve_initial_references(Orb, "RootPOA", ev);
	if (ORBIT2_EX(ev)) {
		ORBIT2_PRINT_EX("resolve_initial_references() : ", ev);
		return CORBA_OBJECT_NIL;
	}

	// force multithreaded POA
	ORBit_ObjectAdaptor_set_thread_hint((ORBit_ObjectAdaptor)root_poa,
					    ORBIT_THREAD_HINT_PER_OBJECT);

	return root_poa;
}

PortableServer_POA create_poa(const char *Name,
			      PortableServer_POAManager PoaManager,
			      PortableServer_POA ParentPOA,
			      CORBA_Environment *ev)
{
	CORBA_PolicyList pl;

	if (CORBA_OBJECT_NIL == ParentPOA)
		return CORBA_OBJECT_NIL;

	pl._maximum = 0;
	pl._length  = 0;
	pl._buffer  = NULL;

	PortableServer_POA retv = CORBA_OBJECT_NIL;
	retv = PortableServer_POA_create_POA(ParentPOA,
					     Name,
					     PoaManager,
					     &pl,
					     ev);

	if (ORBIT2_EX(ev)) {
		ORBIT2_PRINT_EX("create_poa() : ", ev);
		return CORBA_OBJECT_NIL;
	}

	// force multithreaded POA
	ORBit_ObjectAdaptor_set_thread_hint((ORBit_ObjectAdaptor)retv,
					    ORBIT_THREAD_HINT_PER_OBJECT);

	return retv;
}

void sbinary_to_entryid(BRUTUS_SBinary *Bin,
			BRUTUS_ENTRYID **Eid)
{
	*Eid = BRUTUS_ENTRYID__alloc();

	CORBA_unsigned_long n;
	for (n=0; n<4; n++)
		(*Eid)->abFlags[n] = Bin->_buffer[n];

	CORBA_unsigned_long len = Bin->_length;
	(*Eid)->ab._maximum = len-4;
	(*Eid)->ab._length = (*Eid)->ab._maximum;
	(*Eid)->ab._buffer = CORBA_sequence_CORBA_octet_allocbuf((*Eid)->ab._length);
	CORBA_sequence_set_release(&(*Eid)->ab, CORBA_TRUE);

	for (n=4; n<len; n++)
		(*Eid)->ab._buffer[n-4] = Bin->_buffer[n];
}

void print_spropvalue(BRUTUS_SPropValue *PropValue)
{
	CORBA_unsigned_long n;
	CORBA_unsigned_long i;

	switch (PropValue->Value._d) {
	case BRUTUS_BRUTUS_PT_UNSPECIFIED :
		break;
	case BRUTUS_BRUTUS_PT_NULL :
		printf("%ld", (long)PropValue->Value._u.x);
		break;
	case BRUTUS_BRUTUS_PT_SHORT :
		printf("%hd (%#.4hx)", PropValue->Value._u.i, PropValue->Value._u.i);
		break;
	case BRUTUS_BRUTUS_PT_USHORT :
		printf("%#.4hx", PropValue->Value._u.ui);
		break;
	case BRUTUS_BRUTUS_PT_LONG :
		if (!(long)PropValue->Value._u.l)
			printf("%ld (0x%#.8lx)", (long)PropValue->Value._u.l, (long)PropValue->Value._u.l);
		else
			printf("%ld (%#.8lx)", (long)PropValue->Value._u.l, (long)PropValue->Value._u.l);
		break;
	case BRUTUS_BRUTUS_PT_ULONG :
		printf("%#.8lx", (unsigned long)PropValue->Value._u.ul);
		break;
	case BRUTUS_BRUTUS_PT_FLOAT :
		printf("%f", PropValue->Value._u.flt);
		break;
	case BRUTUS_BRUTUS_PT_DOUBLE :
		printf("%f", PropValue->Value._u.dbl);
		break;
	case BRUTUS_BRUTUS_PT_CURRENCY :
		printf("Lo:%lu Hi:%ld",
		       (unsigned long)PropValue->Value._u.cur.Lo,
		       (long)PropValue->Value._u.cur.Hi);
		break;
	case BRUTUS_BRUTUS_PT_APPTIME :
		printf("%f", PropValue->Value._u.at);
		break;
	case BRUTUS_BRUTUS_PT_ERROR :
		printf("%lu", (unsigned long)PropValue->Value._u.err);
		break;
	case BRUTUS_BRUTUS_PT_BOOLEAN :
		printf("%s", (CORBA_TRUE == PropValue->Value._u.b) ? "true" : "false");
		break;
	case BRUTUS_BRUTUS_PT_OBJECT :
		printf("Unknown interface");
		break;
	case BRUTUS_BRUTUS_PT_LONGLONG :
		printf("%lld (%#.8llx)", (long long int)PropValue->Value._u.li, (long long int)PropValue->Value._u.li);
		break;
	case BRUTUS_BRUTUS_PT_STRING8 :
		printf("%s", PropValue->Value._u.lpszA);
		break;
	case BRUTUS_BRUTUS_PT_UNICODE :
		printf("%s", (const char*)PropValue->Value._u.lpszW);
		break;
	case BRUTUS_BRUTUS_PT_SYSTIME :
		printf("dwLowDateTime:%lu dwHighDateTime:%lu",
		       (unsigned long)PropValue->Value._u.ft.dwLowDateTime,
		       (unsigned long)PropValue->Value._u.ft.dwHighDateTime);
		break;
	case BRUTUS_BRUTUS_PT_CLSID :
		printf("%s", PropValue->Value._u.lpguid);
		break;
	case BRUTUS_BRUTUS_PT_BINARY :
		if (PropValue->Value._u.bin._length) {
			if (!PropValue->Value._u.bin._buffer[0])
				printf("0x00");
			else
				printf("%#.2hhx", PropValue->Value._u.bin._buffer[0]);
			for (n=1; n<PropValue->Value._u.bin._length; n++) {
				if (!PropValue->Value._u.bin._buffer[n])
					printf(" 0x00");
				else
					printf(" %#.2hhx", PropValue->Value._u.bin._buffer[n]);
			}
		}
		break;
	case BRUTUS_BRUTUS_PT_MV_SHORT :
		if (PropValue->Value._u.MVi._length) {
			if (!PropValue->Value._u.MVi._buffer[0])
				printf("0x0000");
			else
				printf("%hd", PropValue->Value._u.MVi._buffer[0]);
			for (n=1; n<PropValue->Value._u.MVi._length; n++) {
				if (!PropValue->Value._u.MVi._buffer[n])
					printf(" 0x0000");
				else
					printf(" %hd", PropValue->Value._u.MVi._buffer[n]);
			}
		}
		break;
	case BRUTUS_BRUTUS_PT_MV_LONG :
		if (PropValue->Value._u.MVl._length)
			printf("%d", PropValue->Value._u.MVl._buffer[0]);
		for (n=1; n<PropValue->Value._u.MVl._length; n++)
			printf(" %d", PropValue->Value._u.MVl._buffer[n]);
		break;
	case BRUTUS_BRUTUS_PT_MV_FLOAT :
		if (PropValue->Value._u.MVflt._length)
			printf("%f", PropValue->Value._u.MVflt._buffer[0]);
		for (n=1; n<PropValue->Value._u.MVflt._length; n++)
			printf(" %f", PropValue->Value._u.MVflt._buffer[n]);
		break;
	case BRUTUS_BRUTUS_PT_MV_DOUBLE :
		if (PropValue->Value._u.MVdbl._length)
			printf("%f", PropValue->Value._u.MVdbl._buffer[0]);
		for (n=1; n<PropValue->Value._u.MVdbl._length; n++)
			printf(" %f", PropValue->Value._u.MVdbl._buffer[n]);
		break;
	case BRUTUS_BRUTUS_PT_MV_CURRENCY :
		if (PropValue->Value._u.MVcur._length)
			printf("Lo:%lu Hi:%ld",
			       (unsigned long)PropValue->Value._u.MVcur._buffer[0].Lo,
			       (long)PropValue->Value._u.MVcur._buffer[0].Hi);
		for (n=1; n<PropValue->Value._u.MVcur._length; n++)
			printf(" Lo:%lu Hi:%ld",
			       (unsigned long)PropValue->Value._u.MVcur._buffer[n].Lo,
			       (long)PropValue->Value._u.MVcur._buffer[n].Hi);
		break;
	case BRUTUS_BRUTUS_PT_MV_APPTIME :
		if (PropValue->Value._u.MVat._length)
			printf("%f", PropValue->Value._u.MVat._buffer[0]);
		for (n=1; n<PropValue->Value._u.MVat._length; n++)
			printf(" %f", PropValue->Value._u.MVat._buffer[n]);
		break;
	case BRUTUS_BRUTUS_PT_MV_SYSTIME :
		if (PropValue->Value._u.MVft._length)
			printf("dwLowDateTime:%lu dwHighDateTime:%lu",
			       (unsigned long)PropValue->Value._u.MVft._buffer[0].dwLowDateTime,
			       (unsigned long)PropValue->Value._u.MVft._buffer[0].dwHighDateTime);
		for (n=1; n<PropValue->Value._u.MVft._length; n++)
			printf(" dwLowDateTime:%lu dwHighDateTime:%lu",
			       (unsigned long)PropValue->Value._u.MVft._buffer[n].dwLowDateTime,
			       (unsigned long)PropValue->Value._u.MVft._buffer[n].dwHighDateTime);
		break;
	case BRUTUS_BRUTUS_PT_MV_STRING8 :
		if (PropValue->Value._u.MVszA._length)
			printf("%s", (const char*)PropValue->Value._u.MVszA._buffer[0]);
		for (n=1; n<PropValue->Value._u.MVszA._length; n++)
			printf("\n%s", (const char*)PropValue->Value._u.MVszA._buffer[n]);
		break;
	case BRUTUS_BRUTUS_PT_MV_BINARY :
		for (n=0; n<PropValue->Value._u.MVbin._length; n++) {
			if (PropValue->Value._u.MVbin._buffer[n]._length)
				printf("%#.2hhx", PropValue->Value._u.MVbin._buffer[n]._buffer[0]);
			for (i=1; i<PropValue->Value._u.MVbin._buffer[n]._length; i++)
				printf(" %#.2hhx", PropValue->Value._u.MVbin._buffer[n]._buffer[i]);
			printf("\n");
		}
		break;
	case BRUTUS_BRUTUS_PT_MV_UNICODE :
		if (PropValue->Value._u.MVszA._length)
			printf("%s", (const char*)PropValue->Value._u.MVszW._buffer[0]);
		for (n=1; n<PropValue->Value._u.MVszW._length; n++)
			printf("\n%s", (const char*)PropValue->Value._u.MVszW._buffer[n]);
		break;
	case BRUTUS_BRUTUS_PT_MV_CLSID :
		for (n=0; n<PropValue->Value._u.MVguid._length; n++) {
			printf("%s", PropValue->Value._u.MVguid._buffer[n]);
			printf("\n");
		}
		break;
	case BRUTUS_BRUTUS_PT_MV_LONGLONG :
		if (PropValue->Value._u.MVli._length)
			printf("%lld", (long long int)PropValue->Value._u.MVli._buffer[0]);
		for (n=1; n<PropValue->Value._u.MVl._length; n++)
			printf(" %lld", (long long int)PropValue->Value._u.MVli._buffer[n]);
		break;
	default : ;
	}
}

BRUTUS_BRESULT BrQueryAllRows(BRUTUS_IMAPITable ptable,
			      BRUTUS_SPropTagArray *ptags,
			      BRUTUS_SRestrictionContainer pres,
			      BRUTUS_SSortOrderSet *psort,
			      CORBA_long crowsMax,
			      BRUTUS_SRowSet **pprows,
			      CORBA_Environment *ev)
{
	BRUTUS_BRESULT br = BRUTUS_BRUTUS_UNKNOWN_ERROR;

	if (CORBA_Object_is_nil(ptable, ev))
		return BRUTUS_BRUTUS_MAPI_E_INVALID_PARAMETER;

	if (ptags && ptags->_length) {
		br = BRUTUS_IMAPITable_SetColumns(ptable, ptags, 0, ev);
		if (BRUTUS_BRUTUS_S_OK != br)
			return br;
	}

	if (!CORBA_Object_is_nil(pres,ev)) {
		br = BRUTUS_IMAPITable_Restrict(ptable, pres, 0, ev);
		if (BRUTUS_BRUTUS_S_OK != br)
			return br;
	}

	if (psort && psort->aSort._length) {
		br = BRUTUS_IMAPITable_SortTable(ptable, psort, 0, ev);
		if (BRUTUS_BRUTUS_S_OK != br)
			return br;
	}

	if (!crowsMax)
		crowsMax = 0x0fffffff;

	CORBA_unsigned_long status;
	br = BRUTUS_BRUTUS_MAPI_E_BUSY;
	do {
		br = BRUTUS_IMAPITable_QueryRows(ptable, crowsMax, BRUTUS_BRUTUS_TBL_NOADVANCE, pprows, ev);
		if (BRUTUS_BRUTUS_MAPI_E_BUSY == br) {
			br = BRUTUS_IMAPITable_WaitForCompletion(ptable, 0, 1000, &status, ev);
			switch (br) {
			case BRUTUS_BRUTUS_MAPI_E_TIMEOUT :
			case BRUTUS_BRUTUS_MAPI_E_NO_SUPPORT :
			case BRUTUS_BRUTUS_S_OK :
				br = BRUTUS_BRUTUS_MAPI_E_BUSY;
				break;
			default :
				goto out;
			}
		}
	} while (BRUTUS_BRUTUS_MAPI_E_BUSY == br);

out:
	if (*pprows)
		CORBA_sequence_set_release(*pprows, CORBA_TRUE);

	return br;
}

BRUTUS_BRESULT BrGetOneProp(BRUTUS_IMAPIProp MapiProp,
			    BRUTUS_BDEFINE PropTag,
			    BRUTUS_SPropValue **PropValue,
			    CORBA_Environment *ev)
{
	BRUTUS_SPropTagArray proptag_array;
	BRUTUS_seq_SPropValue *props;
	BRUTUS_BRESULT br;

	if (CORBA_Object_is_nil(MapiProp, ev))
		return BRUTUS_BRUTUS_MAPI_E_INVALID_PARAMETER;

	proptag_array._maximum = 1;
	proptag_array._length = 1;
	proptag_array._buffer = CORBA_sequence_BRUTUS_BDEFINE_allocbuf(proptag_array._length);
	proptag_array._buffer[0] = PropTag;

	br = BRUTUS_IMAPIProp_GetProps(MapiProp,
				       &proptag_array,
				       0,
				       &props,
				       ev);
	if (BRUTUS_BRUTUS_S_OK == br)
		*PropValue = ORBit_copy_value(&props->_buffer[0], TC_BRUTUS_SPropValue);

	CORBA_free(props);

	return br;
}

/*
 * I do generally prefer to postpone declaration of variables
 * until they are needed, but my use of goto makes this approach
 * sub-optimal here.
 */
BRUTUS_BRESULT get_default_message_store(BRUTUS_IMAPISession Session,
					 PortableServer_POA ParentPoa,
					 BRUTUS_IMsgStore *MsgStore,
					 CORBA_Environment *ev)
{
	BRUTUS_BRESULT br = BRUTUS_BRUTUS_UNKNOWN_ERROR;
	PortableServer_POAManager poa_manager = CORBA_OBJECT_NIL;
	BRUTUS_IMAPITable mapi_table = CORBA_OBJECT_NIL;
	PortableServer_POA poa = CORBA_OBJECT_NIL;
	BRUTUS_SPropValue pv;
	BRUTUS_SRestriction res;
	BRUTUS_SRestrictionContainer restrict_container = CORBA_OBJECT_NIL;
	BRUTUS_SPropTagArray *proptag_array = NULL;
	BRUTUS_SSortOrderSet sort_order;
	BRUTUS_SRowSet *row_set = NULL;
	BRUTUS_ENTRYID *eid = NULL;
	CORBA_string iid = CORBA_string_dup(""); // use default interface (IMsgStore)

	// sanity check
	if (CORBA_Object_is_nil(Session, ev) || (CORBA_OBJECT_NIL == ParentPoa))
		return BRUTUS_BRUTUS_MAPI_E_INVALID_PARAMETER;

	// create child poa for restriction container content
	poa = create_poa("", CORBA_OBJECT_NIL, ParentPoa, ev);
	if (CORBA_OBJECT_NIL == poa)
		return BRUTUS_BRUTUS_UNKNOWN_ERROR;

	// get content POA manager
	poa_manager = PortableServer_POA__get_the_POAManager(poa, ev);
	if (ORBIT2_EX(ev)) {
		ORBIT2_PRINT_EX("PortableServer_POA__get_the_POAManager() : ", ev);
		goto exit;
	}

	// activate the content poa
	PortableServer_POAManager_activate(poa_manager, ev);
	if (ORBIT2_EX(ev)) {
		ORBIT2_PRINT_EX("PortableServer_POAManager_activate() : ", ev);
		goto exit;
	}

	// get message store table
	br = BRUTUS_IMAPISession_GetMsgStoresTable(Session, 0, &mapi_table, ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	// initiate SPropValue
	pv.dwAlignPad = 0;
	pv.ulPropTag = BRUTUS_BRUTUS_PR_DEFAULT_STORE;
	pv.Value._d = BRUTUS_BRUTUS_PT_BOOLEAN;
	pv.Value._u.b = CORBA_TRUE;

	// initiate restriction union
	res._d = BRUTUS_BRUTUS_RES_PROPERTY;

	// create SPropertyRestriction
	res._u.resProperty = impl_BRUTUS_SPropertyRestriction__create(BRUTUS_BRUTUS_RELOP_EQ,
								      BRUTUS_BRUTUS_PR_DEFAULT_STORE,
								      pv,
								      poa,
								      ev);

	// Create restriction container.
	// It is important that the container itself is *not* created
	// under the restriction container content poa.
	restrict_container = impl_BRUTUS_SRestrictionContainer__create(&res,      // content
								       poa,       // content poa
								       ParentPoa, // poa for restriction container
								       ev);
	// the content poa and the restriction content will
	// be properly destroyed by the container
	CORBA_Object_release((CORBA_Object)poa, ev);
	CORBA_Object_release(res._u.resProperty, ev);

	// Only retrieve entry id.
	// I am using a pointer as it it easier to release the entire
	// array at once than just the _buffer
	proptag_array = BRUTUS_SPropTagArray__alloc();
	proptag_array->_maximum = 1;
	proptag_array->_length = 1;
	proptag_array->_buffer = CORBA_sequence_BRUTUS_BDEFINE_allocbuf(proptag_array->_length);
	proptag_array->_buffer[0] = BRUTUS_BRUTUS_PR_ENTRYID;
	CORBA_sequence_set_release(proptag_array, CORBA_TRUE);

	// empty sort order
	sort_order.cCategories = 0;
	sort_order.cExpanded = 0;
	sort_order.aSort._maximum = 0;
	sort_order.aSort._length = 0;
	sort_order.aSort._buffer = NULL;

	// get the single row
	br = BrQueryAllRows(mapi_table,
			    proptag_array,
			    restrict_container,
			    &sort_order,
			    0,
			    &row_set,
			    ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	// get entry id from BLOB
	sbinary_to_entryid(&row_set->_buffer[(CORBA_unsigned_long)0]._buffer[(CORBA_unsigned_long)0].Value._u.bin, &eid);

	// Open the first returned (default) message store
	br = BRUTUS_IMAPISession_OpenMsgStore(Session,
					      (const BRUTUS_ENTRYID*)eid,
					      iid,
					      (const BRUTUS_BDEFINE)BRUTUS_BRUTUS_MAPI_BEST_ACCESS,
					      MsgStore,
					      ev);
	if (ORBIT2_EX(ev))
		ORBIT2_PRINT_EX("OpenMsgStore() : ", ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

exit:
	if (BRUTUS_BRUTUS_S_OK != br) {
		BRUTUS_MAPIERROR *mapi_error = NULL;
		if (BRUTUS_BRUTUS_S_OK == BRUTUS_IMAPISession_GetLastError(Session, br, 0, &mapi_error, ev))
			printf("\n\tlpszError     : %s\n\tlpszComponent : %s\n\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
		CORBA_free(mapi_error);
	}

	CORBA_free(eid);
	CORBA_free(iid);
	CORBA_free(proptag_array);
	CORBA_free(row_set);

	if (CORBA_OBJECT_NIL != poa_manager) {
		CORBA_Object_release((CORBA_Object)poa_manager, ev);
		if (ORBIT2_EX(ev))
			ORBIT2_PRINT_EX("free_orbit_object() : ", ev);
	}

	if (!CORBA_Object_is_nil(restrict_container, ev)) {
		free_orbit_object(restrict_container, poa, ev);
		if (ORBIT2_EX(ev))
			ORBIT2_PRINT_EX("CORBA_Object_release() : ", ev);
	}

	if (!CORBA_Object_is_nil(mapi_table, ev)) {
		BRUTUS_IMAPITable_Destroy(mapi_table, 0, ev);
		if (ORBIT2_EX(ev))
			ORBIT2_PRINT_EX("IMAPITable_Destroy() : ", ev);

		CORBA_Object_release(mapi_table, ev);
		if (ORBIT2_EX(ev))
			ORBIT2_PRINT_EX("CORBA_Object_release() : ", ev);
	}

	return br;
}

BRUTUS_BRESULT get_root_folder(BRUTUS_IMsgStore MsgStore,
			       BRUTUS_IMAPIFolder *RootFolder,
			       CORBA_Environment *ev)
{
	BRUTUS_BRESULT br;
	BRUTUS_BDEFINE obj_type;
	BRUTUS_IUnknown unk = CORBA_OBJECT_NIL;
	CORBA_string iid = CORBA_string_dup(""); // use default interface (IMAPIFolder)
	BRUTUS_ENTRYID eid = { // open root folder
		.abFlags = {0},
		.ab = {0}
	};

	// initiate out value asap
	*RootFolder = CORBA_OBJECT_NIL;

	// sanity check
	if (CORBA_Object_is_nil(MsgStore, ev))
		return BRUTUS_BRUTUS_MAPI_E_INVALID_PARAMETER;

	br = BRUTUS_IMsgStore_OpenEntry(MsgStore,
					&eid,
					iid,
					BRUTUS_BRUTUS_MAPI_BEST_ACCESS,
					&obj_type,
					&unk,
					ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	if (BRUTUS_BRUTUS_MAPI_FOLDER == obj_type)
		*RootFolder = (BRUTUS_IMAPIFolder)CORBA_Object_duplicate(unk, ev);
	else {
		BRUTUS_IUnknown_Destroy(unk, 0, ev);
		goto exit;
	}

exit:
	if (BRUTUS_BRUTUS_S_OK != br) {
		BRUTUS_MAPIERROR *mapi_error = NULL;
		if (BRUTUS_BRUTUS_S_OK == BRUTUS_IMsgStore_GetLastError(MsgStore, br, 0, &mapi_error, ev))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
		CORBA_free(mapi_error);
	}

	CORBA_free(iid);

	CORBA_Object_release(unk, ev);

	return br;
}

BRUTUS_BRESULT get_outlook_folder(BRUTUS_IMsgStore MsgStore,
				  BRUTUS_BDEFINE PropTag,
				  BRUTUS_IMAPIFolder *Folder,
				  CORBA_Environment *ev)
{
	BRUTUS_BRESULT br;
	BRUTUS_ENTRYID *eid = NULL;
	BRUTUS_SPropValue *pv = NULL;
	CORBA_string iid = CORBA_string_dup(""); // use default interface (IMAPIFolder)
	BRUTUS_BDEFINE obj_type;
	BRUTUS_IUnknown unk = CORBA_OBJECT_NIL;
	BRUTUS_IMAPIFolder root_folder = CORBA_OBJECT_NIL;;

	// initiate out value asap
	*Folder = CORBA_OBJECT_NIL;

	// sanity check
	if (CORBA_Object_is_nil(MsgStore, ev))
		return BRUTUS_BRUTUS_MAPI_E_INVALID_PARAMETER;

	br = get_root_folder(MsgStore,
			     &root_folder,
			     ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	br = BrGetOneProp(root_folder,
			  PropTag,
			  &pv,
			  ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	// get entry id from BLOB
	sbinary_to_entryid(&pv->Value._u.bin, &eid);

	// open the outlook folder
	br = BRUTUS_IMAPIFolder_OpenEntry(root_folder,
					  eid,
					  iid,
					  BRUTUS_BRUTUS_MAPI_BEST_ACCESS,
					  &obj_type,
					  &unk,
					  ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	if (BRUTUS_BRUTUS_MAPI_FOLDER == obj_type)
		*Folder = (BRUTUS_IMAPIFolder)CORBA_Object_duplicate(unk, ev);
	else {
		BRUTUS_IUnknown_Destroy(unk, 0, ev);
		goto exit;
	}

exit:
	if (BRUTUS_BRUTUS_S_OK != br) {
		BRUTUS_MAPIERROR *mapi_error = NULL;
		if (BRUTUS_BRUTUS_S_OK == BRUTUS_IMsgStore_GetLastError(MsgStore, br, 0, &mapi_error, ev))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
		CORBA_free(mapi_error);
	}

	CORBA_Object_release(unk, ev);
	CORBA_free(pv);
	CORBA_free(eid);
	CORBA_free(iid);

	// release server resources
	if (!CORBA_Object_is_nil(root_folder, ev)) {
		BRUTUS_IMAPIFolder_Destroy(root_folder, 0, ev);
		CORBA_Object_release(root_folder, ev);
	}

	return br;
}

BRUTUS_BRESULT list_container(BRUTUS_IMAPIContainer Container,
			      BRUTUS_BDEFINE ListBy,
			      CORBA_Environment *ev)
{
	BRUTUS_BRESULT br;
	BRUTUS_SSortOrderSet sort_order;
	BRUTUS_IMAPITable contents_table = CORBA_OBJECT_NIL;
	BRUTUS_SRowSet *row_set = NULL;
	BRUTUS_SPropTagArray *proptag_array = NULL;

	// sanity check
	if (CORBA_Object_is_nil(Container, ev))
		return BRUTUS_BRUTUS_MAPI_E_INVALID_PARAMETER;

	br = BRUTUS_IMAPIContainer_GetContentsTable(Container,
						    0,
						    &contents_table,
						    ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	// we are only interested in the ListBy property
	proptag_array = BRUTUS_SPropTagArray__alloc();
	proptag_array->_maximum = 1;
	proptag_array->_length = 1;
	proptag_array->_buffer = BRUTUS_SPropTagArray_allocbuf(proptag_array->_length);
	proptag_array->_buffer[0] = ListBy;
	CORBA_sequence_set_release(proptag_array, CORBA_TRUE);

	// empty sort order
	sort_order.cCategories = 0;
	sort_order.cExpanded = 0;
	sort_order.aSort._maximum = 0;
	sort_order.aSort._length = 0;
	sort_order.aSort._buffer = NULL;

	// get the single row
	br = BrQueryAllRows(contents_table,
			    proptag_array,
			    CORBA_OBJECT_NIL,
			    &sort_order,
			    0,
			    &row_set,
			    ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	// write the object values to stdout
	if (row_set->_length) {
		CORBA_unsigned_long n;
		for (n=0; n<row_set->_length; n++) {
			printf("        %d: ", n);
			print_spropvalue(&row_set->_buffer[n]._buffer[(CORBA_unsigned_long)0]);
			printf("\n");
		}
	}

exit:
	if (BRUTUS_BRUTUS_S_OK != br) {
		BRUTUS_MAPIERROR *mapi_error = NULL;
		if (BRUTUS_BRUTUS_S_OK == BRUTUS_IMAPIProp_GetLastError(Container, br, 0, &mapi_error, ev))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
		CORBA_free(mapi_error);
	}

	CORBA_free(row_set);
	CORBA_free(proptag_array);

	// release server resources
	if (!CORBA_Object_is_nil(contents_table, ev)) {
		BRUTUS_IMAPITable_Destroy(contents_table, 0, ev);
		CORBA_Object_release(contents_table, ev);
	}

	return br;
}

BRUTUS_BRESULT 
dump_contentstable_row(BRUTUS_IMAPIContainer Container,
		       unsigned int row,
		       CORBA_Environment *ev)
{
	BRUTUS_BRESULT br;
	BRUTUS_IMAPITable contents_table = CORBA_OBJECT_NIL;
	BRUTUS_SRowSet *row_set = NULL;
	CORBA_long rows_sought;
	BRUTUS_SPropTagArray *proptag_array = NULL;

	// sanity check
	if (CORBA_Object_is_nil(Container, ev))
		return BRUTUS_BRUTUS_MAPI_E_INVALID_PARAMETER;

	printf("\n");
	printf("Dumping contents table row number %d:\n", row);
	printf("=====================================\n\n");
	br = BRUTUS_IMAPIContainer_GetContentsTable(Container,
						    0,
						    &contents_table,
						    ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	br = BRUTUS_IMAPITable_SeekRow(contents_table,
				       BRUTUS_BRUTUS_BOOKMARK_BEGINNING, 
				       (CORBA_long)row,  
				       &rows_sought,
				       ev);

	proptag_array = BRUTUS_SPropTagArray__alloc();
	proptag_array->_maximum = 16;
	proptag_array->_length = proptag_array->_maximum;
	proptag_array->_buffer = BRUTUS_SPropTagArray_allocbuf(proptag_array->_maximum);
	CORBA_sequence_set_release(proptag_array, CORBA_TRUE);

	// GAL
	proptag_array->_buffer[0] = BRUTUS_BRUTUS_PR_DISPLAY_NAME;
	proptag_array->_buffer[1] = BRUTUS_BRUTUS_PR_SURNAME;
	proptag_array->_buffer[2] = BRUTUS_BRUTUS_PR_GIVEN_NAME;
	proptag_array->_buffer[3] = BRUTUS_BRUTUS_PR_TITLE;
	proptag_array->_buffer[4] = BRUTUS_BRUTUS_PR_COMPANY_NAME;
	proptag_array->_buffer[5] = BRUTUS_BRUTUS_PR_DEPARTMENT_NAME;
	proptag_array->_buffer[6] = BRUTUS_BRUTUS_PR_OFFICE_LOCATION;
	proptag_array->_buffer[7] = BRUTUS_BRUTUS_PR_EMS_AB_DESCRIPTION;
	proptag_array->_buffer[8] = BRUTUS_BRUTUS_PR_COMMENT;
	proptag_array->_buffer[9] = BRUTUS_BRUTUS_PR_SMTP_ADDRESS;
	proptag_array->_buffer[10] = BRUTUS_BRUTUS_PR_BUSINESS_TELEPHONE_NUMBER;
	proptag_array->_buffer[11] = BRUTUS_BRUTUS_PR_STREET_ADDRESS;
	proptag_array->_buffer[12] = BRUTUS_BRUTUS_PR_LOCALITY;
	proptag_array->_buffer[13] = BRUTUS_BRUTUS_PR_STATE_OR_PROVINCE;
	proptag_array->_buffer[14] = BRUTUS_BRUTUS_PR_ENTRYID;
	proptag_array->_buffer[15] = BRUTUS_BRUTUS_PR_LAST_MODIFICATION_TIME;

	// PAB


	// get the two rows
	br = BrQueryAllRows(contents_table,
			    proptag_array,
			    CORBA_OBJECT_NIL,
			    NULL,
			    1,
			    &row_set,
			    ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;


	printf("Row set length is %d:\n", row_set->_length);
	if (!row_set->_length) {
		printf("No rows\n");
		goto exit;
	}

	printf("Row number %d length is %d:\n", row, row_set->_buffer[0]._length);
	if (1 == row_set->_length) {
		CORBA_unsigned_long n;
		for (n = 0; n < row_set->_buffer[0]._length; n++) {
			printf("%#.8lx (%s/%s) :\n",
			       (unsigned long)row_set->_buffer[0]._buffer[n].ulPropTag,
			       proptag_to_str(row_set->_buffer[0]._buffer[n].ulPropTag),
			       proptype_to_str(row_set->_buffer[0]._buffer[n].ulPropTag & 0x0000ffff));
			print_spropvalue(&row_set->_buffer[0]._buffer[n]);
			printf("\n\n");
		}
	}

exit:
	if (BRUTUS_BRUTUS_S_OK != br) {
		BRUTUS_MAPIERROR *mapi_error = NULL;
		if (BRUTUS_BRUTUS_S_OK == BRUTUS_IMAPIProp_GetLastError(Container, br, 0, &mapi_error, ev))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
		CORBA_free(mapi_error);
	}

	CORBA_free(row_set);
	CORBA_free(proptag_array);

	// release server resources
	if (!CORBA_Object_is_nil(contents_table, ev)) {
		BRUTUS_IMAPITable_Destroy(contents_table, 0, ev);
		CORBA_Object_release(contents_table, ev);
	}

	return br;
}

BRUTUS_BRESULT print_properties(BRUTUS_IMAPIProp PropObject,
				CORBA_Environment *ev)
{
	BRUTUS_BRESULT br = BRUTUS_BRUTUS_UNKNOWN_ERROR;
	BRUTUS_seq_SPropValue *props;
	BRUTUS_SPropTagArray proptag_array;

	proptag_array._maximum = 0;
	proptag_array._length = 0;
	proptag_array._buffer = NULL;

	br = BRUTUS_IMAPIProp_GetProps(PropObject,
				       &proptag_array,
				       0,
				       &props,
				       ev);
	if (ORBIT2_EX(ev)) {
		ORBIT2_PRINT_EX("BRUTUS_IMAPIProp_GetProps() : ", ev);
		goto out;
	}
	if ((BRUTUS_BRUTUS_S_OK != br) && (BRUTUS_BRUTUS_MAPI_W_ERRORS_RETURNED != br))
		goto exit;

	CORBA_unsigned_long n;
	for (n=0; n<props->_length; n++) {
		printf("%#.8lx (%s/%s) :\n",
		       (unsigned long)props->_buffer[n].ulPropTag,
		       proptag_to_str(props->_buffer[n].ulPropTag),
		       proptype_to_str(props->_buffer[n].ulPropTag & 0x0000ffff));
		print_spropvalue(&props->_buffer[n]);
		printf("\n\n");
	}

exit:
	if (BRUTUS_BRUTUS_S_OK != br) {
		BRUTUS_MAPIERROR *mapi_error = NULL;
		if (BRUTUS_BRUTUS_S_OK == BRUTUS_IMAPIProp_GetLastError(PropObject, br, 0, &mapi_error, ev))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
		CORBA_free(mapi_error);
	}
out:
	CORBA_free(props);

	return br;
}

BRUTUS_BRESULT dump_container(BRUTUS_IMAPIContainer Container,
			      CORBA_ORB orb,
			      CORBA_Environment *ev)
{
	BRUTUS_BRESULT br;
	BRUTUS_SSortOrderSet sort_order;
	BRUTUS_IMAPITable contents_table = CORBA_OBJECT_NIL;
	BRUTUS_SRowSet *row_set = NULL;
	BRUTUS_SPropTagArray *proptag_array = NULL;
	BRUTUS_ENTRYID *eid = NULL;
	BRUTUS_SPropValue *pv = NULL;
	BRUTUS_BDEFINE obj_type;
	BRUTUS_IUnknown unk = CORBA_OBJECT_NIL;
	BRUTUS_IMAPIProp obj = CORBA_OBJECT_NIL;

	// sanity check
	if (CORBA_Object_is_nil(Container, ev))
		return BRUTUS_BRUTUS_MAPI_E_INVALID_PARAMETER;

	printf("\n\n");
	printf("Dumping container:\n");
	printf("==================\n\n");
	print_properties(Container, ev);

	printf("\n");
	printf("Dumping container content:\n");
	printf("==========================\n\n");
	br = BRUTUS_IMAPIContainer_GetContentsTable(Container,
						    0,
						    &contents_table,
						    ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	// we must get the entryid so that we can query the object
	proptag_array = BRUTUS_SPropTagArray__alloc();
	proptag_array->_maximum = 2;
	proptag_array->_length = 2;
	proptag_array->_buffer = BRUTUS_SPropTagArray_allocbuf(proptag_array->_length);
	proptag_array->_buffer[0] = BRUTUS_BRUTUS_PR_ENTRYID;
	proptag_array->_buffer[1] = BRUTUS_BRUTUS_PR_DISPLAY_NAME;
	CORBA_sequence_set_release(proptag_array, CORBA_TRUE);

	// empty sort order
	sort_order.cCategories = 0;
	sort_order.cExpanded = 0;
	sort_order.aSort._maximum = 0;
	sort_order.aSort._length = 0;
	sort_order.aSort._buffer = NULL;

	// get the two rows
	br = BrQueryAllRows(contents_table,
			    proptag_array,
			    CORBA_OBJECT_NIL,
			    &sort_order,
			    0,
			    &row_set,
			    ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	// dump the container content to stdout
	if (row_set->_length) {
		CORBA_unsigned_long n;
		for (n=0; n<row_set->_length; n++) {
			// get entry id from BLOB
			pv = &row_set->_buffer[n]._buffer[(CORBA_unsigned_long)0];
			sbinary_to_entryid(&pv->Value._u.bin, &eid);

			// open the outlook folder
			br = BRUTUS_IMAPIContainer_OpenEntry(Container,
							     eid,
							     BRUTUS_BRUTUS_IID_IMAPIProp,
							     BRUTUS_BRUTUS_MAPI_BEST_ACCESS,
							     &obj_type,
							     &unk,
							     ev);
			if (BRUTUS_BRUTUS_S_OK != br)
				continue;
			obj = (BRUTUS_IMAPIProp)unk;
			print_ref("Container item IOR:", orb, obj);

			printf("        Container item %d: \"", n);
			print_spropvalue(&row_set->_buffer[n]._buffer[(CORBA_unsigned_long)1]); // display name
			printf("\"\n");
			print_properties(obj, ev);
			printf("\n");
			BRUTUS_IMAPIProp_Destroy(obj, 0, ev);
			CORBA_Object_release(obj, ev);
		}
	}


exit:
	if (BRUTUS_BRUTUS_S_OK != br) {
		BRUTUS_MAPIERROR *mapi_error = NULL;
		if (BRUTUS_BRUTUS_S_OK == BRUTUS_IMAPIProp_GetLastError(Container, br, 0, &mapi_error, ev))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
		CORBA_free(mapi_error);
	}

	CORBA_free(row_set);
	CORBA_free(proptag_array);

	// release server resources
	if (!CORBA_Object_is_nil(contents_table, ev)) {
		BRUTUS_IMAPITable_Destroy(contents_table, 0, ev);
		CORBA_Object_release(contents_table, ev);
	}

	return br;
}

BRUTUS_BRESULT summarize_calendar_recurrences(BRUTUS_IMsgStore MsgStore,
					      CORBA_Environment *ev)
{
	BRUTUS_BRESULT br;
	BRUTUS_SSortOrderSet sort_order;
	BRUTUS_IMAPIFolder mapi_folder = CORBA_OBJECT_NIL;
	BRUTUS_IMAPITable contents_table = CORBA_OBJECT_NIL;
	BRUTUS_SRowSet *row_set = NULL;
	BRUTUS_SPropTagArray *proptag_array = NULL;
	BRUTUS_ENTRYID *eid = NULL;
	BRUTUS_SPropValue *pv = NULL;
	BRUTUS_BDEFINE obj_type;
	BRUTUS_IUnknown unk = CORBA_OBJECT_NIL;
	BRUTUS_IMessage msg = CORBA_OBJECT_NIL;
	BRUTUS_RecurrenceState state;

	// sanity check
	if (CORBA_Object_is_nil(MsgStore, ev))
		return BRUTUS_BRUTUS_MAPI_E_INVALID_PARAMETER;

	br = get_outlook_folder(MsgStore,
				BRUTUS_BRUTUS_PR_OUTLOOK_CALENDAR,
				&mapi_folder,
				ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	br = BRUTUS_IMAPIFolder_GetContentsTable(mapi_folder,
						 0,
						 &contents_table,
						 ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	// we must get the entryid so that we can query the object
	proptag_array = BRUTUS_SPropTagArray__alloc();
	proptag_array->_maximum = 2;
	proptag_array->_length = 2;
	proptag_array->_buffer = BRUTUS_SPropTagArray_allocbuf(proptag_array->_length);
	proptag_array->_buffer[0] = BRUTUS_BRUTUS_PR_ENTRYID;
	proptag_array->_buffer[1] = BRUTUS_BRUTUS_PR_SUBJECT;
	CORBA_sequence_set_release(proptag_array, CORBA_TRUE);

	// empty sort order
	sort_order.cCategories = 0;
	sort_order.cExpanded = 0;
	sort_order.aSort._maximum = 0;
	sort_order.aSort._length = 0;
	sort_order.aSort._buffer = NULL;

	// get the single row
	br = BrQueryAllRows(contents_table,
			    proptag_array,
			    CORBA_OBJECT_NIL,
			    &sort_order,
			    0,
			    &row_set,
			    ev);
	if (BRUTUS_BRUTUS_S_OK != br)
		goto exit;

	// write the object values to stdout
	if (row_set->_length) {
		CORBA_unsigned_long n;
		for (n=0; n<row_set->_length; n++) {
			// get entry id from BLOB
			pv = &row_set->_buffer[n]._buffer[(CORBA_unsigned_long)0];
			sbinary_to_entryid(&pv->Value._u.bin, &eid);

			// open the outlook folder
			br = BRUTUS_IMAPIFolder_OpenEntry(mapi_folder,
							  eid,
							  "",
							  BRUTUS_BRUTUS_MAPI_BEST_ACCESS,
							  &obj_type,
							  &unk,
							  ev);
			if (BRUTUS_BRUTUS_S_OK != br)
				continue;

			msg = (BRUTUS_IMessage)unk;
			br = BRUTUS_IMessage_GetRecurrenceState(msg, CORBA_TRUE, &state, ev);
			BRUTUS_IMessage_Destroy(msg, 0, ev);
			CORBA_Object_release(msg, ev);

			printf("        %d: ", n);
			print_spropvalue(&row_set->_buffer[n]._buffer[(CORBA_unsigned_long)1]); // subject
			printf("\n");
			print_recurrence_state(&state); // recurrence
			printf("\n");
		}
	}

exit:
	if (BRUTUS_BRUTUS_S_OK != br) {
		BRUTUS_MAPIERROR *mapi_error = NULL;
		if (BRUTUS_BRUTUS_S_OK == BRUTUS_IMAPIFolder_GetLastError(mapi_folder, br, 0, &mapi_error, ev))
			printf("\n%s\n%s\n", (const char*)mapi_error->lpszError, (const char*)mapi_error->lpszComponent);
		CORBA_free(mapi_error);
	}

	CORBA_free(eid);
	CORBA_free(row_set);
	CORBA_free(proptag_array);

	// release server resources
	if (!CORBA_Object_is_nil(contents_table, ev)) {
		BRUTUS_IMAPITable_Destroy(contents_table, 0, ev);
		CORBA_Object_release(contents_table, ev);
	}

	if (!CORBA_Object_is_nil(mapi_folder, ev)) {
		BRUTUS_IMAPIFolder_Destroy(mapi_folder, 0, ev);
		CORBA_Object_release(mapi_folder, ev);
	}

	return br;
}

BRUTUS_BRESULT print_recurrence_state(BRUTUS_RecurrenceState *state)
{

	if (BRUTUS_recur_none == state->pattern._d) {
		printf("        Not a recurring event\n");
		return BRUTUS_BRUTUS_S_OK;
	}

	printf("        occurrence_count   = %d\n", state->occurrence_count);
	printf("        exception_count    = %d\n", state->exception_count);
	printf("        modification_count = %d\n", state->modification_count);
	switch (state->how_to_end) {
	case BRUTUS_term_date :
		printf("        how_to_end         = term_date\n");
		break;
	case BRUTUS_term_number :
		printf("        how_to_end         = term_number\n");
		break;
	case BRUTUS_term_never :
		printf("        how_to_end         = term_never\n");
		break;
	}
	switch (state->first_day_of_week) {
	case BRUTUS_Monday :
		printf("        first_day_of_week  = Monday\n");
		break;
	case BRUTUS_Tuesday :
		printf("        first_day_of_week  = Tuesday\n");
		break;

	case BRUTUS_Wednessday :
		printf("        first_day_of_week  = Wednessday\n");
		break;

	case BRUTUS_Thursday :
		printf("        first_day_of_week  = Thursday\n");
		break;

	case BRUTUS_Friday :
		printf("        first_day_of_week  = Friday\n");
		break;

	case BRUTUS_Saturday :
		printf("        first_day_of_week  = Saturday\n");
		break;

	case BRUTUS_Sunday :
		printf("        first_day_of_week  = Sunday\n");
		break;

	}

	switch (state->pattern._d) {
	case BRUTUS_recur_daily_every_N_days :
		printf("        Daily every N days:\n");
		printf("            N                 = %d\n", state->pattern._u.daily_every_N_days.N);
		printf("            regenerating_task = %s\n", state->pattern._u.daily_every_N_days.regenerating_task ? "TRUE" : "FALSE");
		break;
	case BRUTUS_recur_weekly_every_N_weeks :
	{
		char *days = days_to_string(state->pattern._u.weekly_every_N_weeks.which_days);
		printf("        Weekly every N weeks:\n");
		printf("            N                 = %d\n", state->pattern._u.weekly_every_N_weeks.N);
		printf("            regenerating_task = %s\n", state->pattern._u.weekly_every_N_weeks.regenerating_task ? "TRUE" : "FALSE");
		printf("            which_days        = %s\n", days);
		free(days);
	}
	break;
	case BRUTUS_recur_weekly_every_N_weeks_for_regenerating_tasks :
		printf("        Weekly every N weeks for regenerating tasks:\n");
		printf("            N                 = %d\n", state->pattern._u.weekly_every_N_weeks_for_regenerating_tasks.N);
		break;
	case BRUTUS_recur_monthly_every_N_months_on_day_D :
		printf("        Monthly every N months on day D:\n");
		printf("            N                 = %d\n", state->pattern._u.monthly_every_N_months_on_day_D.N);
		printf("            D                 = %d\n", state->pattern._u.monthly_every_N_months_on_day_D.D);
		printf("            regenerating_task = %s\n", state->pattern._u.monthly_every_N_months_on_day_D.regenerating_task ? "TRUE" : "FALSE");
		break;
	case BRUTUS_recur_monthly_every_N_months_on_Xth_Y :
	{
		char *days = days_to_string(state->pattern._u.monthly_every_N_months_on_Xth_Y.Y);
		printf("        Monthly every N months on Xth Y:\n");
		printf("            N                 = %d\n", state->pattern._u.monthly_every_N_months_on_Xth_Y.N);
		printf("            X                 = %d\n", state->pattern._u.monthly_every_N_months_on_Xth_Y.X);
		printf("            Y                 = %s\n", days);
		free(days);
	}
	break;
	case BRUTUS_recur_yearly_on_day_D_of_month_M :
		printf("        Yearly on day D of month M:\n");
		printf("            D                 = %d\n", state->pattern._u.yearly_on_day_D_of_month_M.D);
		printf("            M                 = %s\n", Month_to_string(state->pattern._u.yearly_on_day_D_of_month_M.M));
		printf("            regenerating_task = %s\n", state->pattern._u.yearly_on_day_D_of_month_M.regenerating_task ? "TRUE" : "FALSE");
		break;
	case BRUTUS_recur_yearly_on_the_Xth_Y_of_month_M :
	{
		char *days = days_to_string(state->pattern._u.yearly_on_the_Xth_Y_of_month_M.Y);
		printf("        Yearly on the Xth Y of month M:\n");
		printf("            X                 = %d\n", state->pattern._u.yearly_on_the_Xth_Y_of_month_M.X);
		printf("            Y                 = %s\n", days);
		printf("            M                 = %s\n", Month_to_string(state->pattern._u.yearly_on_the_Xth_Y_of_month_M.M));
		free(days);
	}
	break;
	default :
		return BRUTUS_BRUTUS_INTERNAL_ERROR;
	};

	return BRUTUS_BRUTUS_S_OK;
}
