/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2008 OMC Denmark ApS.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "IMAPITableS_impl.h"
#include "obj_utils.h"
#include "conv_utils.h"

#include <ace/String_Base.h>

static inline ULONG native_bookmark(const ::BRUTUS::BDEFINE BookMark)
{
	switch (BookMark) {
	case ::BRUTUS::BRUTUS_BOOKMARK_BEGINNING :
		return (ULONG)BOOKMARK_BEGINNING;
	case ::BRUTUS::BRUTUS_BOOKMARK_CURRENT :
		return (ULONG)BOOKMARK_CURRENT;
	case ::BRUTUS::BRUTUS_BOOKMARK_END :
		return (ULONG)BOOKMARK_END;
	}

	{
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "Unknown Bookmark from BRUTUS : %X", BookMark);
		BRUTUS_LOG_BUG(msg);
	}
	return (ULONG)BookMark;
}


static inline ULONG native_flags(const ::BRUTUS::BDEFINE Flags)
{
	::BRUTUS::BDEFINE flags = Flags;
	ULONG retval = 0;

	if (!Flags)
		return 0;

	if (flags & ::BRUTUS::BRUTUS_MAPI_UNICODE) {
		retval |= MAPI_UNICODE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_UNICODE);
	}
	if (flags & ::BRUTUS::BRUTUS_TBL_ASYNC) {
		retval |= TBL_ASYNC;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_TBL_ASYNC);
	}
	if (flags & ::BRUTUS::BRUTUS_TBL_BATCH) {
		retval |= TBL_BATCH;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_TBL_BATCH);
	}
	if (flags & ::BRUTUS::BRUTUS_TBL_ALL_COLUMNS) {
		retval |= TBL_ALL_COLUMNS;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_TBL_ALL_COLUMNS);
	}
	if (flags & ::BRUTUS::BRUTUS_TBL_LEAF_ROW) {
		retval |= TBL_LEAF_ROW;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_TBL_LEAF_ROW);
	}
	if (flags & ::BRUTUS::BRUTUS_TBL_EMPTY_CATEGORY) {
		retval |= TBL_EMPTY_CATEGORY;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_TBL_EMPTY_CATEGORY);
	}
	if (flags & ::BRUTUS::BRUTUS_TBL_EXPANDED_CATEGORY) {
		retval |= TBL_EXPANDED_CATEGORY;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_TBL_EXPANDED_CATEGORY);
	}
	if (flags & ::BRUTUS::BRUTUS_TBL_COLLAPSED_CATEGORY) {
		retval |= TBL_COLLAPSED_CATEGORY;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_TBL_COLLAPSED_CATEGORY);
	}
	if (flags & ::BRUTUS::BRUTUS_DIR_BACKWARD) {
		retval |= DIR_BACKWARD;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_DIR_BACKWARD);
	}
	if (flags & ::BRUTUS::BRUTUS_TBL_NOADVANCE) {
		retval |= TBL_NOADVANCE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_TBL_NOADVANCE);
	}

	if (flags) {
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "Unknown flag(s) from BRUTUS : %X", flags);
		BRUTUS_LOG_BUG(msg);
	}

	return retval;
}


static inline ULONG native_eventmask(const ::BRUTUS::BDEFINE EventMask)
{
	::BRUTUS::BDEFINE event_mask = EventMask;
	ULONG retval = 0;

	if (!EventMask)
		return 0;

	if (event_mask & ::BRUTUS::BRUTUS_fnevTableModified) {
		retval |= fnevTableModified;
		FLAGS_OFF(::BRUTUS::BDEFINE, event_mask, ::BRUTUS::BRUTUS_fnevTableModified);
	}

	if (event_mask) {
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "Unknown event mask flag(s) from BRUTUS : %X", event_mask);
		BRUTUS_LOG_BUG(msg);
	}

	return retval;
}


