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

	return retval;
}


BRUTUS_ITableData_i::BRUTUS_ITableData_i(LPTABLEDATA TableData,
					 LPMAPISESSION MAPISession,
					 ::PortableServer::POA_ptr Poa)
	: table_data_(TableData),
	  mapi_session_(MAPISession),
	  poa_(::PortableServer::POA::_duplicate(Poa))
{
	mapi_session_->AddRef();
}


::BRUTUS::BRESULT BRUTUS_ITableData_i::HrGetView(const ::BRUTUS::SSortOrderSet & lpSSortOrderSet,
						 ::BRUTUS::IMAPITable_out lppMAPITable)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::HrGetView()");

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

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

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

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = table_data_->HrGetView(mapi_set,
					    0,
					    0,
					    &mapi_table);
	}
	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;
	}

	if (hr == S_OK)
		lppMAPITable = create_object<BRUTUS_IMAPITable_i, ::BRUTUS::IMAPITable, LPMAPITABLE>
			(mapi_table, poa_.in(), mapi_session_);

	BRUTUS_LOG_INF("Leaving BRUTUS_ITableData_i::HrGetView()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_ITableData_i::HrModifyRow(const ::BRUTUS::SRow & lpSRow)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::HrModifyRow()");

	SRow *row = NULL;
	if (!srow_brutus_to_mapi(&lpSRow, row)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = table_data_->HrModifyRow(row);
	}
	MAPIFreeBuffer(row);

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

::BRUTUS::BRESULT BRUTUS_ITableData_i::HrDeleteRow(const ::BRUTUS::SPropValue & lpSPropValue)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::HrDeleteRow()");

	SPropValue *mapi_prop = NULL;
	if (!spropvalue_brutus_to_mapi(&lpSPropValue, 0, mapi_prop)) {
		MAPIFreeBuffer(mapi_prop);
		BRUTUS_LOG_ERR("Could not convert Brutus SPropValue to MAPI SPropValue");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = table_data_->HrDeleteRow(mapi_prop);
	}
	MAPIFreeBuffer(mapi_prop);

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

::BRUTUS::BRESULT BRUTUS_ITableData_i::HrQueryRow(const ::BRUTUS::SPropValue & lpSPropValue,
						  ::BRUTUS::SRow_out lppSRow,
						  ::CORBA::ULong & lpuliRow)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::HrQueryRow()");

	SPropValue *mapi_prop = NULL;
	if (!spropvalue_brutus_to_mapi(&lpSPropValue, 0, mapi_prop)) {
		MAPIFreeBuffer(mapi_prop);
		BRUTUS_LOG_ERR("Could not convert Brutus SPropValue to MAPI SPropValue");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	::BRUTUS::SRow_var row(0);
	LPSRow mapi_row;

	ULONG n, *l;
	if (lpuliRow)
		l = &n;
	else
		l = NULL;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = table_data_->HrQueryRow(mapi_prop,
					     &mapi_row,
					     l);
	}
	MAPIFreeBuffer(mapi_prop);
	if (lpuliRow)
		lpuliRow = (::CORBA::ULong)*l;

	srow_mapi_to_brutus(mapi_row, row.inout(), mapi_session_, true, poa_);
	lppSRow = row._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_ITableData_i::HrQueryRow()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_ITableData_i::HrEnumRow(::CORBA::ULong ulRowNumber,
						 ::BRUTUS::SRow_out lppSRow)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::HrEnumRow()");

	::BRUTUS::SRow_var row(0);
	LPSRow mapi_row;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = table_data_->HrEnumRow((ULONG)ulRowNumber,
					    &mapi_row);
	}
	srow_mapi_to_brutus(mapi_row, row.inout(), mapi_session_, true, poa_);
	lppSRow = row._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_ITableData_i::HrEnumRow()");
	return br;
}


::BRUTUS::BRESULT BRUTUS_ITableData_i::HrNotify(::BRUTUS::BDEFINE ulFlags,
						const ::BRUTUS::seq_SPropValue & lpSPropValue)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::HrNotify()");

	unsigned long count = 0;

	SPropValue *mapi_props = NULL;
	if (!spropvalue_array_brutus_to_mapi(&lpSPropValue, 0, count, mapi_props)) {
		MAPIFreeBuffer(mapi_props);
		BRUTUS_LOG_ERR("Could not convert Brutus SPropValue to MAPI SPropValue");
		return ::BRUTUS::BRUTUS_INTERNAL_ERROR;
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = table_data_->HrNotify((ULONG)ulFlags,
					   count,
					   mapi_props);
	}
	MAPIFreeBuffer(mapi_props);

	::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_ITableData_i::HrNotify()");

	return br;
}


::BRUTUS::BRESULT BRUTUS_ITableData_i::HrInsertRow(::CORBA::ULong uliRow,
						   const ::BRUTUS::SRow & lpSRow)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::HrModifyRow()");

	SRow *row = NULL;
	if (!srow_brutus_to_mapi(&lpSRow, row)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = table_data_->HrInsertRow((ULONG)uliRow,
					      row);
	}
	MAPIFreeBuffer(row);

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


::BRUTUS::BRESULT BRUTUS_ITableData_i::HrModifyRows(::BRUTUS::BDEFINE ulFlags,
						    const ::BRUTUS::SRowSet & lpSRowSet)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::HrModifyRows()");

	unsigned long flags = native_flags(ulFlags);

	SRowSet *rows = NULL;
	if (!srow_set_brutus_to_mapi(&lpSRowSet, rows)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = table_data_->HrModifyRows(flags, rows);
	}
	MAPIFreeBuffer(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_ITableData_i::HrModifyRows()");
	return br;
}

::BRUTUS::BRESULT BRUTUS_ITableData_i::HrDeleteRows(::BRUTUS::BDEFINE ulFlags,
						    const ::BRUTUS::SRowSet & lprowsetToDelete,
						    ::CORBA::ULong_out cRowsDeleted)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::HrDeleteRows()");

	unsigned long flags = native_flags(ulFlags);

	SRowSet *rows = NULL;
	if (!srow_set_brutus_to_mapi(&lprowsetToDelete, rows)) {
		BRUTUS_LOG_ERR("No memory");
		throw ::CORBA::NO_MEMORY();
	}

	ULONG dels;

	HRESULT hr;
	{
		ACE_Write_Guard<ACE_RW_Mutex> guard(mutex_);
		hr = table_data_->HrDeleteRows(flags,
					       rows,
					       &dels);
	}
	MAPIFreeBuffer(rows);
	cRowsDeleted = (::CORBA::ULong)dels;

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


::BRUTUS::BRESULT BRUTUS_ITableData_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 = table_data_->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_ITableData_i::Destroy(::CORBA::ULong InstanceID)
{
	BRUTUS_LOG_INF("Entering BRUTUS_ITableData_i::Destroy()");

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

	poa_->deactivate_object(oid);

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