/* -*- 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 "IProfAdminS_impl.h"
#include "obj_utils.h"
#include "conv_utils.h"
#include "IMAPITableS_impl.h"
#include "IMsgServiceAdminS_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_MAPI_UNICODE) {
		retval |= MAPI_UNICODE;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_UNICODE);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_DEFAULT_SERVICES) {
		retval |= MAPI_DEFAULT_SERVICES;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_DEFAULT_SERVICES);
	}
	if (flags & ::BRUTUS::BRUTUS_MAPI_DIALOG) {
		retval |= MAPI_DIALOG;
		FLAGS_OFF(::BRUTUS::BDEFINE, flags, ::BRUTUS::BRUTUS_MAPI_DIALOG);
	}

	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_IProfAdmin_i::BRUTUS_IProfAdmin_i(LPPROFADMIN ProfAdmin,
					 LPMAPISESSION MAPISession,
					 ::PortableServer::POA_ptr Poa)
	: prof_admin_(ProfAdmin),
	  mapi_session_(MAPISession),
	  poa_(::PortableServer::POA::_duplicate(Poa))
{
	if (mapi_session_)
		mapi_session_->AddRef();
}


::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::GetLastError(::BRUTUS::BRESULT ReturnCode,
						    ::BRUTUS::BDEFINE ulFlags,
						    ::BRUTUS::MAPIERROR_out lppMAPIError)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_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 = prof_admin_->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_IProfAdmin_i::GetLastError()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::GetProfileTable(::BRUTUS::BDEFINE ulFlags,
						       ::BRUTUS::IMAPITable_out lppTable)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_i::GetProfileTable()");

	lppTable = ::BRUTUS::IMAPITable::_nil();
	if (!mapi_session_)
		return ::BRUTUS::BRUTUS_NO_MAPI_SESSION;

	unsigned long flags = native_flags(ulFlags);
	LPMAPITABLE mapi_table = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = prof_admin_->GetProfileTable(flags,
						  &mapi_table);
	}

	::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)
		lppTable = create_object<BRUTUS_IMAPITable_i, ::BRUTUS::IMAPITable, LPMAPITABLE>
			(mapi_table, poa_.in(), mapi_session_);

	BRUTUS_LOG_INF("Leaving BRUTUS_IProfAdmin_i::GetProfileTable()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::CreateProfile(const char * lpszProfileName,
						     const char * lpszPassword,
						     ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_i::CreateProfile()");

	unsigned long flags = native_flags(ulFlags);
	FLAGS_OFF(ULONG, flags, MAPI_DIALOG);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = prof_admin_->CreateProfile((char*)lpszProfileName,
						(char*)lpszPassword,
						0,
						flags);
	}

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


::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::DeleteProfile(const char * lpszProfileName,
						     ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_i::DeleteProfile()");

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = prof_admin_->DeleteProfile((char*)lpszProfileName,
						flags);
	}

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

::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::ChangeProfilePassword(const char * lpszProfileName,
							     const char * lpszOldPassword,
							     const char * lpszNewPassword,
							     ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_i::ChangeProfilePassword()");

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = prof_admin_->ChangeProfilePassword((char*)lpszProfileName,
							(char*)lpszOldPassword,
							(char*)lpszNewPassword,
							flags);
	}

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

::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::CopyProfile(const char * lpszOldProfileName,
						   const char * lpszOldPassword,
						   const char * lpszNewProfileName,
						   ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_i::CopyProfile()");

	unsigned long flags = native_flags(ulFlags);
	FLAGS_OFF(ULONG, flags, MAPI_DIALOG);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = prof_admin_->CopyProfile((char*)lpszOldProfileName,
					      (char*)lpszOldPassword,
					      (char*)lpszNewProfileName,
					      0,
					      flags);
	}

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

::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::RenameProfile(const char * lpszOldProfileName,
						     const char * lpszOldPassword,
						     const char * lpszNewProfileName,
						     ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_i::RenameProfile()");

	unsigned long flags = native_flags(ulFlags);
	FLAGS_OFF(ULONG, flags, MAPI_DIALOG);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = prof_admin_->RenameProfile((char*)lpszOldProfileName,
						(char*)lpszOldPassword,
						(char*)lpszNewProfileName,
						0,
						flags);
	}

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

::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::SetDefaultProfile(const char * lpszProfileName,
							 ::BRUTUS::BDEFINE ulFlags)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_i::SetDefaultProfile()");

	unsigned long flags = native_flags(ulFlags);

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = prof_admin_->SetDefaultProfile((char*)lpszProfileName,
						    flags);
	}

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

::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::AdminServices(const char * lpszProfileName,
						     const char * lpszPassword,
						     ::BRUTUS::BDEFINE ulFlags,
						     ::BRUTUS::IMsgServiceAdmin_out lppServiceAdmin)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_i::AdminServices()");

	lppServiceAdmin = ::BRUTUS::IMsgServiceAdmin::_nil();
	if (!mapi_session_)
		return ::BRUTUS::BRUTUS_NO_MAPI_SESSION;
	LPSERVICEADMIN service_admin = NULL;

	unsigned long flags = native_flags(ulFlags);
	FLAGS_OFF(ULONG, flags, MAPI_DIALOG);


	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = prof_admin_->AdminServices((char*)lpszProfileName,
						(char*)lpszPassword,
						0,
						flags,
						&service_admin);
	}

	::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)
		lppServiceAdmin = create_object<BRUTUS_IMsgServiceAdmin_i, ::BRUTUS::IMsgServiceAdmin, LPSERVICEADMIN>
			(service_admin, poa_.in(), mapi_session_);

	BRUTUS_LOG_INF("Leaving BRUTUS_IProfAdmin_i::AdminServices()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IProfAdmin_i::QueryInterface(const char *iid,
						      ::BRUTUS::IUnknown_out ppvObject)
{

	ppvObject = ::BRUTUS::IUnknown::_nil();
	if (!mapi_session_)
		return ::BRUTUS::BRUTUS_NO_MAPI_SESSION;

	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 = prof_admin_->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_IProfAdmin_i::Destroy(::CORBA::ULong InstanceID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IProfAdmin_i::Destroy()");

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

	poa_->deactivate_object(oid);

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