static inline ::BRUTUS::BDEFINE table_status_mapi_to_brutus(const ULONG TableStatus)
{
	switch (TableStatus) {
	case TBLSTAT_COMPLETE :
		return ::BRUTUS::BRUTUS_TBLSTAT_COMPLETE;
	case TBLSTAT_QCHANGED :
		return ::BRUTUS::BRUTUS_TBLSTAT_QCHANGED;
	case TBLSTAT_SORTING :
		return ::BRUTUS::BRUTUS_TBLSTAT_SORTING;
	case TBLSTAT_SORT_ERROR :
		return ::BRUTUS::BRUTUS_TBLSTAT_SORT_ERROR;
	case TBLSTAT_SETTING_COLS :
		return ::BRUTUS::BRUTUS_TBLSTAT_SETTING_COLS;
	case TBLSTAT_SETCOL_ERROR :
		return ::BRUTUS::BRUTUS_TBLSTAT_SETCOL_ERROR;
	case TBLSTAT_RESTRICTING :
		return ::BRUTUS::BRUTUS_TBLSTAT_RESTRICTING;
	case TBLSTAT_RESTRICT_ERROR :
		return ::BRUTUS::BRUTUS_TBLSTAT_RESTRICT_ERROR;
	}

	{
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "Unknown table status from MAPI : %X", TableStatus);
		BRUTUS_LOG_BUG(msg);
	}
	return 0;
}


static inline ::BRUTUS::BDEFINE table_type_mapi_to_brutus(const ULONG TableType)
{
	switch (TableType) {
	case TBLTYPE_SNAPSHOT :
		return ::BRUTUS::BRUTUS_TBLTYPE_SNAPSHOT;
	case TBLTYPE_KEYSET :
		return ::BRUTUS::BRUTUS_TBLTYPE_KEYSET;
	case TBLTYPE_DYNAMIC :
		return ::BRUTUS::BRUTUS_TBLTYPE_DYNAMIC;
	}

	{
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "Unknown table type from MAPI : %X", TableType);
		BRUTUS_LOG_BUG(msg);
	}
	return 0;
}


