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

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) {
		char msg[128] = {0};
		sprintf_s(msg, sizeof(msg), "Unknown flag(s) from BRUTUS : %X", flags);
		BRUTUS_LOG_BUG(msg);
	}

	return retval;
}


BRUTUS_IMAPIFormInfo_i::BRUTUS_IMAPIFormInfo_i(LPMAPIFORMINFO FormInfo,
					       LPMAPISESSION MAPISession,
					       ::PortableServer::POA_ptr Poa)
	: form_info_(FormInfo),
	  mapi_session_(MAPISession),
	  poa_(::PortableServer::POA::_duplicate(Poa))
{
	mapi_session_->AddRef();
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::GetLastError (
	::BRUTUS::BRESULT bResult,
	::BRUTUS::BDEFINE ulFlags,
	::BRUTUS::MAPIERROR_out lppMAPIError)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::SaveChanges (
	::BRUTUS::BDEFINE ulFlags)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::GetProps (
	const ::BRUTUS::SPropTagArray & lpPropTagArray,
	::BRUTUS::BDEFINE ulFlags,
	::BRUTUS::seq_SPropValue_out lppPropArray)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::GetPropList (
	::BRUTUS::BDEFINE ulFlags,
	::BRUTUS::SPropTagArray_out lppPropTagArray)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::OpenProperty (
	::CORBA::ULong ulPropTag,
	const char * lpiid,
	::CORBA::ULong ulInterfaceOptions,
	::BRUTUS::BDEFINE ulFlags,
	::BRUTUS::IUnknown_out lppUnk)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::SetProps (
	const ::BRUTUS::seq_SPropValue & lpPropArray,
	::CORBA::Boolean ProblemInfo,
	::BRUTUS::SPropProblemArray_out lppProblems)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::DeleteProps (
	const ::BRUTUS::SPropTagArray & lpPropTagArray,
	::CORBA::Boolean ProblemInfo,
	::BRUTUS::SPropProblemArray_out lppProblems)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::CopyTo (
	const ::BRUTUS::seq_GUID & rgiidExclude,
	const ::BRUTUS::SPropTagArray & lpExcludeProps,
	::BRUTUS::IMAPIProgress_ptr lpProgress,
	const char * lpInterface,
	const ::BRUTUS::ENTRYID & lpDestObj,
	::BRUTUS::BDEFINE ulFlags,
	::CORBA::Boolean ProblemInfo,
	::BRUTUS::SPropProblemArray_out lppProblems)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::CopyProps (
	const ::BRUTUS::SPropTagArray & lpIncludeProps,
	::BRUTUS::IMAPIProgress_ptr lpProgress,
	const char * lpInterface,
	const ::BRUTUS::ENTRYID & lpDestObj,
	::BRUTUS::BDEFINE ulFlags,
	::CORBA::Boolean ProblemInfo,
	::BRUTUS::SPropProblemArray_out lppProblems)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::GetNamesFromIDs (
	::BRUTUS::SPropTagArray & lppPropTags,
	const char * lpPropSetGuid,
	::BRUTUS::BDEFINE ulFlags,
	::BRUTUS::seq_MAPINAMEID_out lpppPropNames)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_i::GetIDsFromNames (
	const ::BRUTUS::seq_MAPINAMEID & lppPropNames,
	::BRUTUS::BDEFINE ulFlags,
	::BRUTUS::SPropTagArray_out lppPropTags)

{
	//Add your implementation here
	return ::BRUTUS::BRUTUS_MAPI_E_NO_SUPPORT;
}

::BRUTUS::BRESULT
BRUTUS_IMAPIFormInfo_i::GetNamedProps(const char *PropSetGuid,
				      const ::BRUTUS::seq_NamedPropertyTag & lpPropTagArray,
				      ::BRUTUS::BDEFINE ulFlags,
				      ::BRUTUS::seq_SPropValue_out lppPropArray)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPIFormInfo_i::GetNamedProps()");

	unsigned long flags = 0;
	SPropValue *props = NULL;
	unsigned long count = 0;

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

	GUID mapi_propset_guid;
	if (!guid_brutus_to_mapi_no_alloc(PropSetGuid, &mapi_propset_guid, false)) {
		lppPropArray = brutus_props._retn();
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	::LPMAPINAMEID *mapi_names = NULL;
	if (S_OK != MAPIAllocateBuffer(lpPropTagArray.length()*sizeof(::MAPINAMEID*),
				       (void**)&mapi_names)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	ULONG n = 0;
	for (n = 0; n < lpPropTagArray.length(); n++) {
		if (S_OK != MAPIAllocateMore(sizeof(::MAPINAMEID),
					     mapi_names,
					     (void**)&(mapi_names[n]))) {
			BRUTUS_LOG_ERR("No memory");
			MAPIFreeBuffer(mapi_names);
			throw ::CORBA::NO_MEMORY();
		}
	}

	for (n = 0; n < lpPropTagArray.length(); n++) {
		mapi_names[n]->lpguid = (LPGUID)&mapi_propset_guid;
		switch (lpPropTagArray[n].name._d()) {
		case ::BRUTUS::BRUTUS_ID_INTEGER :
			mapi_names[n]->ulKind = MNID_ID;
			mapi_names[n]->Kind.lID = lpPropTagArray[n].name.int_name();
			break;
		case ::BRUTUS::BRUTUS_ID_STRING :
			mapi_names[n]->ulKind = MNID_STRING;
			mapi_names[n]->Kind.lpwstrName = ascii_to_unicode((void*)mapi_names, (const char*)lpPropTagArray[n].name.str_name());
			if (!mapi_names[n]->Kind.lpwstrName) {
				BRUTUS_LOG_ERR("No memory");
				MAPIFreeBuffer(mapi_names);
				throw ::CORBA::NO_MEMORY();
			}
			break;
		default :
			BRUTUS_LOG_BUG("Unknown name kind");
			lppPropArray = brutus_props._retn();
			MAPIFreeBuffer(mapi_names);
			return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
		}
	}

	SPropTagArray *tags = NULL;
	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = form_info_->GetIDsFromNames(lpPropTagArray.length(),
						 mapi_names,
						 MAPI_CREATE,
						 &tags);
	}
	MAPIFreeBuffer(mapi_names);

	if (!tags || (tags->cValues != lpPropTagArray.length())) {
		BRUTUS_LOG_ERR("Could not get PropTag ID from name");
		BRUTUS_EXIT;
	}
	if ((MAPI_W_ERRORS_RETURNED != hr) && (S_OK != hr))
		BRUTUS_EXIT;

	ULONG prop_id = 0;
	ULONG prop_type = 0;
	for (n = 0; n < tags->cValues; n++) {
		prop_id = PROP_ID(tags->aulPropTag[n]);
		prop_type = proptype_brutus_to_mapi(lpPropTagArray[n].type);
		tags->aulPropTag[n] = PROP_TAG(prop_type, prop_id);
	}

	flags = native_flags(ulFlags);
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = form_info_->GetProps(tags, flags, &count, &props);
	}

exit:
	MAPIFreeBuffer(tags);

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

	spropvalue_array_mapi_to_brutus(count, props, brutus_props, mapi_session_, true, poa_);
	lppPropArray = brutus_props._retn();

	BRUTUS_LOG_INF("Leaving BRUTUS_IMAPIFormInfo_i::GetNamedProps()");
	return br;
}

