/* -*- 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 "IExchangeManageStoreS_impl.h"
#include "obj_utils.h"
#include "conv_utils.h"
#include "IMAPITableS_impl.h"

#include "templates.i"

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

	if (!Flags)
		return 0;

	if (flags & ::BRUTUS::BRUTUS_OPENSTORE_USE_ADMIN_PRIVILEGE) {
		retval |= OPENSTORE_USE_ADMIN_PRIVILEGE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_OPENSTORE_USE_ADMIN_PRIVILEGE);
	}
	if (flags & ::BRUTUS::BRUTUS_OPENSTORE_PUBLIC) {
		retval |= OPENSTORE_PUBLIC;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_OPENSTORE_PUBLIC);
	}
	if (flags & ::BRUTUS::BRUTUS_OPENSTORE_HOME_LOGON) {
		retval |= OPENSTORE_HOME_LOGON;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_OPENSTORE_HOME_LOGON);
	}
	if (flags & ::BRUTUS::BRUTUS_OPENSTORE_TAKE_OWNERSHIP) {
		retval |= OPENSTORE_TAKE_OWNERSHIP;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_OPENSTORE_TAKE_OWNERSHIP);
	}
	if (flags & ::BRUTUS::BRUTUS_OPENSTORE_OVERRIDE_HOME_MDB) {
		retval |= OPENSTORE_OVERRIDE_HOME_MDB;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_OPENSTORE_OVERRIDE_HOME_MDB);
	}
	if (flags & ::BRUTUS::BRUTUS_OPENSTORE_TRANSPORT) {
		retval |= OPENSTORE_TRANSPORT;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_OPENSTORE_TRANSPORT);
	}
	if (flags & ::BRUTUS::BRUTUS_OPENSTORE_REMOTE_TRANSPORT) {
		retval |= OPENSTORE_REMOTE_TRANSPORT;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_OPENSTORE_REMOTE_TRANSPORT);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_UNICODE) {
		retval |= MAPI_UNICODE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_UNICODE);
	}

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

	return retval;
}

BRUTUS_IExchangeManageStore_i::BRUTUS_IExchangeManageStore_i(LPEXCHANGEMANAGESTORE ExchangeManageStore,
							     LPMAPISESSION MAPISession,
							     ::PortableServer::POA_ptr Poa)
	: exchange_manage_store_(ExchangeManageStore),
	  mapi_session_(MAPISession),
	  poa_(::PortableServer::POA::_duplicate(Poa))
{
	mapi_session_->AddRef();
}

::BRUTUS::BRESULT BRUTUS_IExchangeManageStore_i::CreateStoreEntryID(const char * lpszMsgStoreDN,
								    const char * lpszMailboxDN,
								    ::BRUTUS::BDEFINE ulFlags,
								    ::BRUTUS::ENTRYID_out lppEntryID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IExchangeManageStore_i::CreateStoreEntryID()");

	ULONG flags = native_flags(ulFlags);

	unsigned long count = 0;
	ENTRYID *entry_id = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = exchange_manage_store_->CreateStoreEntryID((char*)lpszMsgStoreDN,
								(char*)lpszMailboxDN,
								flags,
								&count,
								&entry_id);
	}

	::BRUTUS::ENTRYID_var out_entry_id;
	if (!entryid_mapi_to_brutus(count, entry_id, out_entry_id.out(), true)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	lppEntryID = out_entry_id._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_IExchangeManageStore_i::CreateStoreEntryID()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IExchangeManageStore_i::EntryIDFromSourceKey(const ::BRUTUS::seq_octet & lpFolderSourceKey,
								      const ::BRUTUS::seq_octet & lpMessageSourceKey,
								      ::BRUTUS::ENTRYID_out lppEntryID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IExchangeManageStore_i::EntryIDFromSourceKey()");

	void *mapi_lpFolderSourceKey = NULL;
	if (!seq_octet_brutus_to_mapi(lpFolderSourceKey, NULL, mapi_lpFolderSourceKey)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	void *mapi_lpMessageSourceKey = NULL;
	if (!seq_octet_brutus_to_mapi(lpMessageSourceKey, mapi_lpFolderSourceKey, mapi_lpMessageSourceKey)) {
		BRUTUS_LOG_ERR("No memory");
		MAPIFreeBuffer(mapi_lpFolderSourceKey);
		throw ::CORBA::NO_MEMORY();
	}

	unsigned long count = 0;
	ENTRYID *entry_id = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = exchange_manage_store_->EntryIDFromSourceKey(lpFolderSourceKey.length(),
								  (BYTE*)mapi_lpFolderSourceKey,
								  lpMessageSourceKey.length(),
								  (BYTE*)mapi_lpMessageSourceKey,
								  &count,
								  &entry_id);
	}
	MAPIFreeBuffer(mapi_lpFolderSourceKey);

	::BRUTUS::ENTRYID_var out_entry_id;
	if (!entryid_mapi_to_brutus(count, entry_id, out_entry_id.out(), true)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	lppEntryID = out_entry_id._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_IExchangeManageStore_i::EntryIDFromSourceKey()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IExchangeManageStore_i::GetRights(const ::BRUTUS::ENTRYID & lpUserEntryID,
							   const ::BRUTUS::ENTRYID & lpEntryID,
							   ::CORBA::ULong_out lpulRights)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IExchangeManageStore_i::GetRights()");

	ENTRYID *user_entry_id = NULL;
	unsigned long user_entry_id_size;
	if (!entryid_brutus_to_mapi(&lpUserEntryID, 0, user_entry_id_size, user_entry_id)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	ENTRYID *entry_id = NULL;
	unsigned long entry_id_size;
	if (!entryid_brutus_to_mapi(&lpEntryID, user_entry_id, entry_id_size, entry_id)) {
		BRUTUS_LOG_ERR("No memory");
		MAPIFreeBuffer(user_entry_id);
		throw ::CORBA::NO_MEMORY();
	}

	ULONG rights = 0;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = exchange_manage_store_->GetRights(user_entry_id_size,
						       user_entry_id,
						       entry_id_size,
						       entry_id,
						       &rights);
	}
	MAPIFreeBuffer(user_entry_id);

	lpulRights = rights;

	::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_IExchangeManageStore_i::GetRights()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IExchangeManageStore_i::GetMailboxTable(const char * lpszServerName,
								 ::BRUTUS::IMAPITable_out lppTable,
								 ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IExchangeManageStore_i::GetMailboxTable()");

	lppTable = ::BRUTUS::IMAPITable::_nil();
	LPMAPITABLE mapi_table = NULL;

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = exchange_manage_store_->GetMailboxTable((char*)lpszServerName,
							     &mapi_table,
							     flags);
	}

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

	lppTable = create_object<BRUTUS_IMAPITable_i, ::BRUTUS::IMAPITable, LPMAPITABLE>
		(mapi_table, poa_.in(), mapi_session_);

	BRUTUS_LOG_INF("Leaving BRUTUS_IExchangeManageStore_i::GetMailboxTable()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IExchangeManageStore_i::GetPublicFolderTable(const char * lpszServerName,
								      ::BRUTUS::IMAPITable_out lppTable,
								      ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IExchangeManageStore_i::GetPublicFolderTable()");

	lppTable = ::BRUTUS::IMAPITable::_nil();
	LPMAPITABLE mapi_table = NULL;

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = exchange_manage_store_->GetPublicFolderTable((char*)lpszServerName,
								  &mapi_table,
								  flags);
	}

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

	lppTable = create_object<BRUTUS_IMAPITable_i, ::BRUTUS::IMAPITable, LPMAPITABLE>
		(mapi_table, poa_.in(), mapi_session_);

	BRUTUS_LOG_INF("Leaving BRUTUS_IExchangeManageStore_i::GetPublicFolderTable()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IExchangeManageStore_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 = exchange_manage_store_->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_IExchangeManageStore_i::Destroy(::CORBA::ULong InstanceID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IExchangeManageStore_i::Destroy()");

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

	poa_->deactivate_object(oid);

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