BRUTUS_IMAPITable_i::BRUTUS_IMAPITable_i(LPMAPITABLE MAPITable,
					 LPMAPISESSION MAPISession,
					 ::PortableServer::POA_ptr Poa)
	: mapi_table_(MAPITable),
	  mapi_session_(MAPISession),
	  poa_(::PortableServer::POA::_duplicate(Poa))
{
	mapi_session_->AddRef();
	advise_sinks_.Clear();
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::GetLastError(::BRUTUS::BRESULT ReturnCode,
						    ::BRUTUS::BDEFINE ulFlags,
						    ::BRUTUS::MAPIERROR_out lppMAPIError)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::GetLastError()");

	::BRUTUS::MAPIERROR_var error;
	try {
		error = new ::BRUTUS::MAPIERROR;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	error->ulVersion = (::CORBA::ULong)0;
	error->ulLowLevelError = (::CORBA::ULong)0;
	error->ulContext = (::CORBA::ULong)0;

	HRESULT hr;
	if (!bresult_to_hresult(ReturnCode, hr)) {
		lppMAPIError = error._retn();
		BRUTUS_LOG_BUG("Could not convert BRESULT into HRESULT");
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	LPMAPIERROR mapi_error = NULL;
	unsigned long flags = native_flags(ulFlags);

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->GetLastError(hr, flags, &mapi_error);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		MAPIFreeBuffer(mapi_error);
		lppMAPIError = error._retn();
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	if (!mapi_error)
		BRUTUS_LOG_INF("No applicable error information from MAPI");

	mapierror_mapi_to_brutus(mapi_error, error, true);

	lppMAPIError = error._retn();

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::GetLastError()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::Advise(::BRUTUS::BDEFINE ulEventMask,
					      ::BRUTUS::IMAPIAdviseSink_ptr lpAdviseSink,
					      ::CORBA::ULong_out lpulConnection)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::Advise()");

	lpulConnection = (::CORBA::ULong)0;

	if (::CORBA::is_nil(lpAdviseSink)) {
		BRUTUS_LOG_BUG("lpAdviseSink is NIL");
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	unsigned long event_mask = native_eventmask(ulEventMask);

	CMAPIAdviseSink *sink;
	try {
		sink = new CMAPIAdviseSink(lpAdviseSink, mapi_session_, poa_.in());
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	unsigned long adv_con;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->Advise(event_mask, sink, &adv_con);

	}
	sink->Release(); // MAPI::UnAdvise will call Release()

	lpulConnection = (::CORBA::ULong)adv_con;
	if (hr == S_OK)
		advise_sinks_.AddSink(lpulConnection);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::Advise()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::Unadvise(::CORBA::ULong ulConnection)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::Unadvise()");

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->Unadvise(ulConnection);
	}
	advise_sinks_.RemoveSink(ulConnection);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::Unadvise()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::GetStatus(::CORBA::ULong_out lpulTableStatus,
						 ::CORBA::ULong_out lpulTableType)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::GetStatus()");

	unsigned long table_status;
	unsigned long table_type;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->GetStatus(&table_status, &table_type);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	lpulTableStatus = table_status_mapi_to_brutus(table_status);
	lpulTableType = table_type_mapi_to_brutus(table_type);

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::GetStatus()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::SetColumns(const ::BRUTUS::SPropTagArray& lpPropTagArray,
						  ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::SetColumns()");

	SPropTagArray *tags = NULL;
	proptag_array_brutus_to_mapi(&lpPropTagArray, 0, tags);

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->SetColumns(tags, flags);
	}
	MAPIFreeBuffer(tags);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::SetColumns()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IMAPITable_i::QueryColumns(::BRUTUS::BDEFINE ulFlags,
						    ::BRUTUS::SPropTagArray_out lpPropTagArray)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::QueryColumns()");

	LPSPropTagArray tags = NULL;
	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->QueryColumns(flags, &tags);
	}

	if (!proptag_array_mapi_to_brutus(tags, lpPropTagArray, true)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::QueryColumns()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::GetRowCount(::BRUTUS::BDEFINE ulFlags,
						   ::CORBA::ULong_out lpulCount)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::GetRowCount()");

	lpulCount = (::CORBA::ULong)0;
	unsigned long row_count;
	unsigned long flags = (unsigned long)ulFlags;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->GetRowCount(flags, &row_count);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	lpulCount = (::CORBA::ULong)row_count;

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::GetRowCount()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::SeekRow(::CORBA::ULong bkOrigin,
					       ::CORBA::Long lRowCount,
					       ::CORBA::Long_out lplRowsSought)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::SeekRow()");

	BOOKMARK mark = native_bookmark(bkOrigin);
	long row_count = (long)lRowCount;
	long rows_sought;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->SeekRow(mark, row_count, &rows_sought);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	lplRowsSought = (::CORBA::Long)rows_sought;

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::SeekRow()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::SeekRowApprox(::CORBA::ULong ulNumerator,
						     ::CORBA::ULong ulDenominator)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::SeekRowApprox()");

	unsigned long num = (unsigned long)ulNumerator;
	unsigned long denum = (unsigned long)ulDenominator;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->SeekRowApprox(num, denum);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::SeekRowApprox()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::QueryPosition(::CORBA::ULong_out lpulRow,
						     ::CORBA::ULong_out lpulNumerator,
						     ::CORBA::ULong_out lpulDenominator)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::QueryPosition()");

	unsigned long row = (unsigned long)lpulRow;
	unsigned long num = (unsigned long)lpulNumerator;
	unsigned long denum = (unsigned long)lpulDenominator;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->QueryPosition(&row, &num, &denum);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	lpulRow = (::CORBA::ULong)row;
	lpulNumerator = (::CORBA::ULong)num;
	lpulDenominator = (::CORBA::ULong)denum;

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::QueryPosition()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::FindRow(::BRUTUS::SRestrictionContainer_ptr lpRestriction,
					       ::CORBA::ULong bkOrigin,
					       ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::FindRow()");

	SRestriction *mapi_res = NULL;
	if (!::CORBA::is_nil(lpRestriction)) {
		::BRUTUS::SRestriction_var brutus_res;
		try {
			brutus_res = lpRestriction->content();
		}
		catch (const ::CORBA::Exception &e) {
			BRUTUS_LOG_CRITICAL(e._info().c_str());
			return ::BRUTUS::BRUTUS_EXCEPTION_FROM_CLIENT;
		}
		if (!srestriction_brutus_to_mapi(brutus_res.in(), 0, mapi_res)) {
			if (mapi_res)
				MAPIFreeBuffer(mapi_res);

			BRUTUS_LOG_BUG("Conversion error");
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	}
	BOOKMARK mark = native_bookmark(bkOrigin);
	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->FindRow(mapi_res, mark, flags);
	}
	MAPIFreeBuffer(mapi_res);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::FindRow()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::Restrict(::BRUTUS::SRestrictionContainer_ptr lpRestriction,
						::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::Restrict()");

	SRestriction *mapi_res = NULL;
	if (!::CORBA::is_nil(lpRestriction)) {
		::BRUTUS::SRestriction_var brutus_res;
		try {
			brutus_res = lpRestriction->content();
		}
		catch (const ::CORBA::Exception &e) {
			BRUTUS_LOG_CRITICAL(e._info().c_str());
			return ::BRUTUS::BRUTUS_EXCEPTION_FROM_CLIENT;
		}
		if (!srestriction_brutus_to_mapi(brutus_res.in(), 0, mapi_res)) {
			if (mapi_res)
				MAPIFreeBuffer(mapi_res);

			BRUTUS_LOG_BUG("Conversion error");
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	}

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->Restrict(mapi_res, flags);
	}
	MAPIFreeBuffer(mapi_res);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::Restrict()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::CreateBookmark(::CORBA::ULong_out lpbkPosition)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::CreateBookmark()");

	BOOKMARK mark;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->CreateBookmark(&mark);
	}
	lpbkPosition = (::CORBA::ULong)mark;

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::CreateBookmark()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::FreeBookmark(::CORBA::ULong bkPosition)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::FreeBookmark()");

	BOOKMARK mark = (BOOKMARK)bkPosition;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->FreeBookmark(mark);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::FreeBookmark()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::SortTable(const ::BRUTUS::SSortOrderSet & lpSortCriteria,
						 ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::SortTable()");

	if (!lpSortCriteria.aSort.length())
		return ::BRUTUS::BRUTUS_S_OK;

	SSortOrderSet *mapi_set = NULL;
	if (!sort_order_set_brutus_to_mapi(&lpSortCriteria, 0, mapi_set)) {
		if (mapi_set)
			MAPIFreeBuffer(mapi_set);

		BRUTUS_LOG_BUG("Conversion error");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->SortTable(mapi_set, flags);
	}
	MAPIFreeBuffer(mapi_set);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::SortTable()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::QuerySortOrder(::BRUTUS::SSortOrderSet_out lppSortCriteria)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::QuerySortOrder()");

	SSortOrderSet *mapi_set = NULL;
	::BRUTUS::SSortOrderSet_var brutus_set;
	try {
		brutus_set = new ::BRUTUS::SSortOrderSet;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_set->cCategories = 0;
	brutus_set->cExpanded = 0;
	brutus_set->aSort.length(0);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->QuerySortOrder(&mapi_set);
	}
	if (!sort_order_set_mapi_to_brutus(mapi_set, brutus_set.inout(), true)) {
		lppSortCriteria = brutus_set._retn();
		BRUTUS_LOG_BUG("Conversion error");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppSortCriteria = brutus_set._retn();

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::QuerySortOrder()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::QueryRows(::CORBA::Long lRowCount,
						 ::BRUTUS::BDEFINE ulFlags,
						 ::BRUTUS::SRowSet_out lppRows)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::QueryRows()");

	SRowSet *mapi_set = NULL;
	::BRUTUS::SRowSet_var brutus_set;
	try {
		brutus_set = new ::BRUTUS::SRowSet;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_set->length(0);
	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->QueryRows(lRowCount, flags, &mapi_set);
	}
	if (!srow_set_mapi_to_brutus(mapi_set, brutus_set, mapi_session_, true, poa_)) {
		lppRows = brutus_set._retn();
		BRUTUS_LOG_BUG("Conversion error");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppRows = brutus_set._retn();

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::QueryRows()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::Abort(void)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::Abort()");

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->Abort();
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::Abort()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::ExpandRow(const ::BRUTUS::seq_octet & pbInstanceKey,
						 ::CORBA::ULong ulRowCount,
						 ::BRUTUS::BDEFINE ulFlags,
						 ::BRUTUS::SRowSet_out lppRows,
						 ::CORBA::ULong_out lpulMoreRows)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::ExpandRow()");

	::BRUTUS::SRowSet_var brutus_set;
	try {
		brutus_set = new ::BRUTUS::SRowSet;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_set->length(0);

	if (sizeof(BYTE) != sizeof(::CORBA::Octet)) {
		BRUTUS_LOG_BUG("sizeof(BYTE) != sizeof(::CORBA::Octet)");
		lpulMoreRows = 0;
		lppRows = brutus_set._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BYTE *mapi_key = NULL;
	unsigned long count = pbInstanceKey.length();
	if (count) {
		if (S_OK != MAPIAllocateBuffer(count * sizeof(BYTE), (void**)&mapi_key)) {
			BRUTUS_LOG_ERR("No memory");
			throw ::CORBA::NO_MEMORY();
		}
		for (unsigned long n = 0; n < count; n++)
			mapi_key[n] = (BYTE)pbInstanceKey[n];
	}

	SRowSet *mapi_set = NULL;
	unsigned long flags = (unsigned long)ulFlags;
	unsigned long row_count = (unsigned long)ulRowCount;
	unsigned long more_rows;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->ExpandRow(count, mapi_key, row_count, flags, &mapi_set, &more_rows);
	}
	MAPIFreeBuffer(mapi_key);

	if (mapi_set && !srow_set_mapi_to_brutus(mapi_set, brutus_set, mapi_session_, true, poa_)) {
		BRUTUS_LOG_BUG("Conversion error");
		lpulMoreRows = 0;
		lppRows = brutus_set._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppRows = brutus_set._retn();
	lpulMoreRows = (::CORBA::ULong)more_rows;

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::ExpandRow()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::CollapseRow(const ::BRUTUS::seq_octet & pbInstanceKey,
						   ::BRUTUS::BDEFINE ulFlags,
						   ::CORBA::ULong_out lpulRowCount)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::CollapseRow()");

	if (sizeof(BYTE) != sizeof(::CORBA::Octet)) {
		BRUTUS_LOG_BUG("sizeof(BYTE) != sizeof(::CORBA::Octet)");
		lpulRowCount = 0;
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BYTE *mapi_key = NULL;
	unsigned long count = pbInstanceKey.length();
	if (count) {
		if (S_OK != MAPIAllocateBuffer(count * sizeof(BYTE), (void**)&mapi_key)) {
			BRUTUS_LOG_ERR("No memory");
			throw ::CORBA::NO_MEMORY();
		}
		for (unsigned long n = 0; n < count; n++)
			mapi_key[n] = (BYTE)pbInstanceKey[n];
	}

	unsigned long flags = (unsigned long)ulFlags;
	unsigned long row_count;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->CollapseRow(count, mapi_key, flags, &row_count);
	}
	MAPIFreeBuffer(mapi_key);
	lpulRowCount = (::CORBA::ULong)row_count;

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::CollapseRow()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::WaitForCompletion(::BRUTUS::BDEFINE ulFlags,
							 ::CORBA::ULong ulTimeout,
							 ::CORBA::ULong & lpulTableStatus)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::WaitForCompletion()");

	unsigned long flags = (unsigned long)ulFlags;
	unsigned long time_out = (unsigned long)ulTimeout;
	unsigned long table_status;

	HRESULT hr; // TBLSTAT_COMPLETE table_status_mapi_to_brutus
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->WaitForCompletion(flags, time_out, &table_status);
	}
	if (HR_FAILED(hr))
		lpulTableStatus = ::BRUTUS::BRUTUS_TBLSTAT_UNKNOWN;
	else
		lpulTableStatus = table_status_mapi_to_brutus(table_status);

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::WaitForCompletion()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::GetCollapseState(::BRUTUS::BDEFINE ulFlags,
							const ::BRUTUS::seq_octet& lpbInstanceKey,
							::BRUTUS::seq_octet_out lppbCollapseState)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::GetCollapseState()");

	::BRUTUS::seq_octet_var brutus_collapse_state;
	try {
		brutus_collapse_state = new ::BRUTUS::seq_octet;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_collapse_state->length(0);

	if (sizeof(BYTE) != sizeof(::CORBA::Octet)) {
		BRUTUS_LOG_BUG("sizeof(BYTE) != sizeof(::CORBA::Octet)");
		lppbCollapseState = brutus_collapse_state._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BYTE *mapi_key = NULL;;
	unsigned long count = lpbInstanceKey.length();
	if (count) {
		if (S_OK != MAPIAllocateBuffer(count * sizeof(BYTE), (void**)&mapi_key)) {
			BRUTUS_LOG_ERR("No memory");
			throw ::CORBA::NO_MEMORY();
		}
		for (unsigned long n = 0; n < count; n++)
			mapi_key[n] = (BYTE)lpbInstanceKey[n];
	}

	unsigned long flags = (unsigned long)ulFlags;
	unsigned long collapse_state_count;
	BYTE *mapi_collapse_state;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->GetCollapseState(flags, count, mapi_key, &collapse_state_count, &mapi_collapse_state);
	}
	MAPIFreeBuffer(mapi_key);

	brutus_collapse_state->length(collapse_state_count);
	for (unsigned long k = 0; k < count; k++)
		brutus_collapse_state[k] = (::CORBA::Octet)mapi_collapse_state[k];

	lppbCollapseState = brutus_collapse_state._retn();

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::GetCollapseState()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IMAPITable_i::SetCollapseState(::BRUTUS::BDEFINE ulFlags,
							const ::BRUTUS::seq_octet & pbCollapseState,
							::CORBA::ULong_out lpbkLocation)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::SetCollapseState()");

	if (sizeof(BYTE) != sizeof(::CORBA::Octet)) {
		BRUTUS_LOG_BUG("sizeof(BYTE) != sizeof(::CORBA::Octet)");
		lpbkLocation = 0;
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BYTE *mapi_state = NULL;;
	unsigned long count = pbCollapseState.length();
	if (count) {
		if (S_OK != MAPIAllocateBuffer(count * sizeof(BYTE), (void**)&mapi_state)) {
			BRUTUS_LOG_ERR("No memory");
			throw ::CORBA::NO_MEMORY();
		}
		for (unsigned long n = 0; n < count; n++)
			mapi_state[n] = (BYTE)pbCollapseState[n];
	}

	unsigned long location;
	unsigned long flags = (unsigned long)ulFlags;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = mapi_table_->SetCollapseState(flags, count, mapi_state, &location);
	}
	MAPIFreeBuffer(mapi_state);

	lpbkLocation = (unsigned long)location;

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::SetCollapseState()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IMAPITable_i::GetLocalPointer(::CORBA::ULongLong_out Pointer)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::GetLocalPointer()");

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		mapi_table_->AddRef();
		Pointer = (::CORBA::ULongLong)mapi_table_;
	}

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::GetLocalPointer()");
	return ::BRUTUS::BRUTUS_S_OK;
}

::BRUTUS::BRESULT BRUTUS_IMAPITable_i::QueryInterface(const char *iid,
						      ::BRUTUS::IUnknown_out ppvObject)
{
	ppvObject = ::BRUTUS::IUnknown::_nil();

	GUID mapi_iid;
	if (!guid_brutus_to_mapi_no_alloc(iid, &mapi_iid)) {
		BRUTUS_LOG_BUG("Could not convert ::BRUTUS::GUID into MAPI GUID");
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	HRESULT hr;
	void *object = NULL;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr =  mapi_table_->QueryInterface(mapi_iid, &object);
	}

	::BRUTUS::BRESULT br;
	if (!hresult_to_bresult(hr, br)) {
		BRUTUS_LOG_BUG("Could not convert HRESULT into BRESULT");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	if (hr == S_OK) {
		if (!create_brutus_object(iid, (LPUNKNOWN)object, poa_, ppvObject, mapi_session_)) {
			BRUTUS_LOG_BUG("Could not create brutus object.");
			((LPUNKNOWN)object)->Release();
			return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
		}
	}

	return br;
}

void BRUTUS_IMAPITable_i::Destroy(::CORBA::ULong InstanceID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPITable_i::Destroy()");

	::PortableServer::ObjectId_var oid;
	oid = poa_->servant_to_id(this);

	poa_->deactivate_object(oid);

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPITable_i::Destroy()");
}