::BRUTUS::BRESULT
BRUTUS_IMAPIFormInfo_i::SetNamedProps(const char *PropSetGuid,
				      const ::BRUTUS::seq_NamedPropertyTag & lpPropTagArray,
				      const ::BRUTUS::seq_SPropValue & lpPropArray,
				      ::CORBA::Boolean ProblemInfo,
				      ::BRUTUS::SPropProblemArray_out lppProblems)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPIFormInfo_i::SetNamedProps()");

	unsigned long count = 0;
	SPropValue *mapi_props = NULL;
	::BRUTUS::SPropProblemArray_var brutus_problems;
	try {
		brutus_problems = new ::BRUTUS::SPropProblemArray;
	}
	catch (std::bad_alloc &) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}
	brutus_problems->length(0);

	if (!spropvalue_array_brutus_to_mapi(&lpPropArray, 0, count, mapi_props)) {
		MAPIFreeBuffer(mapi_props);
		BRUTUS_LOG_ERR("Could not convert Brutus SPropValue to MAPI SPropValue");
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	HRESULT hr;
	LPSPropProblemArray mapi_problems = NULL;

	GUID mapi_propset_guid;
	if (!guid_brutus_to_mapi_no_alloc(PropSetGuid, &mapi_propset_guid, false)) {
		MAPIFreeBuffer(mapi_props);
		MAPIFreeBuffer(mapi_problems);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
	}

	::LPMAPINAMEID *mapi_names = NULL;
	if (S_OK != MAPIAllocateBuffer(lpPropArray.length()*sizeof(::MAPINAMEID*),
				       (void**)&mapi_names)) {
		MAPIFreeBuffer(mapi_props);
		MAPIFreeBuffer(mapi_problems);
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	ULONG n = 0;
	for (n = 0; n < lpPropArray.length(); n++) {
		if (S_OK != MAPIAllocateMore(sizeof(::MAPINAMEID),
					     mapi_names,
					     (void**)&(mapi_names[n]))) {
			BRUTUS_LOG_ERR("No memory");
			MAPIFreeBuffer(mapi_names);
			throw ::CORBA::NO_MEMORY();
		}
	}

	for (n = 0; n < lpPropTagArray.length(); n++) {
		mapi_names[n]->lpguid = (LPGUID)&mapi_propset_guid;
		switch (lpPropTagArray[n].name._d()) {
		case ::BRUTUS::BRUTUS_ID_INTEGER :
			mapi_names[n]->ulKind = MNID_ID;
			mapi_names[n]->Kind.lID = lpPropTagArray[n].name.int_name();
			break;
		case ::BRUTUS::BRUTUS_ID_STRING :
			mapi_names[n]->ulKind = MNID_STRING;
			mapi_names[n]->Kind.lpwstrName = ascii_to_unicode((void*)mapi_names, (const char*)lpPropTagArray[n].name.str_name());
			if (!mapi_names[n]->Kind.lpwstrName) {
				BRUTUS_LOG_ERR("No memory");
				MAPIFreeBuffer(mapi_names);
				throw ::CORBA::NO_MEMORY();
			}
			break;
		default :
			BRUTUS_LOG_BUG("Unknown name kind");
			MAPIFreeBuffer(mapi_props);
			MAPIFreeBuffer(mapi_problems);
			MAPIFreeBuffer(mapi_names);
			lppProblems = brutus_problems._retn();
			return ::BRUTUS::BRUTUS_MAPI_E_INVALID_PARAMETER;
		}
	}

	SPropTagArray *tags = NULL;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = form_info_->GetIDsFromNames(lpPropTagArray.length(),
						 mapi_names,
						 MAPI_CREATE,
						 &tags);
	}
	MAPIFreeBuffer(mapi_names);

	if (!tags || (tags->cValues != lpPropArray.length())) {
		BRUTUS_LOG_ERR("Could not get PropTag ID from name");
		BRUTUS_EXIT;
	}
	if ((MAPI_W_ERRORS_RETURNED != hr) && (S_OK != hr))
		BRUTUS_EXIT;

	ULONG prop_id = 0;
	ULONG prop_type = 0;
	for (n = 0; n < tags->cValues; n++) {
		prop_id = PROP_ID(tags->aulPropTag[n]);
		prop_type = proptype_brutus_to_mapi(lpPropTagArray[n].type);
		mapi_props[n].ulPropTag = PROP_TAG(prop_type, prop_id);
	}

	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = form_info_->SetProps(lpPropArray.length(), mapi_props, (ProblemInfo ? &mapi_problems : NULL));
	}

exit:
	MAPIFreeBuffer(mapi_props);
	MAPIFreeBuffer(tags);

	if (!sPropProblem_array_mapi_to_brutus(mapi_problems, brutus_problems.inout(), true)) {
		BRUTUS_LOG_ERR("Could not convert MAPI SPropProblemArray to Brutus SPropProblemArray");
		brutus_problems->length(0);
		lppProblems = brutus_problems._retn();
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}
	lppProblems = brutus_problems._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_IMAPIFormInfo_i::SetNamedProps()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_IMAPIFormInfo_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 = form_info_->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_IMAPIFormInfo_i::Destroy(::CORBA::ULong InstanceID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_IMAPIFormInfo_i::Destroy()");

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

	poa_->deactivate_object(oid);

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